Wait unit an event is raised - vb.net

I'm trying to make a code that waits until an event is raised. I used the answer in this answer page for Tcp communication. The event PacketReceived will be raised when a packet is received.
Private Sub FooBar()
'Send some Tcp Message here`
'Wait until a reply is received here
'Execute some code when reply is received
End Sub
This is code Sends the filename and what's the file for. It lacks of file existence checking
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim t = New Thread(AddressOf SendFile)
Dim f = New FileAndData
f.FileList = SlideShowItems
f.FileType = "tempSlideShowImage"
t.Start(f)
End Sub
Private Sub SendFile(obj As Object)
Dim fD = CType(obj, FileAndData)
For Each item In fD.FileList.Items
Dim fileIn As New FileStream(item, FileMode.Open)
Const chunkSize = 10485760
Dim messageFileNamePkt As New TcpMessagePacket(Encoding.Default.GetBytes(Path.GetFileName(item)), TcpMessagePacket.PacketHeader.Filename)
messageFileNamePkt.Send(_etcClient.Client) 'Send file name
Dim messageFiletype As New TcpMessagePacket(Encoding.Default.GetBytes(fD.FileType), TcpMessagePacket.PacketHeader.Filetype)
messageFiletype.Send(_etcClient.Client) 'Send file type
'Wait here for reply if for file existence
While fileIn.Position < fileIn.Length
Dim bytes(chunkSize) As Byte
If fileIn.Length - fileIn.Position < bytes.Length Then
ReDim bytes(fileIn.Length - fileIn.Position - 1)
End If
fileIn.Read(bytes, 0, bytes.Length)
Dim messageFilePkt As New TcpMessagePacket(bytes, TcpMessagePacket.PacketHeader.FileData)
messageFilePkt.Send(_etcClient.Client) 'Send file chunks
End While
fileIn.Close()
Dim messageFileEnd As New TcpMessagePacket(Encoding.Default.GetBytes(""), TcpMessagePacket.PacketHeader.TransferComplete)
messageFileEnd.Send(_etcClient.Client) 'Send transfer complete
Next
End Sub
Public Class FileAndData
Public FileList As ListBox
Public FileType As String
End Class
And this is the handler for the reply
Private Sub Client_PacketReceived(sender As Object, e As ExtendedTcpClient.PacketReceivedEventArgs) Handles _etcClient.PacketReceived
End Sub

Use ManualResetEvent. Ex.
Imports System.Threading
Module Module1
Private ReadOnly WaitEvent As New ManualResetEvent(True)
Sub Main()
For i = 1 To 100
WaitEvent.WaitOne() 'This is like a door. It's open because of ManualResetEvent(True)
If i = 50 Then
WaitEvent.Reset() 'This closes the door
End If
Console.WriteLine(i)
Next
Console.Read()
End Sub
Private Sub SetTheEventWhenCalled()
WaitEvent.Set() 'This on the other hand, Opens the door
End Sub
End Module

Related

Display results scraped from Webpage in Treeview Control from Class

I'm working on a Visual Basic Project. My working environment is :
Windows 10 32bit
Visual Studio 2015
.Net Framework 4.8
Winform
At this stage,I have :
Class (Class1.vb)
Form1 (Form1.vb) with TreeView Control
I'm supposed to Scrape a webpage (i.e: https://www.example.com), I want to display the result of Scraping in a Treeview Control placed on Form1. I have tried some approaches and they worked fine, except that they require using Webbrowser Control which I do not wish to use. I found a method that I'm using now, but it seems to not letting me display the Results on the Form.
Here is my Code of Class1.vb and it's working fine
Imports System.Threading.Tasks
Public Class Class1
' Create a WebBrowser instance.
Private Event DocumentCompleted As WebBrowserDocumentCompletedEventHandler
Private ManufacturersURi As New Uri("https://www.example.com/Webpage.php3")
Public ManList As New List(Of TreeNode)
Public Sub GettHelpPage()
' Create a WebBrowser instance.
Dim webBrowserForPrinting As New WebBrowser() With {.ScriptErrorsSuppressed = True}
' Add an event handler that Scrape Data after it loads.
AddHandler webBrowserForPrinting.DocumentCompleted, New _
WebBrowserDocumentCompletedEventHandler(AddressOf GetManu_Name)
' Set the Url property to load the document.
webBrowserForPrinting.Url = ManufacturersURi
End Sub
Private Sub GetManu_Name(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
Dim webBrowserForPrinting As WebBrowser = CType(sender, WebBrowser)
Dim Divs = webBrowserForPrinting.Document.Body.GetElementsByTagName("Div")
' Scrape the document now that it is fully loaded.
Dim T As Task(Of List(Of TreeNode)) =
Task.Run(Function()
Dim LinksCount As Integer = 0
For Each Div As HtmlElement In Divs
If InStr(Div.GetAttribute("ClassName").ToString, "Div-Name", CompareMethod.Text) Then
LinksCount = Div.GetElementsByTagName("a").Count - 1
For I As Integer = 0 To LinksCount
Dim Txt() As String = Div.GetElementsByTagName("a").Item(I).InnerHtml.Split("<BR>")
Dim Manu_TreeNode As New TreeNode() With
{.Name = I.ToString, .Text = Txt(0)}
ManList.Add(Manu_TreeNode)
Next
End If
Next
Return ManList
End Function)
' Dispose the WebBrowser now that the task is complete.
Debug.WriteLine(T.Result.Count) 'Result is 116
webBrowserForPrinting.Dispose()
End Sub
The above Code results 116 TreeNodes, which are the count of Tags that I scraped. Now when I attempt to display this result on Form1_Load, nothing happens, because the Form loads before the Code finishes executing.
Here is the Form1_Load Code :
Public Class Form1
Dim ThisClass As New Class1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ThisClass.GetHelpPage()
TreeView1.Nodes.Clear()
For I As Integer = 0 To ThisClass.ManList.Count - 1
TreeView1.Nodes.Add(ThisClass.ManList(I))
Next
End Sub
End Class
I noticed that if I placed an empty msgbox("") in the Form1_Load somewhere before For..Next, it forces the Form1_Load Event to wait and successfully populates the TreeView Control.
What am I doing wrong ? or What am I missing there ?
I noticed that if I placed an empty msgbox("") in the Form1_Load somewhere before For..Next, it forces the Form1_Load Event to wait and successfully populates the TreeView Control.
Yes, it plays the await role if you keep it open long enough until the task in the GetManu_Name method is completed. Since the MsgBox is a modal window which blocks the next lines from being executed until it been closed.
Now, either you make it a complete synchronous call by removing the Task.Run(...) from the GetManu_Name method, or utilize an asynchronous pattern in such a way as:
Public Class WebStuff
Public Shared Async Function ToTreeNodes(url As String) As Task(Of IEnumerable(Of TreeNode))
Dim tcsNavigated As New TaskCompletionSource(Of Boolean)
Dim tcsCompleted As New TaskCompletionSource(Of Boolean)
Dim nodes As New List(Of TreeNode)
Using wb As New WebBrowser With {.ScriptErrorsSuppressed = True}
AddHandler wb.Navigated,
Sub(s, e)
If tcsNavigated.Task.IsCompleted Then Return
tcsNavigated.SetResult(True)
End Sub
AddHandler wb.DocumentCompleted,
Sub(s, e)
If wb.ReadyState <> WebBrowserReadyState.Complete OrElse
tcsCompleted.Task.IsCompleted Then Return
tcsCompleted.SetResult(True)
End Sub
wb.Navigate(url)
Await tcsNavigated.Task
'Navigated.. if you need to do something here...
Await tcsCompleted.Task
'DocumentCompeleted.. Now we can process the Body...
Dim Divs = wb.Document.Body.GetElementsByTagName("Div")
Dim LinksCount As Integer = 0
For Each Div As HtmlElement In Divs
If Div.GetAttribute("ClassName").
IndexOf("Div-Name", StringComparison.InvariantCultureIgnoreCase) > -1 Then
LinksCount = Div.GetElementsByTagName("a").Count - 1
For I As Integer = 0 To LinksCount
Dim Txt = Div.GetElementsByTagName("a").Item(I).InnerHtml.
Split({"<BR>"}, StringSplitOptions.RemoveEmptyEntries)
Dim n As New TreeNode With {
.Name = I.ToString, .Text = Txt.FirstOrDefault
}
nodes.Add(n)
Next
End If
Next
End Using
Return nodes
End Function
End Class
Notes on the method:
A single Async function to do the lengthy task and returns IEnumerable(Of TreeNode) to the caller.
Lambda Expressions are used to add the WebBrowser.Navigated and WebBrowser.DocumentCompleted events.
The TaskCompletionSource is necessary here to wait for the completion of the WebBrowser.DocumentCompleted event in order to be able to process the HTML contents.
You need to add the Async modifier to the caller's signature to call the function and wait for the result. For example, the Form.Load event:
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim nodes = Await WebStuff.ToTreeNodes("www....")
TreeView1.Nodes.AddRange(nodes.ToArray)
End Sub
Or Async method:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PopulateTree()
End Sub
Private Async Sub PopulateTree()
Dim nodes = Await WebStuff.ToTreeNodes("www....")
TreeView1.Nodes.AddRange(nodes.ToArray)
End Sub
Try this
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ThisClass As New Class1
Dim i As Integer = 0
Do Until ThisClass.IsCompleted
Threading.Thread.Sleep(100)
'if the document takes too much time
i += 1
If i > 30 Then Exit Do 'more than 3 sec
Loop
TreeView1.Nodes.Clear()
For I As Integer = 0 To ThisClass.ManList.Count - 1
TreeView1.Nodes.Add(ThisClass.ManList(I))
Next
End Sub
End Class
Class Class1
Dim Completed As Boolean = False
ReadOnly Property IsCompleted As Boolean
Get
Return Completed
End Get
End Property
Private Sub GetManu_Name(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
'your code
Completed = True
Return ManList
End Sub

Fire method on Main Thread from other Threads

Issue
I am using multi-threading inside my application and the way it works is that i have an array that contains 22 string which stand for some file names:
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "FFCHLDP", "FFDBKDP", "FFDREQP", "FFINVHP", "FFJMNEP", "FFPIVHP", "FFUNTTP", "FJBJM1P", "FJBJM2P", "FJBNT2P", "FPPBE9P", "FTPCP1P", "FTTEO1P", "FTTRQ1P", "FJBJU1P", "FTTEG1P", "FFJACPP", "XATXTDP"}
I then loop through the array and create a new thread for each file:
For Each mThreadName As String In ThreadList
Dim mFileImportThread = New FileImportThreadHandling(mThreadName, mImportGuid, mImportDate, Directory_Location, mCurrentProcessingDate, mRegion)
Next
So inside the new thread 'FileImportThreadHandling' it will call a method by starting a new thread:
mThread = New Thread(AddressOf DoWork)
mThread.Name = "FileImportThreadHandling"
mThread.Start()
Then in 'DoWork' it will determine what file is current in question and will run the code related to the file.
After the code has ran for the file I want to report this back to the main thread. Can somebody give me a solution please.
You will need to use Delegates
EDIT:
Take a look at this snippet:
Sub Main()
mThread = New Thread(AddressOf doWork)
mThread.Name = "FileImportThreadHandling"
mThread.Start()
End Sub
Sub doWork()
'do a lot of hard work
workDone(result)
End Sub
Delegate Sub workDoneDelegate(result As Integer)
Sub workDone(abilita As Boolean, Optional src As Control = Nothing)
If Me.InvokeRequired Then
Me.Invoke(New workDoneDelegate(AddressOf workDone), {result})
Else
'here you're on the main thread
End If
End Sub
Here is an example that might give you some ideas. Note the use of Async and Task. You will need a form with two buttons, and a label.
Public Class Form1
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "*******", "FFJACPP", "XATXTDP"}
'note Async keyword on handler
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim running As New List(Of Task)
For Each tskName As String In ThreadList
'start new task
Dim tsk As Task
tsk = Task.Run(Sub()
For x As Integer = 1 To 10
Dim ct As Integer = x
'simulate code related to the file
Threading.Thread.Sleep(500)
'report to the UI
Me.Invoke(Sub()
Label1.Text = String.Format("{0} {1}", tskName, ct)
End Sub)
Next
End Sub)
Threading.Thread.Sleep(100) 'for testing delay between each start
running.Add(tsk)
Next
'async wait for all to complete
For Each wtsk As Task In running
Await wtsk
Next
Button1.Enabled = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'test UI responsive during test
Label1.Text = DateTime.Now.ToString
End Sub
End Class
Using Actions and a callback to track remaining operations. (Simplified your class for my example)
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "FFCHLDP"}
Private actionCounter As Integer = 0
Private lockActionCounter As New Object()
Sub Main()
Console.WriteLine("Starting...")
For Each mThreadName As String In ThreadList
Dim mFileImportThread = New FileImportThreadHandling(mThreadName)
actionCounter += 1
Call New Action(AddressOf mFileImportThread.DoWork).
BeginInvoke(AddressOf callback, mThreadName)
Next
Console.Read()
End Sub
Private Sub callback(name As IAsyncResult)
Dim remainingCount As Integer
SyncLock lockActionCounter
actionCounter -= 1
remainingCount = actionCounter
End SyncLock
Console.WriteLine("Finished {0}, {1} remaining", name.AsyncState, actionCounter)
If remainingCount = 0 Then Console.WriteLine("All done")
End Sub
Private Class FileImportThreadHandling
Shared r = New Random()
Private _name As String
Public Sub New(name As String)
_name = name
End Sub
Public Sub DoWork()
Dim delayTime = (r).Next(500, 5000)
Console.WriteLine("Doing {0} for {1:0}ms.", _name, delayTime)
Thread.Sleep(delayTime)
End Sub
End Class

How can I properly implement threaded downloads using WebClient with a BlockingCollection?

I'm attempting to make a multi-threaded download manager that has a limit of 4 concurrent downloads. In my research, I came across the following: C# Downloader: should I use Threads, BackgroundWorker or ThreadPool?
[edit] updated code:
Imports System.Net
Imports System.Collections.Concurrent
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Const MaxClients As Integer = 4
' create a queue that allows the max items
Dim ClientQueue As New BlockingCollection(Of WebClient)(MaxClients)
' queue of urls to be downloaded (unbounded)
Dim UrlQueue As New Queue(Of String)()
Dim downloadThread As Thread
'Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' create four WebClient instances and put them into the queue
For i As Integer = 0 To MaxClients - 1
Dim cli = New WebClient()
AddHandler cli.DownloadFileCompleted, AddressOf DownloadFileCompleted
AddHandler cli.DownloadProgressChanged, AddressOf DownloadProgressChanged
ClientQueue.Add(cli)
Next
' Fill the UrlQueue here
UrlQueue.Enqueue("http://www.gnu.org/licenses/gpl-1.0.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/gpl-2.0.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/gpl-3.0.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/lgpl-2.1.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/lgpl-3.0.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/fdl-1.1.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/fdl-1.2.txt")
UrlQueue.Enqueue("http://www.gnu.org/licenses/fdl-1.3.txt")
downloadThread = New Thread(AddressOf downloadQueue)
downloadThread.IsBackground = True
downloadThread.Start()
End Sub
Private Sub downloadQueue()
' Now go until the UrlQueue is empty
While UrlQueue.Count > 0
Dim cli As WebClient = ClientQueue.Take() ' blocks if there is no client available
Dim url As String = UrlQueue.Dequeue()
Dim fname As String = CreateOutputFilename(url)
cli.DownloadFileAsync(New Uri(url), fname, New DownloadArgs(url, fname, cli))
AppendText(url & " started" & vbCrLf)
End While
End Sub
Private Sub DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs)
Dim args As DownloadArgs = DirectCast(e.UserState, DownloadArgs)
' Do status updates for this download
End Sub
Private Sub DownloadFileCompleted(sender As Object, e As AsyncCompletedEventArgs)
Dim args As DownloadArgs = DirectCast(e.UserState, DownloadArgs)
' do whatever UI updates
Dim url As String = "Filename" '<============I'd like to be able to pass the filename or URL but can't figure this out
AppendText(url & " completed" & vbCrLf)
' now put this client back into the queue
ClientQueue.Add(args.Client)
End Sub
Public Function CreateOutputFilename(ByVal url As String) As String
Try
Return url.Substring(url.LastIndexOf("/") + 1)
Catch ex As Exception
Return url
End Try
End Function
Private Delegate Sub SetTextCallback(text As String)
Private Sub AppendText(text As String)
If Me.TextBox1.InvokeRequired Then
TextBox1.Invoke(New Action(Of String)(AddressOf AppendText), text)
Return
End If
Me.TextBox1.AppendText(text)
Me.TextBox1.SelectionStart = TextBox1.TextLength
Me.TextBox1.ScrollToCaret()
End Sub
End Class
Class DownloadArgs
Public ReadOnly Url As String
Public ReadOnly Filename As String
Public ReadOnly Client As WebClient
Public Sub New(u As String, f As String, c As WebClient)
Url = u
Filename = f
Client = c
End Sub
End Class
This will successfully download the first 4 files in the UrlQueue, but it then seems to freeze and no further files download. I'd imagine the problem lies in something minor I missed in the process of converting from C# to vb.net, but I can't seem to figure this out.
ClientQueue.Take() blocks the UI thread. Also, WebClient will want to raise the DownloadFileCompleted event on the UI thread - but it is already blocked by ClientQueue.Take(). You have a deadlock.
To resolve this, you got to move your blocking loop to another background thread.
You are blocking the ability for your async queue to process. Not sure this is the "Correct" way to do this but the changes here make it work:
While UrlQueue.Count > 0
Do While ClientQueue.Count = 0
Application.DoEvents()
Loop
Dim cli As WebClient = ClientQueue.Take() ' blocks if there is no client available
Dim url As String = UrlQueue.Dequeue()
Dim fname As String = CreateOutputFilename(url) ' or however you get the output file name
cli.DownloadFileAsync(New Uri(url), fname, New DownloadArgs(url, fname, cli))
End While

VB.NET TCPClient/Server Issue- Sending on a Timer

So my program starts out by counting how many processes of "XProcess" and has a timer to check every 1 second, which works great! I have the input of the count going into Setting variable. I then have a sub routine that takes that Setting variable, along with a IF statement to output that the TCPClient is sending the string ("One Process").
Well the issue is, I have no event to use with the sub routine and so I tied it in with the timer to send the message out every 1 second. The TCPClient sends it to a local address(127.0.0.1) right now, and sends it to a Textbox. Well PROBLEM!!!
It repeats ("One Process") over and over and over, which I can see why this happens.
So with the Code below, how can the TCPClient send an notification of how many processes of "XProcess" to the TCPServer and the TCPSERVER to spit out that 1 or more processes are running ? (with out the SERVER repeating the string (integer works as wel) over and over again)
The below works, but repeats how many processes are running as a string.
MainWindow.xaml.vb
Imports System.Windows.Threading
Imports System.Net.Sockets
Public Class MainWindow
Private Run_ProgramRunCheck_timer As New DispatcherTimer
Private Run_RecieveCheck_timer As New DispatcherTimer
Dim processCount As Integer
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
ServerStart()
'Check for program changes every second
' Set interval for timer
Run_ProgramRunCheck_timer.Interval = TimeSpan.FromMilliseconds(1000)
'Start timer on button click
Run_ProgramRunCheck_timer.Start()
AddHandler Run_ProgramRunCheck_timer.Tick, AddressOf __ProgramCheck
'Check for Message Recieve every second
' Set interval for timer
Run_RecieveCheck_timer.Interval = TimeSpan.FromMilliseconds(1000)
'Start timer on button click
Run_RecieveCheck_timer.Start()
AddHandler Run_RecieveCheck_timer.Tick, AddressOf _RecieveMessageConvert
TCPClientSender()
End Sub
Public Sub __ProgramCheck()
'This sub will be checked every 1 seconds for changes
'Count number of processes
processCount = Process.GetProcessesByName("tvnviewer").Count()
My.Settings.TotalProcesses = processCount
End Sub
Public Sub TCPClientSender()
My.Settings.TotalProcesses = 0
If My.Settings.TotalProcesses = 1 Then
Dim port As Int32 = 50000
Dim client As New TcpClient("127.0.0.1", port)
' Translate the passed message into ASCII and store it as a Byte array.
Dim data As [Byte]() = System.Text.Encoding.ASCII.GetBytes("One Process")
' Get a client stream for reading and writing.
' Stream stream = client.GetStream();
Dim stream As NetworkStream = client.GetStream()
' Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length)
End If
End Sub
Public Sub _RecieveMessageConvert()
TextBlock1.Text = My.Settings.StoreSentMessage
End Sub
End Class
TCPServer.vb
Imports System.Net
Imports System.Threading
Imports System.Net.Sockets
Imports System.IO
Public Module TCPServer
Dim Server = New TcpListener(IPAddress.Any, 50000) ' <-- Listen on Port 50,000
Dim Client As New TcpClient
Private ServerThread As Thread = Nothing
Dim Message As String = ""
Private Threads As New List(Of Thread)
Public Sub ServerStart()
ServerThread = New Thread(AddressOf ConnectionListener)
ServerThread.IsBackground = True
ServerThread.Start()
End Sub
Private Sub ConnectionListener()
Try
Server.Start()
While True
Dim client As TcpClient = Server.AcceptTcpClient ' Blocks until Connection Request is Received
Dim Reader As New StreamReader(client.GetStream())
While Reader.Peek > -1
Message = Message + Convert.ToChar(Reader.Read()).ToString
End While
My.Settings.StoreSentMessage = Message
End While
Catch ex As Exception
End Try
End Sub
End Module
Dont you just want to do this:
Dim data As [Byte]() = System.Text.Encoding.ASCII.GetBytes(My.Settings.TotalProcesses)
I dont know how you ever get it to say "One Process" when you set the value to zero and then check if the value is 1 on the next line???
My.Settings.TotalProcesses = 0
If My.Settings.TotalProcesses = 1 Then

Multithreading A Function in VB.Net

I am trying to multi thread my application so as it is visible while it is executing the process, this is what I have so far:
Private Sub SendPOST(ByVal URL As String)
Try
Dim DataBytes As Byte() = Encoding.ASCII.GetBytes("")
Dim Request As HttpWebRequest = TryCast(WebRequest.Create(URL.Trim & "/webdav/"), HttpWebRequest)
Request.Method = "POST"
Request.ContentType = "application/x-www-form-urlencoded"
Request.ContentLength = DataBytes.Length
Request.Timeout = 1000
Request.ReadWriteTimeout = 1000
Dim PostData As Stream = Request.GetRequestStream()
PostData.Write(DataBytes, 0, DataBytes.Length)
Dim Response As WebResponse = Request.GetResponse()
Dim ResponseStream As Stream = Response.GetResponseStream()
Dim StreamReader As New IO.StreamReader(ResponseStream)
Dim Text As String = StreamReader.ReadToEnd()
PostData.Close()
Catch ex As Exception
If ex.ToString.Contains("401") Then
TextBox2.Text = TextBox2.Text & URL & "/webdav/" & vbNewLine
End If
End Try
End Sub
Public Sub G0()
Dim siteSplit() As String = TextBox1.Text.Split(vbNewLine)
For i = 0 To siteSplit.Count - 1
Try
If siteSplit(i).Contains("http://") Then
SendPOST(siteSplit(i).Trim)
Else
SendPOST("http://" & siteSplit(i).Trim)
End If
Catch ex As Exception
End Try
Next
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim t As Thread
t = New Thread(AddressOf Me.G0)
t.Start()
End Sub
However, the 'G0' sub code is not being executed at all, and I need to multi thread the 'SendPOST' as that is what slows the application.
Catch ex As Exception
End Try
A very effective way to stop .NET from telling you what you did wrong. Not knowing why it doesn't work is however the inevitable outcome.
Delete that.
Public Class Form1
'This just shows some concepts of threading.
'it isn't intended to do anything
'requires a Button, and two Labels
'
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
'starts / stops a test thread
'isRun = 0 no thread running, start one
'isRun = 1 thread running, stop it
If Threading.Interlocked.Read(isRun) = 0L Then
'start thread
Threading.Interlocked.Increment(isRun)
t = New Threading.Thread(AddressOf showTime)
'simple threading app - display time about twice per second
t.IsBackground = True 'from a background thread
t.Start()
Else
'stop thread
Threading.Interlocked.Exchange(isRun, 0L)
t.Join() 'wait for thread to end
Threading.Monitor.Enter(listLock)
intervalList.Clear() 'clear the list
Threading.Monitor.Exit(listLock)
Label1.Text = "Stop"
Label2.Text = ""
End If
End Sub
Dim t As Threading.Thread
Dim intervalList As New List(Of Double)
Dim listLock As New Object
Dim isRun As Long = 0L
Private Sub showTime()
Dim dlgt As New UpdLblDel(AddressOf UpdateLabel) 'delegate for UI access
Dim lastDateTime As DateTime = Nothing
Do
Dim d As DateTime = DateTime.Now
If lastDateTime <> Nothing Then
'record difference of times - check sleep interval
Threading.Monitor.Enter(listLock)
intervalList.Add((d - lastDateTime).TotalMilliseconds)
Threading.Monitor.Exit(listLock)
End If
lastDateTime = DateTime.Now
dlgt.BeginInvoke(d, Nothing, Nothing) 'update the UI - note immediate return
Threading.Thread.Sleep(500) 'sleep for approx. 500 ms.
Loop While Threading.Interlocked.Read(isRun) = 1L
End Sub
Delegate Sub UpdLblDel(ByVal theTime As Object)
Private Sub UpdateLabel(ByVal theTime As Object)
If Threading.Interlocked.Read(isRun) = 1L Then
If Label1.InvokeRequired Then 'prevent cross-thread errors
Label1.BeginInvoke(New UpdLblDel(AddressOf UpdateLabel), theTime)
Exit Sub
Else
Label1.Text = CType(theTime, DateTime).ToString("HH:mm:ss.f") 'show the time from the background thread
End If
If Threading.Interlocked.Read(intervalList.Count) >= 10L Then
'take average
Threading.Monitor.Enter(listLock)
Dim avg As Double = intervalList.Sum / intervalList.Count 'sum all of the intervals / count
intervalList.Clear() 'clear the list
intervalList.Add(avg) 'forward the average
Label2.Text = avg.ToString("n2") 'show average
Threading.Monitor.Exit(listLock)
End If
End If
End Sub
End Class
You have to wrap the method that accesses the UI component in a delegate (it doesn't have to be a named delegate; it can be anonymous or an Action or Func), and then pass that to Me.Invoke, as others have alluded to.
In this example, I'm wrapping the split functionality in a lambda, and assigning that lambda to a variable of type Func(Of String()). I then pass that variable to Me.Invoke.
Public Sub G0()
Dim siteSplitFunc As Func(Of String()) = Function() _
TextBox1.Text.Split(vbNewLine.ToCharArray())
Dim siteSplit As String() = CType(Me.Invoke(siteSplitFunc), String())
For i = 0 To siteSplit.Count - 1
Try
If siteSplit(i).Contains("http://") Then
SendPOST(siteSplit(i).Trim)
Else
SendPOST("http://" & siteSplit(i).Trim)
End If
Catch ex As Exception
'Do something useful
End Try
Next
End Sub
You cannot access UI object directly from a thread.
When you want to read/write a textbox, you have to do it in the UI thread. This can be done by using Invoke. Or better yet, send/receive the information with parameters.
Here's a Delegate and a matching method. You call the method to update the textbox and it figures out if it should proxy the method for you by basically asking form if its on the same thread:
Private Delegate Sub UpdateTextBoxDelegate(ByVal text As String)
Private Sub UpdateTextBox(ByVal text As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), text)
Else
TextBox2.Text &= text
End If
End Sub
To use it, just change your catch statement to:
If ex.ToString.Contains("401") Then
UpdateTextBox(URL & "/webdav/" & vbNewLine)
End If