Wait for BackgroundWorker finish, if running, at FormClosing time - c#

How can I wait for BackgroundWorker to finish, if running, when the user request to close application? I'd like to wait to this BackgroundWorker finish then exit application. I tried with AutoResetEvent but a call to WaitOne() at FormClosing time seems to block the entire UI and doesn't fire RunWorkerCompleted event where Set() is called.
How can I accomplish this?
I'm looking for an alternative/proper way for this:
bool done = false;
private void my_backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
resetEvent.Set();
done = true;
}
private void myForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (notify_backgroundWorker.IsBusy)
{
while(!done)
{
Application.DoEvents();
Thread.Sleep(500);
}
//resetEvent.WaitOne();
}
}

No need to make it so complex, just have a class level variable
bool quitRequestedWhileWorkerBusy=false;
If the user tried to close the form, in the form closing event check if the worker isbusy, cancel the event and set quitRequestedWhileWorkerBusy=true
In your worker completed event, if(quitRequestedWhileWorkerBusy) this.Close();

Another way is based on OP's sample code, but simplified:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Stop the background worker thread (if running) to avoid race hazard.
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
// Wait for the background worker thread to actually finish.
while (backgroundWorker1.IsBusy)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
}

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.

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();
}

How to prevent from moving forward until a background task is completed?

I have an external library which has a method which performs a long running task on a background thread. When it's done it fires off a Completed event on the thread that kicked off the method (typically the UI thread). It looks like this:
public class Foo
{
public delegate void CompletedEventHandler(object sender, EventArgs e);
public event CompletedEventHandler Completed;
public void LongRunningTask()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(5000);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Completed != null)
Completed(this, EventArgs.Empty);
}
}
The code that calls this library looks like this:
private void button1_Click(object sender, EventArgs e)
{
Foo b = new Foo();
b.Completed += new Foo.CompletedEventHandler(b_Completed);
b.LongRunningTask();
Debug.WriteLine("It's all done");
}
void b_Completed(object sender, EventArgs e)
{
// do stuff
}
In the button1_Click method, after I call b.LongRunningTask(), the Completed event fires off 5 seconds later on the UI thread, I update the UI and everything is great, since I don't have to deal with marshaling stuff to the proper thread.
However, I now have a need for the process to be synchronous (without changing the external library). In other words, after I kick off .LongRunningTask method, the next meaningful statement in that method should fire after .LongRunningTask has completed.
I've tried doing it with the EventWaitHandle (e.g. doing WaitOne after the call to LongRunningTask and then Resetting it in the Completed event, but that just locks everything up).
Is there a method in the .NET framework that allows me to do this?
I've tried doing it with the EventWaitHandle (e.g. doing WaitOne after the call to LongRunningTask and then Resetting it in the Completed event, but that just locks everything up).
That is exactly what will happen if you make this synchronous, by definition. You can't make it synchronous without blocking the UI thread.
Instead of having "the next meaningful statement in that method" fire after the operation, you'll need to either make it blocking, or have the meaningful statement fire in the callback.

Cancelling Background Tasks

When my C# application closes it sometimes gets caught in the cleanup routine. Specifically, a background worker is not closing. This is basically how I am attempting to close it:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) ; // Gets stuck here.
}
Is there a different way that I should be doing this? I am using Microsoft Visual C# 2008 Express Edition. Thanks.
ADDITIONAL INFORMATION:
The background worker does not appear to be exiting. This is what I have:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
// Do something.
}
}
I've also modified the cleanup code:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
while (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
System.Threading.Thread.Sleep(1000);
}
}
Is there something else that I should be doing?
Some pretty good suggestions, but I don't believe they address the underlying issue: canceling a background task.
Unfortunately, when using BackgroundWorker, termination of your task depends on the task itself. The only way your while loop will terminate, is if your background task checks its Cancel property and returns or breaks from its current process.
Example Base
For example, consider
private readonly BackgroundWorker worker = new BackgroundWorker ();
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
// semantically, you want to perform this task for lifetime of
// application, you may even expect that calling CancelAsync
// will out and out abort this method - that is incorrect.
// CancelAsync will only set DoWorkEventArgs.Cancel property
// to true
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
for ( ; ;)
{
// because we never inspect e.Cancel, we can never leave!
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
while (worker.IsBusy);
}
This is what is happening by default. Now, maybe your task isn't an infinite loop, perhaps it is just a long-running task. Either way, your main thread will block [actually it is spinning, but whatevs] until the task completes, or doesn't as the case may be.
If you have personally written and can modify the task, then you have a few options.
Example Improvement
For instance, this is a better implementation of the above example
private readonly BackgroundWorker worker = new BackgroundWorker ();
// this is used to signal our main Gui thread that background
// task has completed
private readonly AutoResetEvent isWorkerStopped =
new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.RunWorkerCompleted += BackgroundTask_Completed;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
// execute until canceled
for ( ; !e.Cancel;)
{
// keep in mind, this task will *block* main
// thread until cancel flag is checked again,
// so if you are, say crunching SETI numbers
// here for instance, you could still be blocking
// a long time. but long time is better than
// forever ;)
}
}
private void BackgroundTask_Completed (
object sender,
RunWorkerCompletedEventArgs e)
{
// ok, our task has stopped, set signal to 'signaled' state
// we are complete!
isStopped.Set ();
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
isStopped.WaitOne ();
}
While this is better, it's not as good as it could be. If you can be [reasonably] assured your background task will end, this may be "good enough".
However, what we [typically] want, is something like this
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = isStopped.WaitOne (gracePeriod);
if (!isStoppedGracefully)
{
// KILL! KILL! KILL!
}
}
Alas, we cannot. BackgroundWorker does not expose any means of forceful termination. This is because it is an abstraction built on top of some hidden thread management system, one which could potentially destabalize other parts of your application if it were forcefully terminated.
The only means [that I have seen at least] to implement the above is to manage your own threading.
Example Ideal
So, for instance
private Thread worker = null;
// this time, 'Thread' provides all synchronization
// constructs required for main thread to synchronize
// with background task. however, in the interest of
// giving background task a chance to terminate gracefully
// we supply it with this cancel signal
private readonly AutoResetEvent isCanceled = new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker = new Thread (BackgroundTask_HotelCalifornia);
worker.IsBackground = true;
worker.Name = "Some Background Task"; // always handy to name things!
worker.Start ();
}
private void BackgroundTask_HotelCalifornia ()
{
// inspect cancel signal, no wait period
//
// NOTE: so cheating here a bit, this is an instance variable
// but could as easily be supplied via parameterized thread
// start delegate
for ( ; !isCanceled.WaitOne (0);)
{
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
isCanceled.Set ();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = worker.Join (gracePeriod);
if (!isStoppedGracefully)
{
// wipe them out, all of them.
worker.Abort ();
}
}
And that there, is a decent introduction on thread management.
Which is best suited for you? Depends on your application. It is probably best not to rock the boat, and modify your current implementation to ensure that
your background task inspects and respects the Cancel property
your main thread waits for completion, as opposed to polling
It is very important to compare and evaluate the pros and cons of each approach.
If you must control and guarantee termination of someone else's tasks, then writing a thread management system that incorporates the above may be the way to go. However you would lose out on out-of-box features like thread pooling, progress reporting, cross-thread data marshalling [worker does that, no?], and a bunch of other stuff. Not to mention, "rolling your own" is often error prone.
Anyway, hope this helps :)
Kevin Gale is correct in stating that your BackgroundWorker's DoWork handler needs to poll for CancellationPending and return if a cancellation is requested.
That being said, if this is happening when your application is shutting down, you can just ignore it safely, as well. BackgroundWorker uses a ThreadPool thread, which is, by definition, a background thread. Leaving this running will not prevent your application from terminating, and the thread will automatically be torn down when your application shuts down.
In the background worker thread you need to check the BackgroundWorker.CancellationPending flag and exit if it is true.
The CancelAsync() just sets this flag.
Or to put it another way. CancelAsync() doesn't actually cancel anything. It won't abort the thread or cause it to exit. If the worker thread is in a loop and checks the CancellationPending flag periodically it can catch the cancel request and exit.
MSDN has an example here although it doesn't use a loop in the worker routine.
This code is guaranteed to deadlock when the BGW is still running. BGW cannot complete until its RunWorkerCompleted event finished running. RunWorkerCompleted cannot run until the UI thread goes idle and runs the message loop. But the UI thread isn't idle, it is stuck in the while loop.
If you want the BGW thread to complete cleanly, you have to keep your form alive. Check this thread to see how to do that.
Try:
if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();

How to stop BackgroundWorker on Form's Closing event?

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();
}

Categories