BackgroundWorker reporting ProgressChanged after closing its form - c#

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

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.

C# Threading still make UI unresponsive

Well , I have a form1 which has buttons and if you click one of its button
It would load the UserControl into panel in form1
That usercontrol1 contains a lot of data like Database,charts and picture boxes too. So it would definitely make the User Interface unresponsive while loading.
So I read some article and I found out that I need to run it through another thread so I tried it and it just increase the performance by a little bit.
The usercontrol1 still make the GUI unresponsive for about 3-5 sec and what if my data become larger.
I want to make it responsive and show to user that still loading by running the animated picturebox and stop if its finish loading
here is my code:
private void click_dashb_Click(object sender, EventArgs e)
{
ParameterizedThreadStart pts = new ParameterizedThreadStart(load_UserControl);
Thread t = new Thread(pts);
t.Start();
//Animated Picturebox to show user that UI is loading
pictureBox1.Enabled = true;
hover.Location = new Point(42, 130);
}
private void load_UserControl(object state)
{
Invoke(new Action(() =>
{
//load user control through another thread
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
}));
//Stop the animated GIF means the load is finish!
pictureBox1.Enabled = false;
}
If you help me about this problem. I might apply it to all of my works. because most of it contains large data.
Thanks stackoverflow community :)
EDIT:
After reading the comments suggesting to use Background worker . I tried to use it. but still getting a little bit unresponsiveness
Here's the new code:
private void click_dashb_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BeginInvoke((MethodInvoker)delegate
{
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
});
}
catch (Exception x)
{
MessageBox.Show("An error occured while performing operation" + x);
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Operation Cancelled");
}
else
{
MessageBox.Show("Operation Completed");
}
}
It's a little bit better but i still got a little unresponsiveness. Can you check out my code and what's wrong with this.?
thanks again
The problem with your code is that although you run the load_UserControl code in a new thread, that code calls Invoke which effectively makes al the code run on the UI thread again. I can image you did that because accessing Forms and PictureBoxes requires running on the UI thread.
The solution (in general) is to do non-UI work on a seperate thread and then switch back to the UI thread to update the visual controls.
To do this, you can conveniently use the BackgroundWorker class. In the DoWork event handler you do the heavy computation, in the RunWorkerCompleted event handler you update the controls. If you want, you can even update some controls (like a progressbar) during the work by means of the ProgressChanged event handler.
Well, just starting a new thread doesn't make the UI responsive by definition. You need to make the thread so that it actually does stuff in parallel.
Your thread does not, as it basically executes all code in this.Invoke.
That being said: Your code needs to be executed in this.Invoke, as almost everything you do needs to be done on the UI thread.
So in your case, there's really no point in parallelizing stuff, as there's no way to do what you want to do without blocking the UI thread and no technique I know of (Threads, Tasks, BackgroundWorker, etc.) will solve this problem.

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

BackgroundWorker does not fire the RunWorkerCompleted event

I'm creating backgroundworker not in my windows form but in the class file (BusinessLogic) that implements all the processing. From main form I first call the BL method that initializes the BGW. Then I call the method of BL which will start the BGW.
Here is more background :) on my implementation.
How to use BackGroundWorker in class file?
The DoWork event runs fine but it doesnt call the RunWorkerCompleted.
Some googling and I found out this link. I've a feeling that my problem is same as this guys.
http://www.eggheadcafe.com/software/aspnet/29191764/backgroundworker-does-not-fire-the-runworkercompleted-event.aspx
I'd appreciate any input on this issue. Thanks in advance.
Code in Main form:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
BusinessLogic.BGWInitialize();
BusinessLogic.StartBackgroundWorker();
while (!BusinessLogic.firstCycleDone)
{
Thread.Sleep(100);
}
Show();
}
Code in BusinessLogic:
public static void BGWInitialize()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
}
public static void StartBackgroundWorker()
{
bgWorker.RunWorkerAsync();
}
private static void bgWorker_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
firstCycleDone = true;
}
The completed event is Invoked to the main thread. It is supposed to be picked up and executed by the MessagePump.
However, your Wait-and-Sleep code is blocking the message loop.
Hide();
....
while (!BusinessLogic.firstCycleDone)
{
Thread.Sleep(100);
}
Show();
The answer here is that you have no use for a Backgroundworker or another form of threading...
Just call bgWorker_DoWork() directly:
// Hide();
bgWorker_DoWork(); // rename
Show();
if you would just call Application.DoEvents(); instead of Sleep(100); your code would work, but as I said previously BackgroundWorker class is a buggy thing and I would personally use my own threads and reports
Alternatively you could sleep a bit and then call DoEvents

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