How to stop BackgroundWorker on Form's Closing event? - c#

I have a form that spawns a BackgroundWorker, that should update form's own textbox (on main thread), hence Invoke((Action) (...)); call.
If in HandleClosingEvent I just do bgWorker.CancelAsync() then I get ObjectDisposedException on Invoke(...) call, understandably. But if I sit in HandleClosingEvent and wait for bgWorker to be done, than .Invoke(...) never returns, also understandably.
Any ideas how do I close this app without getting the exception, or the deadlock?
Following are 3 relevant methods of the simple Form1 class:
public Form1() {
InitializeComponent();
Closing += HandleClosingEvent;
this.bgWorker.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); }));
}
}
private void HandleClosingEvent(object sender, CancelEventArgs e) {
this.bgWorker.CancelAsync();
/////// while (this.bgWorker.CancellationPending) {} // deadlock
}

The only deadlock-safe and exception-safe way to do this that I know is to actually cancel the FormClosing event. Set e.Cancel = true if the BGW is still running and set a flag to indicate that the user requested a close. Then check that flag in the BGW's RunWorkerCompleted event handler and call Close() if it is set.
private bool closePending;
protected override void OnFormClosing(FormClosingEventArgs e) {
if (backgroundWorker1.IsBusy) {
closePending = true;
backgroundWorker1.CancelAsync();
e.Cancel = true;
this.Enabled = false; // or this.Hide()
return;
}
base.OnFormClosing(e);
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (closePending) this.Close();
closePending = false;
// etc...
}

I've found another way. If you have more backgroundWorkers you can make:
List<Thread> bgWorkersThreads = new List<Thread>();
and in every backgroundWorker's DoWork method make:
bgWorkesThreads.Add(Thread.CurrentThread);
Arter that you can use:
foreach (Thread thread in this.bgWorkersThreads)
{
thread.Abort();
}
I used this in Word Add-in in Control, which i use in CustomTaskPane. If someone close the document or application earlier then all my backgroundWorkes finishes their work, it raises some COM Exception(I don't remember exatly which).CancelAsync() doesn't work.
But with this, I can close all threads which are used by backgroundworkers Immediately in DocumentBeforeClose event and my problem is solved.

Here was my solution (Sorry it's in VB.Net).
When I run the FormClosing event I run BackgroundWorker1.CancelAsync() to set the CancellationPending value to True. Unfortunately, the program never really gets a chance to check the value CancellationPending value to set e.Cancel to true (which as far as I can tell, can only be done in BackgroundWorker1_DoWork).
I didn't remove that line, although it doesn't really seem to make a difference.
I added a line that would set my global variable, bClosingForm, to True. Then I added a line of code in my BackgroundWorker_WorkCompleted to check both e.Cancelled as well as the global variable, bClosingForm, before performing any ending steps.
Using this template, you should be able to close your form out at any time even if the backgroundworker is in the middle of something (which might not be good, but it's bound to happen so it might as well be dealt with). I'm not sure if it's necessary, but you could dispose the Background worker entirely in the Form_Closed event after this all takes place.
Private bClosingForm As Boolean = False
Private Sub SomeFormName_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
bClosingForm = True
BackgroundWorker1.CancelAsync()
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Run background tasks:
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Else
'Background work here
End If
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Not bClosingForm Then
If Not e.Cancelled Then
'Completion Work here
End If
End If
End Sub

Can you not wait on the signal in the destructor of the form?
AutoResetEvent workerDone = new AutoResetEvent();
private void HandleClosingEvent(object sender, CancelEventArgs e)
{
this.bgWorker.CancelAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text =
Environment.TickCount.ToString(); }));
}
}
private ~Form1()
{
workerDone.WaitOne();
}
void backgroundWorker1_RunWorkerCompleted( Object sender, RunWorkerCompletedEventArgs e )
{
workerDone.Set();
}

Firstly, the ObjectDisposedException is only one possible pitfall here. Running the OP's code has produced the following InvalidOperationException on a substantial number of occasions:
Invoke or BeginInvoke cannot be called
on a control until the window handle
has been created.
I suppose this could be amended by starting the worker on the 'Loaded' callback rather than the constructor, but this entire ordeal can be avoided altogether if BackgroundWorker's Progress reporting mechanism is used. The following works well:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending)
{
this.bgWorker.ReportProgress(Environment.TickCount);
Thread.Sleep(1);
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.ProgressPercentage.ToString();
}
I kind of hijacked the percentage parameter but one can use the other overload to pass any parameter.
It is interesting to note that removing the above sleep call clogs the UI, consumes high CPU and continually increases the memory use. I guess it has something to do with the message queue of the GUI being overloaded. However, with the sleep call intact, the CPU usage is virtually 0 and the memory usage seems fine, too. To be prudent, perhaps a higher value than 1 ms should be used? An expert opinion here would be appreciated... Update: It appears that as long as the update isn't too frequent, it should be OK: Link
In any case, I can't foresee a scenario where the updating of the GUI has to be in intervals shorter than a couple of milliseconds (at least, in scenarios where a human is watching the GUI), so I think most of the time progress reporting would be the right choice

I really dont see why DoEvents is regarded as such a bad choice in this case if you are using this.enabled = false. I think it would make it quite neat.
protected override void OnFormClosing(FormClosingEventArgs e) {
this.Enabled = false; // or this.Hide()
e.Cancel = true;
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) {
Application.DoEvents();
}
e.cancel = false;
base.OnFormClosing(e);
}

Your backgroundworker should not use Invoke to update the textbox. It should ask the UI thread nicely to update the textbox using event ProgressChanged with the value to put in the textbox attached.
During event Closed (or maybe event Closing), the UI thread remembers that the form is closed before it cancels the backgroundworker.
Upon receiving the progressChanged the UI thread checks if the form is closed and only if not, it updates the textbox.

This won't work for everyone, but if you are doing something in a BackgroundWorker periodically, like every second or every 10 seconds, (perhaps polling a server) this seems to work well to stop the process in an orderly manner and without error messages (at least so far) and is easy to follow;
public void StopPoll()
{
MyBackgroundWorker.CancelAsync(); //Cancel background worker
AutoResetEvent1.Set(); //Release delay so cancellation occurs soon
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (!MyBackgroundWorker.CancellationPending)
{
//Do some background stuff
MyBackgroundWorker.ReportProgress(0, (object)SomeData);
AutoResetEvent1.WaitOne(10000);
}
}

I'd pass in the SynchronizationContext associated with the textbox to the BackgroundWorker and use that to perform Updates on the UI thread. Using SynchronizationContext.Post, you can check if the control is disposed or disposing.

What about Me.IsHandleCreated?
Private Sub BwDownload_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BwDownload.RunWorkerCompleted
If Me.IsHandleCreated Then
'Form is still open, so proceed
End If
End Sub

Another way:
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
while (backgroundWorker.IsBusy)
{
Application.DoEvents();
}
}

One solution that works, but too complicated. The idea is to spawn the timer that will keep trying to close the form, and form will refuse to close until said bgWorker is dead.
private void HandleClosingEvent(object sender, CancelEventArgs e) {
if (!this.bgWorker.IsBusy) {
// bgWorker is dead, let Closing event proceed.
e.Cancel = false;
return;
}
if (!this.bgWorker.CancellationPending) {
// it is first call to Closing, cancel the bgWorker.
this.bgWorker.CancelAsync();
this.timer1.Enabled = true;
}
// either this is first attempt to close the form, or bgWorker isn't dead.
e.Cancel = true;
}
private void timer1_Tick(object sender, EventArgs e) {
Trace.WriteLine("Trying to close...");
Close();
}

Related

Confused about backgroundworker not stopping when expected

I have the following code. It is just a form app. On load it will run the bacground worker.
Then I have a button that is supposed to stop the infinite loop in the background worker by setting a flag to true.
I'm logging the out put of the backgroundworker1.IsBusy and it says it is busy but according to the logic in my code it shouldn't be busy because I set the flag to true thus exiting the while loop and running the backgroundworker_Completed event.
I must be doing something wrong but I can not figure it out.
If I'm approaching this incorrectly could somebody either help me fix what I'm doing wrong or point me in a better direction on how I can accomplish what I"m trying to do here.
private volatile bool StopScanning = false;
private void myForm_Load(object sender, EventArgs e)
{
try
{
if (backgroundWorker1.IsBusy)
{
//do nothing
}
else
{
backgroundWorker1.RunWorkerAsync();
}
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (StopScanning == false)
{
Application.DoEvents();
try
{
ReturnScannedItems();
System.Threading.Thread.Sleep(1000);
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
}
private void cancelbutton_Click(object sender, EventArgs e)
{
try
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
}
catch (Exception boo)
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
Log.log(boo.ToString());
}
while (backgroundWorker1.IsBusy)
{
Log.log("Still busy");
}
this.Close();
}
You are blocking the UI thread, which prevents the BackgroundWorker from completing. It can't raise the RunWorkerCompleted event until the UI thread is free to process new messages (raising the event involves posting a message to the UI thread's message queue, so that the UI thread can then execute the code that will actually raise the event).
Your code also is flawed in that it's calling Application.DoEvents() from the worker thread. You should never call this method anyway, but it's particularly foolish to call it from a worker thread, because the whole point of having a worker thread is to avoid having to call that method (and it won't do anything when called on the worker thread anyway, because the worker thread shouldn't own any window objects that would need to receive a window message).
Instead of sitting in a busy loop, checking IsBusy and blocking the UI thread, you should just subscribe to the RunWorkerCompleted event and do whatever you need to do there. Without a good Minimal, Complete, and Verifiable code example that fully illustrates what you're actually trying to do, it's not possible to provide any more specific advice than that.

correct way to re-call backgroundworker

As in title, I have some bgw I want to call on every button press.
Is this code correct ?
private static BackgroundWorker bgw = null;
private void bttn_Click(...)
{
if(!bgw.IsBusy)
doSomeWorkInBg();
else
MessageBox.Show("Slow down a bit");
}
private void doSomeWorkInBg()
{
if (bgw == null)
{
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
}
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// do some work that takes time
}
Or maybe I should add RunWorkerCompleted event with "bgw = null;" code or something like that?
You should avoid making your bgw static, because you are using it in non-static context.
When I need to re-run the worker frequently based on the UI event, this is the construct that I usually use:
bool ShouldRunWorkedASAP;
private void bttn_Click(...){
ShouldRunWorkedASAP=true;
if (!bgw.IsBusy) bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
while (ShouldRunWorkedASAP) {
ShouldRunWorkedASAP=false;
// do some work that takes time
}
}
This assumes that the worked is instantiated in the class constructor. Basically this code sets the flag that the worker should run ASAP whenever the UI event occurred, then tries to run the worker. If it is already running - then the while() loop inside the worker implementation will schedule the job for the next run as soon as it completes.
This code does not ensure that the worker will run exactly the number of times the user presses the button, not sure if in your case this is required or not.

BackgroundWorker reporting ProgressChanged after closing its form

In my winforms project I got some Form, that runs BackgroundWorker. Basically it's a simulation, so it can be solved at some point but it can also be stopped by the user, so when it finishes either way it needs to redraw the results. I got a problem with the fact, that when I close main window its reference becames null, but BW can still fire ReportProgress, which ends up with an exception.
My BackgroundWorker (simplified code):
void backgroundWorkerRunSimulation_DoWork(object sender, DoWorkEventArgs e)
{
bool ShouldContinue = true;
do
{
SomeHeavyComputing();
if(CheckIfSimulationDone() | backgroundWorkerRunSimulation.CancellationPending)
ShouldContinue=false;
//Redrawing results on the interface
if (AlwaysRefresh | !ShouldContinue)
UpdateGUI(); //<--Method invoked on form's thread
//Reporting progress
backgroundWorkerRunSimulation.ReportProgress(0, SomeReportingObject);
}
while(ShouldContinue);
}
And ReportProgress:
private void backgroundWorkerRunSimulation_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateStatusBar((GUIReportInfo)e.UserState);//<-also invoked on form's thread
}
Where
void UpdateStatusBar(GUIReportInfo ReportInfo)
{
if (this.InvokeRequired)
{
ReportInfoDelegate updateStatusBar = new ReportInfoDelegate(UpdateStatusBar);
this.Invoke(updateStatusBar, new object[] { ReportInfo });
}
else
//Some assignments on form - crashes here, null reference exc
}
Obviously UpdateStatusBar shouldn't be fired up, and I'm suprised that UpdateGUI (which is analogic) doesn't crash. But is there any way to check whether the form is still there?
Adding:
this.Invoke(new MethodInvoker(delegate { this.Close(); }));
to FormClosing event seems to solve the problem, though I don't understand why would that work. Isn't Close() method fired up on the form's thread anyway?
I'd be glad for any help.
I'm guessing that the added invoke just accidentally changes the order of events, so it won't crash. The elegant solution would be to implement FormClosing and abort the BW there, and wait for it till it finishes. Also, in the BW code you should check for "CancellationPending". More information here:
http://msdn.microsoft.com/en-us/library/hybbz6ke.aspx
EDIT:
The top-voted answer from here demonstrates the solution:
How to stop BackgroundWorker on Form's Closing event?
I've managed to solve my problem by just waiting for worker with a timer.
void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (backgroundWorkerSimulationRunMany.IsBusy)
{
backgroundWorkerSimulationRunMany.CancelAsync();
e.Cancel = true;
timerDelayQuit.Start();
}
}
private void timerQuitDelay_Tick(object sender, EventArgs e)
{
timerDelayQuit.Stop();
this.Close();
}

Call Method From BackgroundWorker

I hate that my first question seems to have been answered many times, but I'm still having a tough time getting my head around how to call a method using BackgroundWorker.
I'm processing a very large text file using a series of classes and methods. The entire process is kicked off after the user selects a tool strip item. Sequentially, it goes like this:
User selects the tool strip item
User selects a file to be processed via a dialog box
The action starts
I think I can wrap everything into BackgroundWorker from the moment the user pops the initial dialog box, but what I'd like to do for now is just put the method where all the heavy lifting is done into its own instance of BackGroundWorker. I'll add a ProgressBar, too, but I think I can handle that if I can just get the BackgroundWorker process rolling.
From the top (pseudocode used for example purposes. Much omitted for brevity):
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
processFile(fileName);
}
static public void processFile(string fileName)
{
// many vars/loops exist but not shown
foreach (data in bigData)
{
processItem(stringA, stringB); // <-- this method is where the expensive work is done
x++;
}
}
I've created an instance of BackgroundWorker...:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Things go here
}
...and I've tried too many things to list, so I've gone back to the beginning for the presentation above.
If I'm understanding BackgroundWorker, I'll need to do the following:
Replace processItem(stringA, stringB) in the above code with something like:
backgroundWorker1.RunWorkerAsync(processItem(stringA, stringB));
...and then do some type of DoWork call?
...and then do some type of RunWorkerCompleted call?
Not sure why my brain is freezing, but I'm embarrassed at the amount of time I've spent on this with no result. Any help would be greatly appreciated. Without StackOverflow, I would have been DOA a long time ago.
FYI: I've referenced other SO posts, MSDN, and DotNetPerls examples. I'm just missing something conceptually, I suppose.
Replace processItem(stringA, stringB) in the above code with something like...
No, that's how you got in trouble. You most definitely want to move the processFile() call to the worker. There is no perceivable benefit from running processItem() in a worker, at least not in the snippet you posted. And doing so is difficult, it would require starting more than one worker. One for each item. Having a lot of workers that each do little work is not very healthy. If it is really necessary then you don't want to use BackgroundWorker, you'll want an entirely different approach with several Threads that consume packets of work from a thread-safe queue. Don't go there if you can avoid it.
The only non-trivial problem to solve is passing the string that processFile() needs. Luckily BackgroundWorker.RunWorkerAsync() has an overload that takes a single object. Pass your string. Obtain its value in your DoWork event handler, casting e.Argument back to a string. Thus:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
string path = (string)e.Argument;
processFile(path);
}
private void processToolStripMenuItem_Click(object sender, EventArgs e) {
backgroundWorker1.RunWorkerAsync(openSingleFile.FileName);
processToolStripMenuItem.Enabled = false;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
processToolStripMenuItem.Enabled = true;
}
Starting up a new background worker is an expensive operation. You don't want to be starting one for each iteration of a loop. Instead, put the entire loop inside of a single background worker's scope.
When ToolStripMenuItem_Click is run create your background worker, have processFile be what is done in the DoWork event handler.
Make sure that when doing that work you're really just doing that work, not updating the UI. You'll want to separate business logic from the user interface. If you want to update the UI with some current progress then call ReportProgress and ensure that there is an event handler to properly update the UI.
If you need to update the UI when the work is all done then you can do so in the RunWorkerCompleted event handler. If the work you are doing generates some result that is used to update the UI use the Result property of the background worker to pass it from the DoWork method to the completed handler.
BackgroundWorker bgw;
In the Load event or constructor:
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
//bgw.WorkerSupportsCancellation = true;
bgw.DoWork += bgw_DoWork;
bgw.ProgressChanged += bgw_ProgressChanged;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
/
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
bgw.RunWorkerAsync(fileName);
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
string fileName = (string)e.Argument;
processFile(fileName);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int Progress = e.ProgressPercentage;
//Update progressbar here
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Job completed
}

CrossThreading issue with BackgroundWorker and statusstrip update

I have been working on a tool that uses a BackgroundWorker to perform a ping operation on a regular interval. I am running into an issue with the BackgroundWorker ProgressChanged event. The code for the ProgressChanged Event is below:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressUpdated update = (ProgressUpdated)e.UserState;
if (sender.ToString() == "System.ComponentModel.BackgroundWorker")
{
toolStripStatusLabel1.Text = update.GeneralStatus;
toolStripProgressBar1.Value = update.ProgressStatus;
toolStripStatusLabel2.Text = update.SpecificStatus;
}
else
{
toolStripStatusLabel1.Text = update.GeneralStatus;
toolStripProgressBar2.Value = update.ProgressStatus;
toolStripStatusLabel3.Text = update.SpecificStatus;
}
}
The ProgressChanged event gets called both in the BackgroundWork where it updates the first values and from the pingcompletedcallback event when a ping finishes. I only run into the cross threading issue when the ProgressChanged event runs from the PingCompletedCallback event. It throws the error when it goes to update the second Progress bar.
I can not seem to figure out why its happening for one of the calls but not the other.
Is the PingCompletedCallBack happening on the BackgroundWorker thread and thats why its causing the cross threading issues?
If so how do I raise the event so that it will be processed on the UI thread and not the backgroundworker?
Edit:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// creates ping and sends it async
ProgressUpdated args = new ProgressUpdated(string1, int1, string 2);
worker.ReportProgress(0,args);
// rest of thread for cleanup when cancellation is called
}
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
// handle the ping response
ProgressUpdated update = new ProgressUpdated(string1, int1, string2);
ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update);
backgroundWorker1_ProgressChanged(this, changed);
// handle other types of responses
}
I thought the use of events was to allow the separation of threads. Aka worker thread raises an event that the UI thread is listening for, then the raised event gets processed on the UI thread.
Since my understanding was wrong, would the PingCompletedCallBack have access to the the ReportProgress method of the backgroundworker?
I could then change in PingCompletedCallback:
ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update);
backgroundWorker1_ProgressChanged(this, changed);
to:
backgroundWorker1.ReportProgress(1, update);
or would I need to change it in some other way?
Thanks for anyone's assistance.
Edit 2:
Changed ProgrssChanged event
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressUpdated update = (ProgressUpdated)e.UserState;
toolStripStatusLabel1.Text = update.GeneralStatus;
toolStripProgressBar1.Value = update.ProgressStatus;
toolStripStatusLabel2.Text = update.SpecificStatus;
}
I then created a second update event
private void PingUpdate (object sender, ProgressUpdated e)
{
toolStripStatusLabel1.Text = e.GeneralStatus;
toolStripProgressBar2.Value = e.ProgressStatus;
toolStripStatusLable3.Text = e.SepcificStatus;
}
The only thing I have left is to call the new event from PingCompletedCallback in such a way as it gets executed on the UI Thread. Is this where the Invoke statement would be used or should the Invokes be used in the new event?
The documentation for BackgroundWorker states that you should not be manipulating UI objects through the DoWork method, and that any changes to UI objects should be made through ReportProgress. I haven't looked at reflector, but it's probably performing a hidden "Invoke" for you. Whatever is raising your PingCompleted event is probably executing within the worker thread or some other thread that is not the main thread.
You will see in the threads window of the Visual Studio debugger that DoTask does not execute on the main thread; however, when ReportProgress is called, the handler is executed on the main thread. Since your controls were probably created on the main thread, you do not see the exception.
Now, if you attempt to call backgroundWorker1_ProgressChanged explicitly within the DoWork method, then backgroundWorker1_ProgressedChanged will be executed on the same thread that's executing the DoWork method, or, in your case, the method that's raising the PingCompleted event:
You can probably solve this cross thread exception by adding InvokeRequired checks within your backgroundWorker1_ProgressChanged handler, or route your PingCompleted handler to call ReportProgress
EDIT:
Calling ReportProgress from the PingCompleted handler won't work because you will lose the original sender.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (InvokeRequired)
{
Invoke(new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged), sender, e);
return;
}
// The rest of your code goes here
}
EDIT 2 Response:
private void PingUpdate (object sender, ProgressUpdated e)
{
if (InvokeRequired)
{
Invoke(new Action<object, ProgressUpdated>(PingUpdate), sender, e);
return;
}
toolStripStatusLabel1.Text = e.GeneralStatus;
toolStripProgressBar2.Value = e.ProgressStatus;
toolStripStatusLable3.Text = e.SepcificStatus;
}

Categories