Two weeks ago I found a very interesting article: "A HTTP file server in 130 lines of code". I translated the original code in Visual basic (I use Visual Basic Net 2005 Express).
Imports System
Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Web
Public Class HttpFileServer
Implements IDisposable
Public rootPath As String
Private Const bufferSize As Integer = 1024 * 512
'512KB
Private ReadOnly http As HttpListener
Public Sub New(ByVal rootPath As String)
Me.rootPath = rootPath
http = New HttpListener()
http.Prefixes.Add("http://localhost:80/")
http.Start()
http.BeginGetContext(requestWait, Nothing)
End Sub
Public Sub Dispose() Implements System.IDisposable.Dispose
http.[Stop]()
End Sub
Private Sub requestWait(ByVal ar As IAsyncResult)
If Not http.IsListening Then
Return
End If
Dim c = http.EndGetContext(ar)
http.BeginGetContext(requestWait, Nothing)
Dim url = tuneUrl(c.Request.RawUrl)
Dim fullPath = IIf(String.IsNullOrEmpty(url), rootPath, Path.Combine(rootPath, url))
If Directory.Exists(fullPath) Then
returnDirContents(c, fullPath)
ElseIf File.Exists(fullPath) Then
returnFile(c, fullPath)
Else
return404(c)
End If
End Sub
Private Sub returnDirContents(ByVal context As HttpListenerContext, ByVal dirPath As String)
context.Response.ContentType = "text/html"
context.Response.ContentEncoding = Encoding.UTF8
Using sw = New StreamWriter(context.Response.OutputStream)
sw.WriteLine("html")
sw.WriteLine("head meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8""/head")
sw.WriteLine("body ul")
Dim dirs = Directory.GetDirectories(dirPath)
For Each d As Object In dirs
Dim link = d.Replace(rootPath, "").Replace("\"c, "/"c)
sw.WriteLine("<li><DIR> a href=""" + link + """ " + Path.GetFileName(d) + "/a /li ")
Next
Dim files = Directory.GetFiles(dirPath)
For Each f As Object In files
Dim link = f.Replace(rootPath, "").Replace("\"c, "/"c)
sw.WriteLine(" li <a href=""" + link + """ " + Path.GetFileName(f) + " /a /li ")
Next
sw.WriteLine(" /ul /body /html ")
End Using
context.Response.OutputStream.Close()
End Sub
Private Shared Sub returnFile(ByVal context As HttpListenerContext, ByVal filePath As String)
context.Response.ContentType = getcontentType(Path.GetExtension(filePath))
Dim buffer = New Byte(bufferSize - 1) {}
Using fs = File.OpenRead(filePath)
context.Response.ContentLength64 = fs.Length
Dim read As Integer
While (InlineAssignHelper(read, fs.Read(buffer, 0, buffer.Length))) > 0
context.Response.OutputStream.Write(buffer, 0, read)
End While
End Using
context.Response.OutputStream.Close()
End Sub
Private Shared Sub return404(ByVal context As HttpListenerContext)
context.Response.StatusCode = 404
context.Response.Close()
End Sub
Private Shared Function tuneUrl(ByVal url As String) As String
url = url.Replace("/"c, "\"c)
url = HttpUtility.UrlDecode(url, Encoding.UTF8)
url = url.Substring(1)
Return url
End Function
Private Shared Function getcontentType(ByVal extension As String) As String
Select Case extension
Case ".avi"
Return "video/x-msvideo"
Case ".css"
Return "text/css"
Case ".doc"
Return "application/msword"
Case ".gif"
Return "image/gif"
Case ".htm", ".html"
Return "text/html"
Case ".jpg", ".jpeg"
Return "image/jpeg"
Case ".js"
Return "application/x-javascript"
Case ".mp3"
Return "audio/mpeg"
Case ".png"
Return "image/png"
Case ".pdf"
Return "application/pdf"
Case ".ppt"
Return "application/vnd.ms-powerpoint"
Case ".zip"
Return "application/zip"
Case ".txt"
Return "text/plain"
Case Else
Return "application/octet-stream"
End Select
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
End Class
But I got errors:
Error 1 Argument not specified for parameter 'ar' of 'Private Sub requestWait(ar As System.IAsyncResult)'. line 19
Error 2 Argument not specified for parameter 'ar' of 'Private Sub requestWait(ar As System.IAsyncResult)'. line 31
Both errors refer to the expression http.BeginGetContext(requestWait, Nothing). But I don't know how to handle this.
To compile your code change the lines
http.BeginGetContext(requestWait, Nothing)
to
http.BeginGetContext(AddressOf requestWait, Nothing)
You can have a look at AddressOf Operator and this question.
Related
I had to re-write the post because stackoverflow detects the post as already solved in another thread and i had to delete it. But the thread that referenced the forum was for C# and in anycase even reading it I can't find out a solution for my problem.
So, hello again guys :D
I did find somewhere online a code that allows me to detect usb devices and relative files,
it works but not very well because each time i plug or unplug the usb device i get:
"System.NullReferenceException", i will not write the rest of the error to avoid that stackoverflow closes the thread, anyway you should have understood my issue.
Here the code:
Imports System.IO
Public Class Form1
#Region "USB EVENT"
Private WM_DEVICECHANGE As Integer = &H219
Public Enum WM_DEVICECHANGE_WPPARAMS As Integer
DBT_CONFIGCHANGECANCELED = &H19
DBT_CONFIGCHANGED = &H18
DBT_CUSTOMEVENT = &H8006
DBT_DEVICEARRIVAL = &H8000
DBT_DEVICEQUERYREMOVE = &H8001
DBT_DEVICEQUERYREMOVEFAILED = &H8002
DBT_DEVICEREMOVECOMPLETE = &H8004
DBT_DEVICEREMOVEPENDING = &H8003
DBT_DEVICETYPESPECIFIC = &H8005
DBT_DEVNODES_CHANGED = &H7
DBT_QUERYCHANGECONFIG = &H17
DBT_USERDEFINED = &HFFFF
End Enum
Private Structure DEV_BROADCAST_VOLUME
Public dbcv_size As Int32
Public dbcv_devicetype As Int32
Public dbcv_reserved As Int32
Public dbcv_unitmask As Int32
Public dbcv_flags As Int16
End Structure
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Try
If m.Msg = WM_DEVICECHANGE Then
Dim Volume As DEV_BROADCAST_VOLUME
Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
If Not GetDriveLetterFromMask(Volume.dbcv_unitmask).ToString.Trim = String.Empty Then
Dim DriveLetter As String = (GetDriveLetterFromMask(Volume.dbcv_unitmask) & ":\")
Select Case m.WParam
Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL
LblUSB.Text = DriveLetter
GetFiles()
MsgBox("Usb connected")
' Code When add USB
Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE
LblUSB.Text = ""
CheckedListBox1.Items.Clear()
MsgBox("Usb disconnected")
' Code When Remove USB
End Select
End If
End If
Catch ex As Exception
End Try
MyBase.WndProc(m)
End Sub
Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As Char
For i As Integer = 0 To 25
If Unit = (2 ^ i) Then
Return Chr(Asc("A") + i)
End If
Next
Return ""
End Function
#End Region
Sub GetFiles()
Try
Dim Path = LblUSB.Text
Dim LstFiles = My.Computer.FileSystem.GetFiles(Path, FileIO.SearchOption.SearchTopLevelOnly)
For Each File In LstFiles
Dim F As New IO.FileInfo(File)
CheckedListBox1.Items.Add(F.Name)
Next
Catch ex As Exception
End Try
End Sub
End Class
The System.NullReferenceException i get is at this point:
Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
I did read that this error happens when the value is null and there should be a value associated,
i tried different approaches but none did work and i hope that someone of you could give me a solution and an explaination to better understand that.
Thank you very much guys for the help!
When one runs the code in VS 2019, beneath the System.NullReferenceException it states: System.Runtime.InteropServices.Marshal.PtrToStructure(...) returned Nothing which occurs because m.LParam is 0. This can be observed by placing the following code just before the DirectCast statement:
Debug.WriteLine($"m.LParam: '{m.LParam.ToString()}'").
There isn't a need to attempt to get a drive letter until it's been detected that the USB device has been inserted. If one refactors the code as shown below, the issue doesn't occur:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_DEVICECHANGE Then
Debug.WriteLine($"m.LParam: '{m.LParam.ToString()}'")
Select Case CInt(m.WParam)
Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL
Debug.WriteLine($" DBT_DEVICEARRIVAL")
Dim Volume As DEV_BROADCAST_VOLUME
Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
If Not GetDriveLetterFromMask(Volume.dbcv_unitmask).ToString.Trim = String.Empty Then
Dim DriveLetter As String = (GetDriveLetterFromMask(Volume.dbcv_unitmask) & ":\")
LblUSB.Text = DriveLetter
GetFiles()
MsgBox("Usb connected")
' Code When add USB
End If
Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE
LblUSB.Text = ""
CheckedListBox1.Items.Clear()
MsgBox("Usb disconnected")
' Code When Remove USB
End Select
End If
MyBase.WndProc(m)
End Sub
A side note: It's recommended to enable Option Strict for your project.
Update:
After enabling Option Strict, one receives the following compiler error: BC30512: Option Strict On disallows implicit conversions from 'String' to 'Char' in function GetDriveLetterFromMask because of the statement: Return "". To fix the issue, try one of the following:
Option 1:
Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As String
For i As Integer = 0 To 25
If Unit = (2 ^ i) Then
Return Chr(Asc("A") + i)
End If
Next
Return ""
End Function
Option 2:
Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As Char
For i As Integer = 0 To 25
If Unit = (2 ^ i) Then
Return Chr(Asc("A") + i)
End If
Next
Return Chr(0)
End Function
Resources:
Marshal.PtrToStructure Method
DirectCast Operator (Visual Basic)
CType Function (Visual Basic)
I am developing a server-side scripting language which I intend to use on my private server. It is similar to PHP, and I know that I could easily use PHP instead but I'm just doing some programming for fun.
The syntax of basic commands in my language is as follows:
command_name "parameter1" : "parameter2" : "parameter3"
But it can also be like this when I want to join values for a parameter:
command_name "parameter1" : "param" & "eter2" : "par" & "amet" & "er3"
How would I go about parsing a string like the ones shown above (it will be perfectly typed, no syntax errors) to an object that has these properties
Custom class "Request"
Property "Command" as String, should be the "command_name" part
Property "Parameters" as String(), should be an array of Parameter objects
Shared Function FromString(s As String) as Request, this should accept a string in the language above and parse it to a Request object
Custom class "Parameter"
Property "Segments" as String(), for example "para", "mete", and "r3"
Sub New(ParamArray s as String()), this is how it should be generated from the code
It should be done in VB.NET and I am a moderate level programmer, so even if you just have an idea of how to attack this then please share it with me. I am very new to parsing complex data like this so I need a lot of help. Thanks so much!
Here is another method that is simpler.
Module Module1
Sub Main()
Dim inputs As String() = {"command_name ""parameter1"" : ""parameter2"" : ""parameter3""", "command_name ""parameter1"" : ""param"" & ""eter2"" : ""par"" & ""amet"" & ""er3"""}
For Each _input As String In inputs
Dim commandStr As String = _input.Substring(0, _input.IndexOf(" ")).Trim()
Dim parameters As String = _input.Substring(_input.IndexOf(" ")).Trim()
Dim parametersA As String() = parameters.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(x) x.Trim()).ToArray()
Dim parametersB As String()() = parametersA.Select(Function(x) x.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(y) y.Trim(" """.ToCharArray())).ToArray()).ToArray()
Dim newCommand As New Command() With {.name = commandStr, .parameters = parametersB.Select(Function(x) New Parameter(x)).ToArray()}
Command.commands.Add(newCommand)
Next (_input)
Dim z = Command.commands
End Sub
End Module
Public Class Command
Public Shared commands As New List(Of Command)
Public name As String
Public parameters As Parameter()
End Class
Public Class Parameter
Sub New()
End Sub
Sub New(names As String())
Me.names = names
End Sub
Public names As String()
End Class
I figured it out myself
Module Module1
Sub Main()
Dim r As Request = Request.Parse(Console.ReadLine())
Console.WriteLine("The type of request is " & r.Name)
For Each p As Parameter In r.Parameters
Console.WriteLine("All segments inside of parameter " & r.Parameters.IndexOf(p).ToString)
For Each s As String In p.Segments
Console.WriteLine(" Segment " & p.Segments.IndexOf(s).ToString & " is " & s)
Next
Next
Main()
End Sub
Public Class Request
Public Name As String
Public Parameters As New List(Of Parameter)
Public Shared Function Parse(line As String)
Dim r As New Request
r.Name = line.Split(" ")(0)
Dim u As String = line.Substring(line.IndexOf(" "), line.Length - line.IndexOf(" "))
Dim p As String() = u.Split(":")
For Each n As String In p
Dim b As String() = n.Split("&")
Dim e As New List(Of String)
For Each m As String In b
Dim i As Integer = 0
Do Until i > m.Length - 1
If m(i) = ControlChars.Quote Then
Dim s As String = ""
i += 1
Do Until i > m.Length - 1 Or m(i) = ControlChars.Quote
s &= m(i)
i += 1
Loop
e.Add(s)
End If
i += 1
Loop
Next
r.Parameters.Add(New Parameter(e.ToArray))
Next
Return r
End Function
End Class
Public Class Parameter
Public Segments As New List(Of String)
Public Sub New(ParamArray s As String())
Segments = s.ToList
End Sub
End Class
End Module
Here is my code.
Yes, I am using both, DropNet and Dropbox APIs as I found the DropNet upload works nicely. But I am trying to use the Dropbox one to check for filename (as I couldn't get it to work on DropNet and could not find any help about it online)
I have little doubt that my problem has something to do with the whole Async & Await , as I have never worked with this stuff before.
The File Upload & Get Share both work just fine.
This is a VB.Net Website.
When I run it, it freezes in side the DoesDropBoxFileExist function
Imports Dropbox.Api
Imports DropNet
Imports DropNet.Models
Partial Class _Default
Inherits System.Web.UI.Page
Dim br As String = "<br>"
Public FileName As String
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If FileUpload1.HasFile Then
Dim dropNet_client As New DropNetClient("", "", "")
Dim dropBox_client As New DropboxClient("")
FileName = FileUpload1.PostedFile.FileName
Response.Write("before: " & FileName & br)
MsgBox(1)
FileName = DoesDropBoxFileExist(dropBox_client).Result
MsgBox(3)
Response.Write("after: " & FileName & br)
Dim content As Byte() = FileUpload1.FileBytes
Dim pathToFile As String = Server.MapPath("~")
'Response.Write(pathToFile)
dropNet_client.UploadFile("/AlertImages/", FileName, content, True)
Dim shareResponse As ShareResponse = dropNet_client.GetShare("/AlertImages/" & FileName)
Response.Write(shareResponse.Url)
If Not FileName.ToLower.Contains("pdf") Then
Dim rawBytes As Byte() = dropNet_client.GetThumbnail("/AlertImages/" & FileName, 2)
Dim base64String As String = Convert.ToBase64String(rawBytes, 0, rawBytes.Length)
Image1.ImageUrl = "data:image/png;base64," & base64String
Image1.Visible = True
End If
dropBox_client.Dispose()
End If
End Sub
Private Async Function DoesDropBoxFileExist(_client As DropboxClient) As Threading.Tasks.Task(Of String)
Dim rtn As String = FileName
Dim list = Await _client.Files.ListFolderAsync("/AlertImages")
MsgBox(2)
' show folders then files
For Each item As Files.Metadata In list.Entries.Where(Function(i) i.IsFolder)
If item.Name = FileName Then
FileName = FileName & Now.ToString
End If
Response.Write(" < b > " & item.Name & "</b>" & br)
'Dim list2 As ListFolderResult = Await dbx.Files.ListFolderAsync(item.Name)
'For Each itm As Files.Metadata In list2.Entries.Where(Function(j) j.IsFile)
' Response.Write(item.Name & " : " & item.AsFile.Size & br)
'Next
Next
For Each item As Files.Metadata In list.Entries.Where(Function(i) i.IsFile)
Response.Write("'" & item.Name & "' '" & FileName & "'" & br)
If item.Name = FileName Then
Response.Write("test" & br)
rtn = FileName & "_" & Now.ToString
End If
Next
Return rtn
End Function
End Class
METHOD 1
To check in VB.NET if a file exists in Dropbox using the API, you can use this method.
First we create a button with a click event as follows:
Private Sub btnCheck_Click(sender As Object, e As EventArgs) Handles btnCheck.Click
'FileToCheck is declared in Form1 as Public Shared
'FileFound is declared in Form1 as Public Shared
FileToCheck = cmbFiles.Text
FileFound = False
Dim task1 = Task.Run(Function() CheckFileMetadata())
task1.Wait()
If FileFound = True Then
'Do something
Else
'Do something else
End If
End Sub
And now the function:
Private Async Function CheckFileMetadata() As Task
Using dbx = New DropboxClient(DbxToken) 'DbxToken = your token text
Try
Await dbx.Files.GetMetadataAsync(Form1.FileToCheck)
FileFound = True
Debug.WriteLine("Found it!")
Catch exapi As ApiException(Of Dropbox.Api.Files.GetMetadataError)
If exapi.ErrorResponse.IsPath And exapi.ErrorResponse.AsPath.Value.IsNotFound Then
Debug.WriteLine("Nothing found at " + Form1.FileToCheck)
End If
Catch ex As Exception
Debug.WriteLine("Error checking file metadata" + vbCrLf + ex.ToString)
End Try
End Using
End Function
This method was adapted from the code here.
METHOD 2
This example demonstrates using VB.NET to recursively iterate through all Dropbox folders to retrieve the names of all files and put them into a collection. Then we check to see if our file is in the collection or not. This method does work, but it's not as efficient as the method above for obvious reasons. I've left it here because it illustrates some additional methods that might help someone.
A couple of notes:
If you have a lot of files and/or folders, there can be a delay due to all of the calls that have to be made to do the recursive processing.
DbxFolders and DbxFiles are declared as Public in the main form class, like so:
Public DbxFolders As New List(Of String)
Public DbxFiles As New List(Of String)
Note use of the .tolower since the Dropbox API returns all found paths in all lowers:
Private Sub btnWalk_Click(sender As Object, e As EventArgs) Handles btnWalk.Click
DbxFolders.Clear()
DbxFiles.Clear()
Dim FindIt As String = "/Folder/File-To-Find.txt".ToLower
Dim task2 = Task.Run(Function() GetTree(String.Empty))
task2.Wait()
If DBFileExists(FindIt) Then MsgBox("Found it!") Else MsgBox("File not found")
End Sub
Private Async Function GetTree(dir As String) As Task
Using dbx = New DropboxClient("Your_Token_Goes_Here")
Dim list = Await dbx.Files.ListFolderAsync(dir)
For Each item In list.Entries.Where(Function(i) i.IsFile)
DbxFiles.Add(item.PathLower)
Next
For Each item In list.Entries.Where(Function(i) i.IsFolder)
DbxFolders.Add(item.PathLower)
Await GetTree(item.PathLower)
Next
End Using
End Function
Private Function DBFileExists(file As String) As Boolean
If DbxFiles.IndexOf(file) > -1 Then Return True Else Return False
End Function
DISCUSSION
Method 1 is obviously the more efficient of the two methods by far because we only call the API once. Note how the ApiException is used in Try-Catch to determine that the file was not found.
Method 2 illustrates some additional concepts that were helpful to me to learn, so I've left it here because someone may have a scenario where this code and the lists that it creates comes in handy.
Note that when we call GetTree(String.Empty), it would be more efficient to pass the specific folder to look in, instead of starting at the root, since we are attempting to match the full path (/path/to/file.txt) in this example anyway, but I wanted to illustrate the recursive iteration because it might be needed in a different situation.
If you don't care what folder an item is in, but only want to see if it exists in a folder without regard to which folder that is, then you would need to use this recursive iteration but instead of item.pathlower you would want to collect item.name instead.
If desired, you can process the collected file list from Method 2 with a simple loop:
For each DbxFile as string in DbxFiles
'Do something
Next
There's lots and lots of pages on the internet regarding threading but I can't seem to get my head around it.
I have a Form, which on the click of a button, loops through a file and reads it line by line. Each line is the login details for different FTP sites.
When it reads a line, it Dim's a variable as a new instance of a class named CallFTP using the login details.
It then Dim's a variable as a new Thread using a function in CallFTP named PerformFTP.
PerformFTP returns a string with the results of the FTP and I want to add this to a ListBox on the form that began it all.
The code for the button goes like this...
Private Sub cmdRun_Click(sender As Object, e As EventArgs) Handles cmdRun.Click
For Each _FTPLine As String In Split(_FTPDetails, vbNewLine)
Dim _Active As Boolean = CBool(Split(_FTPLine, "|")(7))
If _Active Then
_CurNum += 1
_ID = Format(Now.Year, "0000") & Format(Now.Month, "00") & Format(Now.Day, "00") & Format(Now.Hour, "00") & Format(Now.Minute, "00") & Format(Now.Second, "00") & Format(Now.Millisecond, "000") & Format(_CurNum, "00000")
Dim _FTP As New CallFTP(_ID, Split(_FTPLine, "|")(0), Split(_FTPLine, "|")(1), Split(_FTPLine, "|")(2), Split(_FTPLine, "|")(3), Split(_FTPLine, "|")(4), Split(_FTPLine, "|")(5), Split(_FTPLine, "|")(6))
Dim _Thread = New Thread(New ThreadStart(AddressOf _FTP.PerformFTP))
With _Thread
.IsBackground = True
.Start()
End With
End If
Next _FTPLine
End Sub
The class is as below (not quite but you don't need the rest of the code lol)
Public Class CallFTP
Private _ID As String = ""
Private _Response As String = ""
Private _IPAddress As String = ""
Private _Port As String = ""
Private _User As String = ""
Private _Pass As String = ""
Private _Remote As String = ""
Private _Local As String = ""
Private _InOut As String = ""
Public Sub New(ID As String, Server As String, PortNum As String, Username As String, Password As String, RemoteDir As String, LocalDir As String, InOrOut As String)
_ID = ID
_IPAddress = Server
_Port = PortNum
_User = Username
_Pass = Password
_Remote = RemoteDir
_Local = LocalDir
_InOut = InOrOut
End Sub
Public Function PerformFTP() As String
Return "This is a test"
End Function
End Class
Could anyone explain how I would call a sub named LogMessage on a module named modMisc (which adds a string to a ListBox on the main form)?
I've read that you need to invoke it but everything I read seems to give me a headache and make me need to lie down in a dark room for a few hours.
Is anyone capable of explaining as though you're speaking to a 2 year old? :)
Any help would be much appreciated.
You need to invoke a delegate to update your GUI if you're going to update it from another thread that from where it was created.
1º Your delegate must match (have the same signature) than the method you'll use:
Delegate Sub LogMessageExampleDelegate(ByVal x As Integer, ...)
Signature means that the delegate must return and receive the same types than your function/method.
2º Call your function to update GUI using delegate. This for example inside your update GUI function:
If yourListBox.InvokeRequired Then
yourListBox.Invoke(New LogMessageExampleDelegate(AddressOf THE_FUNCTION_WHICH_UPDATES_THE_GUI_NAME), parameter_value)
Else
'Just call your function
End If
With, as example:
sub addToListBox(byval text as string)
myListBox.Items.add(text)
end sub
So your invoke would be:
If yourListBox.InvokeRequired Then
yourListBox.Invoke(New LogMessageExampleDelegate(AddressOf addToListBox), "Item 1")
Else
'Just call your function
addToListBox("Item 1")
End If
PS: I wrote it two times so hope I didn't mess up with something without noticing it.
I'm trying to retrieve image dimensions from an image URL.
How is this possible?
I've done plenty of research and haven't found any code that can achieve this, all of the information gets image dimensions from an image on the local disk which is not what I want.
Is it possible to achieve this?
You don't need to download entire file, only first few bytes. If image is PNG, width is in 17th, 18th, 19th and 20th byte, and height is in 21st, 22nd, 23rd and 24th byte. If it's GIF, width is in 7th and 8th byte, and height is in 9th and 10th byte. It's complicated for JPG. Note that byte order in PNG is big-endian(255 - 000000FF) and in GIF is little-endian(255 - FF00). Here is code:
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Threading
Imports Microsoft.VisualBasic
Public Class Form1
Private Sub GetImageDimensions() Handles Button1.Click
HTTPWebRequest_GetResponse.Main("http://www.example.com/image.png") 'without slash at end
Do
If HTTPWebRequest_GetResponse.done = True Then
Dim width As Integer = HTTPWebRequest_GetResponse.width
Dim height As Integer = HTTPWebRequest_GetResponse.height
Exit Do
End If
If HTTPWebRequest_GetResponse.exception Then
Exit Do 'prevents inifinite loop if exception occured
End If
Loop
End Sub
End Class
Public Class RequestState
' This class stores the State of the request.
Public requestData As StringBuilder
Public BufferRead() As Byte
Public request As HttpWebRequest
Public response As HttpWebResponse
Public streamResponse As Stream
Public Sub New()
requestData = New StringBuilder("")
request = Nothing
streamResponse = Nothing
End Sub 'New
End Class 'RequestState
Class HTTPWebRequest_GetResponse
Private BUFFER_SIZE As Integer = 1024
Public Shared response As String
Public Shared done As Boolean = False
Public Shared length As Long = 1
Public Shared progress As Integer
Public Shared myHttpWebRequest As HttpWebRequest
Public Shared myRequestState As New RequestState
Public Shared status As String
Private Shared body As Boolean = False
Public Shared responseStream As Stream
Private Shared offset As Integer
Private Shared bytestoread As Integer
Public Shared width As Integer
Public Shared height As Integer
Private Shared imageType As String
Public Shared exception As Boolean = False
Shared Sub Main(url As String)
done = False
exception = False
Try
If url.Substring(url.Length - 4) = ".png" Then
myRequestState.BufferRead = New Byte(23) {}
offset = 16
bytestoread = 24
imageType = "png"
ElseIf url.Substring(url.Length - 4) = ".gif" Then
myRequestState.BufferRead = New Byte(9) {}
offset = 6
bytestoread = 10
imageType = "gif"
Else
Throw New NotSupportedException("You can only use this with PNG or GIF images.")
End If
' Create a HttpWebrequest object to the desired URL.
myHttpWebRequest = WebRequest.Create(url)
' Create an instance of the RequestState and assign the previous myHttpWebRequest
' object to its request field.
myRequestState.request = myHttpWebRequest
' Start the asynchronous request.
Dim result As IAsyncResult = CType(myHttpWebRequest.BeginGetResponse(New AsyncCallback(AddressOf RespCallback), myRequestState), IAsyncResult)
Catch e As WebException
exception = True
Debug.WriteLine(ControlChars.Lf + "Main Exception raised!")
Debug.WriteLine(ControlChars.Lf + "Message: " + e.Message)
Debug.WriteLine(ControlChars.Lf + "Status: " + e.Status)
Catch e As Exception
exception = True
Debug.WriteLine(ControlChars.Lf + "Main Exception raised!")
Debug.WriteLine("Source : " + e.Source)
Debug.WriteLine("Message : " + e.Message)
End Try
End Sub 'Main
Private Shared Sub RespCallback(asynchronousResult As IAsyncResult)
Try
Dim myHttpWebRequest As HttpWebRequest = myRequestState.request
myRequestState.response = CType(myHttpWebRequest.EndGetResponse(asynchronousResult), HttpWebResponse)
' Read the response into a Stream object.
Dim responseStream As Stream = myRequestState.response.GetResponseStream()
myRequestState.streamResponse = responseStream
' Begin the Reading of the contents of the HTML page.
responseStream.Read(myRequestState.BufferRead, 0, bytestoread)
If BitConverter.IsLittleEndian Then
If imageType = "png" Then
height = BitConverter.ToInt32(myRequestState.BufferRead.Skip(offset).Reverse.Take(4).ToArray, 0)
width = BitConverter.ToInt32(myRequestState.BufferRead.Skip(offset).Reverse.Skip(4).Take(4).ToArray, 0)
Else
width = BitConverter.ToInt16(myRequestState.BufferRead.Skip(offset).Skip(6).Take(2).ToArray, 0)
height = BitConverter.ToInt16(myRequestState.BufferRead.Skip(offset).Skip(8).Take(2).ToArray, 0)
End If
Else
If imageType = "png" Then
width = BitConverter.ToInt32(myRequestState.BufferRead.Skip(offset).Skip(16).Take(4).ToArray, 0)
height = BitConverter.ToInt32(myRequestState.BufferRead.Skip(offset).Skip(20).Take(4).ToArray, 0)
Else
height = BitConverter.ToInt16(myRequestState.BufferRead.Skip(offset).Reverse.Take(2).ToArray, 0)
width = BitConverter.ToInt16(myRequestState.BufferRead.Skip(offset).Reverse.Skip(2).Take(2).ToArray, 0)
End If
End If
done = True
Return
Catch e As WebException
exception = True
Debug.WriteLine(ControlChars.Lf + "RespCallback Exception raised!")
Debug.WriteLine(ControlChars.Lf + "Message: " + e.Message)
Debug.WriteLine(ControlChars.Lf + "Status: " + e.Status)
Catch ex As Exception
exception = True
Debug.WriteLine(ControlChars.Lf + "RespCallback Exception raised!")
Debug.WriteLine(ex.ToString)
End Try
End Sub 'RespCallback
End Class
Code is large, but I think this is only to download only enough bytes.
Sources: https://en.wikipedia.org/wiki/Gif#Example_GIF_file
https://en.wikipedia.org/wiki/Portable_Network_Graphics#Technical_details
http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
I don't think this is possible. Thing is...before any information can be retrieved from a certain URL or file or picture from the internet, it has to be downloaded first. It's the reason why Windows has the temp folder. Everything is stored inside the temp folder including the scenario where you're just browsing the internet. So my suggestion would be to do the same thing, download the picture first and save it somewhere and retrieve the information you want, then you can delete the file after if you don't want it anymore.