I have a stop problem in the backgroundworker, I use the following commands for backgroundworker:
update codes
Private Sub UnpackSystem_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles UnpackSystem.DoWork
Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
For i As Integer = 1 To 100
If worker.CancellationPending Then
e.Cancel = True
Exit For
End If
worker.ReportProgress(i, i & " iterations complete")
Threading.Thread.Sleep(250)
Dim oProcess As New Process()
Dim oStartInfo As New ProcessStartInfo("cmd.exe", "/c bin\Imgtool\simg2img.exe tmp/system.img tmp/system.img.ext4")
oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
oStartInfo.CreateNoWindow = True
oStartInfo.UseShellExecute = False
oStartInfo.RedirectStandardOutput = True
oProcess.StartInfo = oStartInfo
oProcess.Start()
Dim sOutput As String
Using oStreamReader As System.IO.StreamReader = oProcess.StandardOutput
sOutput = oStreamReader.ReadToEnd()
End Using
TextBox8.Invoke(Sub() TextBox8.AppendText(Environment.NewLine & sOutput))
Next i
End Sub
Private Sub UnpackSystem_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles UnpackSystem.ProgressChanged
Me.ProgressBar5.Value = e.ProgressPercentage
End Sub
Private Sub UnpackSystem_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles UnpackSystem.RunWorkerCompleted
If e.Cancelled = True Then
TextBox8.AppendText(Environment.NewLine & "Canceled!")
ElseIf e.Error IsNot Nothing Then
TextBox8.AppendText(Environment.NewLine & "Error: " & e.Error.Message)
Else
TextBox8.AppendText(Environment.NewLine & "Done!")
End If
End Sub
and, I use the following code to stop backgroundworker proccess ...
Private Sub Button55_Click(ByVal sender As System.Object, ByVal e As EventArgs) Handles Button55.Click
If Me.UnpackSystem.IsBusy Then
Me.UnpackSystem.CancelAsync()
End If
Dim cmd() As Process
cmd = Process.GetProcessesByName("cmd")
If cmd.Count > 0 Then
Process.GetProcessesByName("cmd")(0).Kill()
End If
End Sub
But it not be canceled, Where is my problem?
You are only testing whether a cancellation is pending when the DoWork event handler first starts, which of course it will not be at that stage. In order for the background task to be cancelled later on, you would have to actually test whether a cancellation is pending later on.
There's no magic here though. As I said, in order to cancel, you have to actually test whether a cancellation is pending within the DoWork event handler. That test can only come between other lines of code. Once you call that ReadToEnd method, you are going to read the stream to the end, whether the user has requested cancellation or not. You obviously can't test for a pending cancelation until that call returns
Related
I have added a Timer control to a vb.net application that is used to check if a website is up or down. There are two Timer controls in the app. Both are from System.Windows.Forms.Timer.
There is Timer1 and Timer2. Timer1 is used to perform the website check. Timer2 is simply to display a current date and time in the ToolStrip at the base of the form. The Timer2 runs without issue displaying the time, but when I start the website check that runs Timer1, the application freezes after two checks. Sometimes it runs longer, but eventually freezes.
I have to End Task from within Task Manager in order to shut down the application. I have tested this from within the Debugger and after running the executable. I have also completely removed Timer2 and tested, but the freezing remains. Here is my code. Any assistance would be greatly appreciated.
Imports System
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
lblStatus.Text = ""
Timer1.Stop()
btnStart.Enabled = True
btnStop.Enabled = False
End Sub
Public Function CheckAddress(ByVal URL As String) As Boolean
Try
Dim request As WebRequest = WebRequest.Create(URL)
Dim response As WebResponse = request.GetResponse()
Catch ex As Exception
Return False
End Try
Return True
End Function
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
Dim startPoint As Integer = -1
If CheckAddress(txtAddress.Text).ToString() = True Then
rtxtResults.AppendText(" -- " & txtAddress.Text & " - Website shows UP at " & tsClock.Text & " - " & vbNewLine)
ElseIf CheckAddress(txtAddress.Text).ToString() = False Then
rtxtResults.AppendText(" -- " & txtAddress.Text & " - Website shows DOWN at " & tsClock.Text & " - " & vbNewLine)
End If
Do
startPoint = rtxtResults.Find("Website shows DOWN at", startPoint + 1, RichTextBoxFinds.None)
If (startPoint >= 0) Then
rtxtResults.SelectionStart = startPoint
rtxtResults.SelectionLength = "Website shows DOWN at".Length
rtxtResults.SelectionColor = Color.Red
End If
Loop Until startPoint < 0
End Sub
Private Sub Timer2_Tick(sender As System.Object, e As System.EventArgs)
tsClock.Text = Now()
End Sub
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
If txtInterval.Text = "" Then
MsgBox("Must enter a number")
Exit Sub
End If
lblStatus.ForeColor = Color.Green
lblStatus.Text = "Running"
btnStart.Enabled = False
btnStop.Enabled = True
Timer1.Interval = Int(txtInterval.Text) * 1000
Timer1.Start()
End Sub
Private Sub btnStop_Click(sender As System.Object, e As System.EventArgs) Handles btnStop.Click
lblStatus.ForeColor = Color.Red
lblStatus.Text = "Stopped"
btnStop.Enabled = False
btnStart.Enabled = True
Timer1.Stop()
End Sub
End Class
Your application's UI will "freeze" until the Timer1_Tick method completes. Most likely your Timer1_Tick's Do...Loop is not exiting. Try deleting the Do and Loop lines.
I'm having an issue where BackgroundWorker.CancelAsync() isn't working. I have WorkerSupportsCancellation set to TRUE. I am also polling BackgroundWorker1.CancellationPending in DoWork. Here is sample code of what I am trying to achieve. I have background worker looping through a time stamp and assigning a value to Measurement variable. I have a subroutine that queries the last reported Measurement variable and writes to listbox. After 5 loops I send BackgroundWorker.CancelAsync(). I can see the cancellation is pending, but it doesn't actually cancel the background worker. Is this a race condition?
Public Class Form1
Dim Measurement As String
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
Delay(0.5)
For x = 1 To 5
ListBox1.Items.Add(Measurement)
ListBox1.TopIndex = ListBox1.Items.Count - 1
TextBox1.Text = BackgroundWorker1.CancellationPending
Delay(1)
Next
BackgroundWorker1.CancelAsync()
TextBox1.Text = BackgroundWorker1.CancellationPending
ListBox1.Items.Add("Cancel Pend: " & BackgroundWorker1.CancellationPending)
Delay(5)
ListBox1.Items.Add("Busy: " & BackgroundWorker1.IsBusy)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Sub
Else
Do
Measurement = Now()
Loop
End If
End Sub
End Class
You just need to move the check for the cancellation inside the Do...Loop otherwise it will be tested only at the start of the DoWork event handler and never after that
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Else
Do
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Do
Else
Measurement = Now()
End If
Loop
End if
End Sub
I am using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?
Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub
This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Using the following VB.Net simple code to upload files in FTP, a call to WebClient.CancelAsync() doesn't actually cancel the upload.
Does someone know why, and what can be done about this?
Private Sub UploadProgressChanged(ByVal sender As Object, ByVal e As System.Net.UploadProgressChangedEventArgs)
'TO-DO: Why is pbar empty?
ProgressBar1.Value = e.ProgressPercentage
Label1.Text = e.BytesSent & " bytes sent"
End Sub
Private Sub UploadFileCompleted(ByVal sender As Object, ByVal e As System.Net.UploadFileCompletedEventArgs)
MessageBox.Show("Done!")
Button1.Text = "Upload"
ProgressBar1.Value = 0
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim client As New WebClient
If Button1.Text = "Cancel" Then
'TO-DO: Doesn't actually cancel upload!
client.CancelAsync()
Button1.Text = "Upload"
ProgressBar1.Value = 0
Else
Button1.Text = "Cancel"
Const MYFILE = "big.file.bin"
Const LocalFile As String = "C:\" & MYFILE
Dim RemoteFile As String = "ftp://upload.acme.com/" & MYFILE
client.Credentials = New NetworkCredential("anonymous", "test")
client.Proxy = Nothing
AddHandler client.UploadFileCompleted, AddressOf UploadFileCompleted
AddHandler client.UploadProgressChanged, AddressOf UploadProgressChanged
ProgressBar1.Maximum = 100
Try
client.UploadFileAsync(New Uri(RemoteFile), LocalFile)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
client.Dispose()
End If
End Sub
Thank you.
I don't use VB myself but it looks to me like you are calling client.cancelAsync() on the wrong client.
You are making a new client each time you press the cancel/upload button.
If you want to cancel the first client you started you need to instantiate it externally to the click_handler.
Also why are you using the button label to check if the client should be cancelled?
You can do if client.IsBusy ...
Im using Vb.net visual studio 2008. i have got a process(system.diagnostics.process) to be run in background and it updates the Ui thread progressbar and a label and i have worked it using backgroundworker.runworkerAsync. Now the problem is i have to work the same process for a number of time with different inputs.
The code block is :
Private Sub fnStartEvent(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.click
Dim inputFolder = Directory.GetFiles(textboxSourceFolder.text)
Dim currentNumberOfFile As Integer=1
For each Files in inputFolder
Dim arguments As Object = Files
updateProgressStatus(0, currentNumberOfFile)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync(New Object() {arguments})
currentNumberOfFile += 1
Next
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'uses the arguments
Dim Bg_process As System.Diagnostics.Process = New System.Diagnostics.Process
With Bg_process.StartInfo
.Arguments = str_arguments
.FileName = ffmpegPath
.CreateNoWindow = True
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardError = True
End With
Bg_process.Start()
Dim outputReader As StreamReader = Bg_process.StandardError
Dim output As String
While Not Bg_process.HasExited
output = outputReader.ReadLine()
BackgroundWorker1.ReportProgress(0, output)
Threading.Thread.Sleep(500)
End While
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
' process args
updateProgressStatus(args(0), args(1) )
End Sub
Private Function BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Messagebox.Show(e.error.ToString)
End Try
Sub updateProgressStatus(ByVal progressValue As Integer, ByVal progressStatus As String)
progressBar.value = progressValue
lblprogressStatus.Text = progressStatus
End Sub
The problem in here is the fnStartEvent method once gets started, it calls BackgroundWorker1.runWorkerAsync process and that runs in a seperate thread and does not wait for the thread to complete and it moves to the next line of code i.e loops to the next item and it returns to same BackgroundWorker1.runWorkerAsync line and throws exception that it is already running.
Tried
1.
Private Sub fnStartEvent(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.click
Dim inputFolder = Directory.GetFiles(textboxSourceFolder.text)
Dim currentNumberOfFile As Integer=1
For each Files in inputFolder
Dim arguments As Object = Files
updateProgressStatus(0, currentNumberOfFile)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync(New Object() {arguments})
''
Do Until BackgroundWorker1.IsBusy
Thread.Sleep(500)
Loop
currentNumberOfFile += 1
Next
End Sub
But this does not updates the Ui thread used to denote progress.
2.Put the whole process inside the DoWork thread and loop for each file in there itself, but that to returns the cross thread error in progress bar updation.
what is the standard procedure in executing a loop for the backgroundworker to wait for finishing as well as update the UI without blocking it.
Whenever you use Thread.Sleep, you are actually sleeping the current thread that is hooked to your startup class (ie. Form). So, if you sleep your main thread, no UI updates will happen.
Per the MSDN article regarding BackgroundWorker.IsBusy, you need to throw an Application.DoEvents. Code below is copied from the article linked above.
Private Sub downloadButton_Click( _
ByVal sender As Object, _
ByVal e As EventArgs) _
Handles downloadButton.Click
' Start the download operation in the background.
Me.backgroundWorker1.RunWorkerAsync()
' Disable the button for the duration of the download.
Me.downloadButton.Enabled = False
' Once you have started the background thread you
' can exit the handler and the application will
' wait until the RunWorkerCompleted event is raised.
' If you want to do something else in the main thread,
' such as update a progress bar, you can do so in a loop
' while checking IsBusy to see if the background task is
' still running.
While Me.backgroundWorker1.IsBusy
progressBar1.Increment(1)
' Keep UI messages moving, so the form remains
' responsive during the asynchronous operation.
Application.DoEvents()
End While
End Sub