Re: BackgroundWorker problem
For future reference, please only post RELEVANT code because everything else just clouds the issue. You ask a question about downloading files using a BackgroundWorker and we have to wade through irrelevant stuff about resizing and persisting size information and unmanaged that we don't care about. If you make it harder for us to help then it's a waste of our time and makes it less likely that you'll get the help you want, so everybody loses.
Anyway, a BackgroundWorker can only run once at a time. Either check the IsBusy property first to make sure that you don't try to run it more than once at a time or else find another way to implement your multi-threading so that you can perform multiple background tasks at the same time, e.g. Task or ThreadPool.
Also, get rid of that line that sets CheckForIllegalCrossThreadCalls to False and NEVER do that again. That defeats the purpose of the BackgroundWorker in the first place. Follow the CodeBank link in my signature below and check out my thread on Using The BackgroundWorker to learn how to use it properly. DO NOT do anything related to the UI in the DoWork event handler. Anything that touches the UI gets done in the ProgressChanged or RunWorkerCompleted event handler. That's why the BackgroundWorker exists.
Re: BackgroundWorker problem
VS 2012 has new features to make what you are trying to do as simple as apple pie
Async and Await
I just posted code that is fully asynchronous and downloads multiple files while looping through a regular expression match collection and updates the UI all without using a bacgroundworker or using mutli-threading.
I think you could accomplish what you want to do by replacing the BacgroundWorker.RunWorkerAsync and calling the code below instead. .
I haven't tested it but I only changed 2 things from your original code.
vb.net Code:
Private Async Sub NoMoreBlastedBackgroundWorker()
'mod this
FileName = Label5.Text
File.Delete("C:\Windows\Temp\MPNGTemp\" & FileName)
If FileName = "GitPortable.zip" Then
link = "http://www.sshcs.com/MPNG/ArduPilot-Arduino-1.0.3-windows.zip"
End If
If FileName = "ArduPilot-Arduino-1.0.3-windows.zip" Then
link = "http://www.sshcs.com/MPNG/GitPortable.zip"
End If
If FileName = "MHV_AVR_Tools_20121007.exe" Then
link = "http://firmware.diydrones.com/Tools/Arduino/MHV_AVR_Tools_20121007.exe"
End If
Try
Dim size As Integer
Dim wr As WebRequest
wr = WebRequest.Create(link)
' Async response.
Dim webr As WebResponse = Await wr.GetResponseAsync
size = webr.ContentLength
size = size / 1024
ProgressBar1.Maximum = size
Label2.Text = size
Dim wc As New WebClient
'Async downloading file. Simple as pie!
Await wc.DownloadFileTaskAsync(link, "C:\Windows\Temp\MPNGTemp\" & FileName)
RichTextBox1.AppendText("Finished downloading " & FileName & vbCrLf)
Catch ex As Exception
End Try
End Sub
Re: BackgroundWorker problem
Quote:
Originally Posted by
jmcilhinney
For future reference, please only post RELEVANT code because everything else just clouds the issue. You ask a question about downloading files using a BackgroundWorker and we have to wade through irrelevant stuff about resizing and persisting size information and unmanaged that we don't care about. If you make it harder for us to help then it's a waste of our time and makes it less likely that you'll get the help you want, so everybody loses.
Anyway, a BackgroundWorker can only run once at a time. Either check the IsBusy property first to make sure that you don't try to run it more than once at a time or else find another way to implement your multi-threading so that you can perform multiple background tasks at the same time, e.g. Task or ThreadPool.
Also, get rid of that line that sets CheckForIllegalCrossThreadCalls to False and NEVER do that again. That defeats the purpose of the BackgroundWorker in the first place. Follow the CodeBank link in my signature below and check out my thread on Using The BackgroundWorker to learn how to use it properly. DO NOT do anything related to the UI in the DoWork event handler. Anything that touches the UI gets done in the ProgressChanged or RunWorkerCompleted event handler. That's why the BackgroundWorker exists.
Sorry :(
The problem [for me at least] is I don't WANT it to run all 3 at once. I want it to run one, and when it's complete then run the next, and when that is completed run the final one.
I will look into the IsBusy event and see if that will do what I need.
thanks for taking the time to set me straight on a few key aspects of the question.
Re: BackgroundWorker problem
Quote:
Originally Posted by
OICU812
VS 2012 has new features to make what you are trying to do as simple as apple pie
Async and Await
I just
posted code that is fully
asynchronous and downloads multiple files while looping through a regular expression match collection and updates the UI all without using a bacgroundworker or using mutli-threading.
I think you could accomplish what you want to do by replacing the BacgroundWorker.RunWorkerAsync and calling the code below instead. .
I haven't tested it but I only changed 2 things from your original code.
vb.net Code:
Private Async Sub NoMoreBlastedBackgroundWorker()
'mod this
FileName = Label5.Text
File.Delete("C:\Windows\Temp\MPNGTemp\" & FileName)
If FileName = "GitPortable.zip" Then
link = "http://www.sshcs.com/MPNG/ArduPilot-Arduino-1.0.3-windows.zip"
End If
If FileName = "ArduPilot-Arduino-1.0.3-windows.zip" Then
link = "http://www.sshcs.com/MPNG/GitPortable.zip"
End If
If FileName = "MHV_AVR_Tools_20121007.exe" Then
link = "http://firmware.diydrones.com/Tools/Arduino/MHV_AVR_Tools_20121007.exe"
End If
Try
Dim size As Integer
Dim wr As WebRequest
wr = WebRequest.Create(link)
' Async response.
Dim webr As WebResponse = Await wr.GetResponseAsync
size = webr.ContentLength
size = size / 1024
ProgressBar1.Maximum = size
Label2.Text = size
Dim wc As New WebClient
'Async downloading file. Simple as pie!
Await wc.DownloadFileTaskAsync(link, "C:\Windows\Temp\MPNGTemp\" & FileName)
RichTextBox1.AppendText("Finished downloading " & FileName & vbCrLf)
Catch ex As Exception
End Try
End Sub
I'm guessing you are doing this against a different version of .NET
I need to target this for 3.5 or lower, because as sucky as it is, quite a few peeps still run XP.
When I added that I get a ton of errors :( I read your other post and if it wasn't for the need to support XP it looks EXACTLY like what I need :)
sigh. Back to seeing if IsBusy can do what I need.
Thanks for taking the time to respond.
Re: BackgroundWorker problem
Quote:
Originally Posted by
thequestor
Sorry :(
The problem [for me at least] is I don't WANT it to run all 3 at once. I want it to run one, and when it's complete then run the next, and when that is completed run the final one.
I will look into the IsBusy event and see if that will do what I need.
thanks for taking the time to set me straight on a few key aspects of the question.
Are you saying that you want the first download done on the first click, the second on the second and the third on the third, or that you want all three done on the one click, one after the other?
Re: BackgroundWorker problem
Quote:
Originally Posted by
jmcilhinney
Are you saying that you want the first download done on the first click, the second on the second and the third on the third, or that you want all three done on the one click, one after the other?
all 3 one after the other on a single click. I don't have an issue with it if I do it one by one [ie a click for each]. :)
Re: BackgroundWorker problem
Quote:
Originally Posted by
thequestor
all 3 one after the other on a single click. I don't have an issue with it if I do it one by one [ie a click for each]. :)
Then write a method that downloads a file and call it three times from the DoWork event handler. It's that simple. Download one file, then download the second and then download the third.
Re: BackgroundWorker problem
Quote:
Originally Posted by
jmcilhinney
Then write a method that downloads a file and call it three times from the DoWork event handler. It's that simple. Download one file, then download the second and then download the third.
That's what I thought I already did :( which is the reason I came here asking.
Re: BackgroundWorker problem
Quote:
Originally Posted by
thequestor
That's what I thought I already did :( which is the reason I came here asking.
Look at this from your DoWork event handler:
Code:
If Filename = "GitPortable.zip" Then
link = "http://www.sshcs.com/MPNG/ArduPilot-Arduino-1.0.3-windows.zip"
End If
If Filename = "ArduPilot-Arduino-1.0.3-windows.zip" Then
link = "http://www.sshcs.com/MPNG/GitPortable.zip"
End If
If Filename = "MHV_AVR_Tools_20121007.exe" Then
link = "http://firmware.diydrones.com/Tools/Arduino/MHV_AVR_Tools_20121007.exe"
End If
Filename can't be all three of those values can it? It can only have one value at a time so your code is only going to download one file at a time.
Re: BackgroundWorker problem
I "thought" I could just make a collection and just loop through them, like I do with arrays, one at a time, but because, I guess, they are running async it is running all 3 without waiting for the 1st loop to finish. I haven't found a way to do it synchronous.
I pretty much was hoping it would be able to run the 1st loop and finish then run the 2nd loop and finish and finally the last loop.
I mean I guess I could somehow chain them but I was trying out just using one bit of code to do all 3 things :(
Re: BackgroundWorker problem
You can use a loop. There is only one secondary thread so only one file can be downloaded at a time. Do exactly what you just said and it will work.
Re: BackgroundWorker problem
Quote:
Originally Posted by
jmcilhinney
You can use a loop. There is only one secondary thread so only one file can be downloaded at a time. Do exactly what you just said and it will work.
How would I go about doing so with this sub?
Code:
Public Sub CallWorker()
Dim colFiles As New Collection
Dim intCtr As Integer
'the collection
colFiles.Add("GitPortable.zip")
colFiles.Add("ArduPilot-Arduino-1.0.3-windows.zip")
colFiles.Add("MHV_AVR_Tools_20121007.exe")
'the loop
For intCtr = 1 To colFiles.Count
If System.IO.File.Exists("C:\Windows\Temp\MPNGTemp\" & colFiles(intCtr)) Then
RichTextBox1.AppendText(colFiles(intCtr) & " Already exists so skipping" & vbCrLf)
Else
Label5.Text = colFiles(intCtr)
RichTextBox1.AppendText(colFiles(intCtr) & " does not exist so downloading" & vbCrLf)
'Control.CheckForIllegalCrossThreadCalls = False
'starts the progress bar
Timer1.Start()
'starts the download sub
BackgroundWorker.RunWorkerAsync()
End If
'end of loop
Next intCtr
End Sub
That is the loop I am using to call the backgroundworker.
And it is calling this as the background worker
Code:
Private Sub Downloader_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
'mod this
Filename = Label5.Text
File.Delete("C:\Windows\Temp\MPNGTemp\" & Filename)
If Filename = "ArduPilot-Arduino-1.0.3-windows.zip" Then
link = "http://www.sshcs.com/MPNG/ArduPilot-Arduino-1.0.3-windows.zip"
ElseIf Filename = "GitPortable.zip" Then
link = "http://www.sshcs.com/MPNG/GitPortable.zip"
ElseIf Filename = "MHV_AVR_Tools_20121007.exe" Then
link = "http://firmware.diydrones.com/Tools/Arduino/MHV_AVR_Tools_20121007.exe"
ElseIf Filename = "" Then
MsgBox("Error No File Passed")
End If
Try
Dim size As Integer
Dim wr As WebRequest
wr = WebRequest.Create(link)
Dim webr As WebResponse = wr.GetResponse
size = webr.ContentLength
size = size / 1024
ProgressBar1.Maximum = size
Label2.Text = size
Dim wc As New WebClient
wc.DownloadFile(link, "C:\Windows\Temp\MPNGTemp\" & Filename)
RichTextBox1.AppendText("Finished downloading " & Filename & vbCrLf)
Catch ex As Exception
End Try
Exit Sub
Each by themselves work fine and both work fine together if say 2 of the files are already there [it doesn't matter which 2]
How do I call a backgroundworker and halt any further execution of the sub until the initial loop finishes running and then running the 2nd loop and when it's done run the last loop?
Re: BackgroundWorker problem
I thought I was onto something :(
Code:
Public Sub CallWorker()
Dim colFiles As New Collection
Dim intCtr As Integer
colFiles.Add("GitPortable.zip")
colFiles.Add("ArduPilot-Arduino-1.0.3-windows.zip")
colFiles.Add("MHV_AVR_Tools_20121007.exe")
For intCtr = 1 To colFiles.Count
If System.IO.File.Exists("C:\Windows\Temp\MPNGTemp\" & colFiles(intCtr)) Then
RichTextBox1.AppendText(colFiles(intCtr) & " Already exists so skipping" & vbCrLf)
Else
Label5.Text = colFiles(intCtr)
RichTextBox1.AppendText(colFiles(intCtr) & " does not exist so downloading" & vbCrLf)
'Control.CheckForIllegalCrossThreadCalls = False
Timer1.Start()
'BackgroundWorker.RunWorkerAsync()
Try
'Start background worker
BackgroundWorker.RunWorkerAsync()
While BackgroundWorker.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Voila, we are back in sync
RichTextBox1.AppendText("Success!" & vbCrLf)
Catch ex As Exception
'MsgBox("Oops!" & vbCrLf & ex.Message)
End Try
End If
Next intCtr
and while it does seem to do "something" it doesn't actually download anything lol
Re: BackgroundWorker problem
Get rid of that CallWorker method entirely. When the user clicks the Button, just call RunWorkerAsync and that's it. Now, in the DoWork event handler, create your list of URLs and loop through it. Done!
As I have already said, DO NOT touch any controls in the DoWork event handler. If you want to update the UI then do so ONLY in the ProgressChanged and/or RunWorkerCompleted event handler.
Re: BackgroundWorker problem
Quote:
Originally Posted by
jmcilhinney
Get rid of that CallWorker method entirely. When the user clicks the Button, just call RunWorkerAsync and that's it. Now, in the DoWork event handler, create your list of URLs and loop through it. Done!
As I have already said, DO NOT touch any controls in the DoWork event handler. If you want to update the UI then do so ONLY in the ProgressChanged and/or RunWorkerCompleted event handler.
I kind of get what you're saying, but in doing that I lose my working progress bar and bytes and size on my form no?
Re: BackgroundWorker problem
Quote:
Originally Posted by
thequestor
I kind of get what you're saying, but in doing that I lose my working progress bar and bytes and size on my form no?
Um, no.
Re: BackgroundWorker problem
I'm giving up :(
I removed callworker
I moved my loop into downloader_dowork
but as I thought I had to remove everything that would update my progressbar and labels [like you said remove gui stuff] else it errors like mad
I ended up with this
Code:
Private Sub Downloader_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
Dim colFiles As New Collection
Dim intCtr As Integer
colFiles.Add("GitPortable.zip")
colFiles.Add("ArduPilot-Arduino-1.0.3-windows.zip")
colFiles.Add("MHV_AVR_Tools_20121007.exe")
For intCtr = 1 To colFiles.Count
If System.IO.File.Exists("C:\Windows\Temp\MPNGTemp\" & colFiles(intCtr)) Then
'RichTextBox1.AppendText(colFiles(intCtr) & " Already exists so skipping" & vbCrLf)
Else
'Label5.Text = colFiles(intCtr)
'RichTextBox1.AppendText(colFiles(intCtr) & " does not exist so downloading" & vbCrLf)
'Timer1.Start()
Filename = colFiles(intCtr)
If Filename = "ArduPilot-Arduino-1.0.3-windows.zip" Then
link = "http://www.sshcs.com/MPNG/ArduPilot-Arduino-1.0.3-windows.zip"
ElseIf Filename = "GitPortable.zip" Then
link = "http://www.sshcs.com/MPNG/GitPortable.zip"
ElseIf Filename = "MHV_AVR_Tools_20121007.exe" Then
link = "http://firmware.diydrones.com/Tools/Arduino/MHV_AVR_Tools_20121007.exe"
ElseIf Filename = "" Then
MsgBox("Error No File Passed")
End If
File.Delete("C:\Windows\Temp\MPNGTemp\" & Filename)
Try
Dim size As Integer
Dim wr As WebRequest
wr = WebRequest.Create(link)
Dim webr As WebResponse = wr.GetResponse
size = webr.ContentLength
size = size / 1024
'ProgressBar1.Maximum = size
'Label2.Text = size
Dim wc As New WebClient
wc.DownloadFile(link, "C:\Windows\Temp\MPNGTemp\" & Filename)
'RichTextBox1.AppendText("Finished downloading " & Filename & vbCrLf)
Catch ex As Exception
End Try
End If
Next intCtr
Exit Sub
End Sub
and it "does" download each file and skips any that are already there but like I said above I lose my progress bar and labels updating and
at the end it tosses an error that says I have to remove my just in time debugger to see the error but what I seem to see is it is a crossthreading issue.
sigh.
I'm just going to run 3 routines to do this.
2 Attachment(s)
Re: BackgroundWorker problem
Sorry my other suggestion didn't work for you since you're trying to support XP
I wrote a little test code that runs a loop and calls a method that creates a new Backgroundworker each time.
All of the event handlers for the BackgroundWorker are self contained in the DoWorkAsync method.
Try it and see if it might help you create what you want.
Add a Form to the project and 1 Button and a RichTextBox to the form
Attachment 112231
vb.net Code:
Imports System.ComponentModel
Public Class Form1
Private _cancelToken As Boolean
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.RichTextBox1.Clear()
_cancelToken = False
For i = 1 To 10
Dim name = "Thread " & i.ToString
DoWorkAsync(name)
Next
End Sub
' Create any parameters needed to download your file.
Private Sub DoWorkAsync(threadName As String)
' fresh new BackgroundWorker is created each time.
Dim worker As New BackgroundWorker
worker.WorkerReportsProgress = True
AddHandler worker.DoWork,
Sub(s, e)
' do not access UI controls or update UI
' controls from within worker.DoWork
Dim w = DirectCast(s, BackgroundWorker)
'Dim wc As New System.Net.WebClient
'wc.DownloadFile("someinternetaddress", "someFileName")
For i = 1 To 10
'If _cancelToken = true Then
' Exit For
'Else
Threading.Thread.Sleep(500) 'simulate long running task.
w.ReportProgress(i * 10)
'End If
Next
End Sub
AddHandler worker.ProgressChanged,
Sub(s, e)
' Safe to update UI from here.
Me.RichTextBox1.AppendText(threadName & " is at " & e.ProgressPercentage & "%." & Environment.NewLine)
End Sub
AddHandler worker.RunWorkerCompleted,
Sub(s, e)
' Safe to update UI from here.
Me.RichTextBox1.AppendText(threadName & " RunWorkercompleted!" & Environment.NewLine)
End Sub
' Start the asynchrouous operation.
worker.RunWorkerAsync()
End Sub
End Class
Sigh... I just read that you dont want the downloads to run concurrent.
Edit:
Ok I just reconfigured the code to use an Queue to fire each BackgroundWorker successive order.
Edited and removed the use of the Tuple
Attachment 112233
vb.net Code:
Imports System.ComponentModel
Public Class Form1
Private _cancelToken As Boolean
Private _actionQueue As New Queue(Of Action)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Me._actionQueue.Count > 0 Then Exit Sub
Me.RichTextBox1.Clear()
Me._actionQueue.Clear()
_cancelToken = False
For i = 1 To 10
Dim name = "Thread " & i.ToString
' Create an action and place it in the queue
_actionQueue.Enqueue(CreateWorkerItem(name))
Next
' Dequeue the Action and invoke it.
_actionQueue.Dequeue().Invoke()
End Sub
' Create any parameters needed to download your file.
Private Function CreateWorkerItem(threadName As String) As Action
Dim myAction = New Action(
Sub()
' fresh new BackgroundWorker is created each time.
Dim worker As New BackgroundWorker
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
AddHandler worker.DoWork,
Sub(s, e)
' do not access UI controls or update UI
' controls from within worker.DoWork
Dim w = DirectCast(s, BackgroundWorker)
'Dim wc As New System.Net.WebClient
'wc.DownloadFile("someinternetaddress", "someFileName")
For i = 1 To 10
'If _cancelToken = True Then
' w.CancelAsync()
'Else
Threading.Thread.Sleep(100) 'simulate long running task.
'If _cancelToken = True Then w.CancelAsync()
w.ReportProgress(i * 10)
'End If
Next
End Sub
AddHandler worker.ProgressChanged,
Sub(s, e)
' Safe to update UI from here.
Me.RichTextBox1.AppendText(threadName & " is at " & e.ProgressPercentage & "%." & Environment.NewLine)
Me.RichTextBox1.ScrollToCaret()
End Sub
AddHandler worker.RunWorkerCompleted,
Sub(s, e)
' Safe to update UI from here.
Me.RichTextBox1.AppendText(threadName & " RunWorkercompleted!" & Environment.NewLine)
Me.RichTextBox1.ScrollToCaret()
' Dequeue the next item in the queue.
If _actionQueue.Count > 0 Then
_actionQueue.Dequeue().Invoke()
Else
_actionQueue.Clear()
End If
End Sub
' Start the asynchrouous operation.
worker.RunWorkerAsync()
End Sub)
' Return a tuple to hold the Action and the ThreadName parameter.
Return myAction
End Function
End Class
Re: BackgroundWorker problem
I see in your last code where you did like jmcilhinney suggested and loop inside the DoWork event that you would never be able to update your labels until the very end when the BackGroundWorker fires the RunWorkerCompleted event. However, you probably could pass whatever information between each loop to the reportProgress UserState object and work out the messages for the labels in the ProgressChanged event.
My Last post above here could be simplified and not have to used the Tuple or have to carry the arguments with the Queue. All it needs to be is a Queue(of Action). It works pretty slick, I never messed with programming this way but I learned something new in the process.
Re: BackgroundWorker problem
A more simpler if thats to confusing
vb Code:
Imports System.Net
Public Class MainForm
Private ReadOnly temp As String = "http://download.piriform.com/ccsetup327.exe"
Private m_dictionary As New Dictionary(Of WebClient, String)
Private Sub Download(ByVal Path As String, ByVal FileName As String)
Dim client As New WebClient
Dim file As String = IO.Path.Combine(Path, FileName)
AddHandler client.DownloadProgressChanged, AddressOf DownloadProgressChanged
AddHandler client.DownloadFileCompleted, AddressOf DownloadFileCompleted
client.DownloadFileAsync(New Uri(temp), file)
Me.m_dictionary.Add(client, file)
End Sub
Private Sub DownloadProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim file As String = m_dictionary.Item(CType(sender, WebClient))
Me.RichTextBox1.AppendText(String.Format("File {0} {1}{2}", file, e.ProgressPercentage & "%", vbNewLine))
Me.RichTextBox1.ScrollToCaret()
End Sub
Private Sub DownloadFileCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
Dim client As WebClient = DirectCast(sender, WebClient)
client.Dispose()
m_dictionary.Remove(client)
End Sub
End Class
Re: BackgroundWorker problem
Quote:
Originally Posted by
ident
A more simpler if thats to confusing
vb Code:
Imports System.Net
Public Class MainForm
Private ReadOnly temp As String = "http://download.piriform.com/ccsetup327.exe"
Private m_dictionary As New Dictionary(Of WebClient, String)
Private Sub Download(ByVal Path As String, ByVal FileName As String)
Dim client As New WebClient
Dim file As String = IO.Path.Combine(Path, FileName)
AddHandler client.DownloadProgressChanged, AddressOf DownloadProgressChanged
AddHandler client.DownloadFileCompleted, AddressOf DownloadFileCompleted
client.DownloadFileAsync(New Uri(temp), file)
Me.m_dictionary.Add(client, file)
End Sub
Private Sub DownloadProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim file As String = m_dictionary.Item(CType(sender, WebClient))
Me.RichTextBox1.AppendText(String.Format("File {0} {1}{2}", file, e.ProgressPercentage & "%", vbNewLine))
Me.RichTextBox1.ScrollToCaret()
End Sub
Private Sub DownloadFileCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
Dim client As WebClient = DirectCast(sender, WebClient)
client.Dispose()
m_dictionary.Remove(client)
End Sub
End Class
Don't forget to call RemoveHandler for each WebClient or otherwise they will not get G.C.'d
Re: BackgroundWorker problem
Of course, but also i should be using the "using statement". I can't write the whole project for them. I also never include try catch in my examples.