Exception not caught when using BackgroundWorker - c#

I have a WPF window with a BackgroundWorker. I get an exception in Send() method here:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
smtpClient.Send(mail);
}
which in turn is invoked in a Click Event for some button like this:
private async void SendClickAsync(object sender, RoutedEventArgs e)
{
using (MessageServiceClient client = new MessageServiceClient())
{
try
{
[...]
worker.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show("Error! Check your sender data!", "!", MessageBoxButton.OK, MessageBoxImage.Error);
[...]
}
}
}
Why does this exception doesn't get handled? When I was doing it not asynchronously (everything was in SendClickAsync() method) the message box popped up nicely.

When you call worker.RunWorkerAsync() method, your main thread continue execution and exits try..catch block. To handle exception use RunWorkerCompleted event handler. RunWorkerCompletedEventArgs arguement has property Error which will contain exception object:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error", "!", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}

The BackgroundWorker is using thread pool threads, so it doesn't want your code to be able to do something weird on the worker thread. That's why the BackgroundWorker swallows your exception that happened during DoWork and lets you know that it happened through RunWorkerCompletedEventArgs.Error.

The reason the exception is not handled is because it is thrown on a different thread.
The call to worker.RunWorkerAsync(); does not block, rather it starts the job on the threadpool and returns control immediately in the main thread - this is the whole point to doing it asynchronously.
But this also means that after the call to worker.RunWorkerAsync() the main thread exits your try-catch block, so the exception doesn't get handled (on your main thread).

Related

Inner Exception from async thread Owerwrites Outer Exception in .Net [duplicate]

I'm seeing some wierd behaviour when throwing exceptions and catching them in the Application.ThreadException event handler.
Basically whats happening in the sample below is that an exception is thrown in the DoWork event handler of a BackgroundWorker. The RunWorkerCompleted event handler rethrows a new exception with the original as the inner exception.
Why does the inner exception show up in the ThreadException event handler and not the acutal exception being thrown? If I do not provide an inner exception in the RunWorkerCompleted event handler, the correct exception will show up.
using System;
using System.Windows.Forms;
using System.ComponentModel;
namespace WierdExceptionApp
{
class WierdExceptionForm : Form
{
BackgroundWorker worker = new BackgroundWorker();
public WierdExceptionForm()
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
throw new Exception("worker_RunWorkerCompleted", e.Error);
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
throw new Exception("worker_DoWork");
}
[STAThread]
static void Main()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new WierdExceptionForm());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message);
}
}
}
The RunWorkerCompleted event is marshaled from the BGW thread to the UI thread by the WF plumbing that makes Control.Invoke() work. Essentially, there's a queue with delegates that is emptied by the message loop. The code that does this, Control.InvokeMarshaledCallbacks(), you'll see it on the call stack, has a catch (Exception) clause to catch unhandled exceptions. That clause calls Application.OnThreadException, passing the value of Exception.GetBaseException().
Well, that explains why you only see the inner exception. Why it is done this way is a bit unclear. Possibly to slice off the stack frames of the code in the UI thread that are otherwise pretty confusing since the real exception came from the background thread.
if (e.Error != null)
{
throw new Exception("worker_RunWorkerCompleted", new Exception("Inner", new Exception("Inner inner")));
}
You get "inner inner" at the end. It seems that this is the behavior of Application_ThreadException method to look at the inner-most exception.

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.

Passing results back to main thread from a cancelled BackgroundWorker thread

How can I cancel a backgroundworker and pass back an error message. I know that you can use DoWorkEventArgs e.Results to pass back results to main thread but e.Results gets overwritten when I cancel the child thread. Example:
private MyProgram_DoWork(object sender, DoWorkEventArgs e)
{
e.Cancel = true;
e.Result = "my error message";
return;
}
private void MyProgram_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
string ErrorMsg = (string)e.Result; //exception happens here
....
}
else
{
// success code
}
}
Is there another way to stop my child thread and send a string back to the main thread?
If your long-running process was canceled, it wouldn't really have a "result", as the process didn't completely finish.
According to the documentation:
Your RunWorkerCompleted event handler should always check the Error and Cancelled properties before accessing the Result property. If an exception was raised or if the operation was canceled, accessing the Result property raises an exception.
I took a peek inside BackgroundWorker. Here's the contents of the Result property:
public object Result
{
get
{
this.RaiseExceptionIfNecessary();
return this.result;
}
}
And the contents of RaiseExceptionIfNecessary():
protected void RaiseExceptionIfNecessary()
{
if (this.Error != null)
throw new TargetInvocationException(SR.GetString("Async_ExceptionOccurred"), this.Error);
if (this.Cancelled)
throw new InvalidOperationException(SR.GetString("Async_OperationCancelled"));
}
So if you cancel the thread, referencing Result will throw an InvalidOperationException. That's just the way it's designed.
I don't know what the "best" way is to pass a string back. I'd say you could define a variable in the same method you run the BackgroundWorker from, and assign a value to it from the DoWork event.
You just have to be very careful that nothing on the UI thread is somehow bound to the variable or you could run into problems. A string should be safe, but don't start adding to a list that's bound to a ComboBox or something.

MessageBox.Show called from backgound worker with the handle of the main UI thread

I have this code:
public void Blah(IWin32Window _this)
{
for (int i = 0; i < item_quantity; i++)
{
try { File.Delete(item[0, i]); }
catch (Exception ex)
{
if (MessageBox.Show(_this, String.Format("Error while accessing {0}\n{1}"
, item[0, i], ex.Message), "Error", MessageBoxButtons.RetryCancel
, MessageBoxIcon.Error) == DialogResult.Retry)
{ i--; }
}
}
}
...and this code in the main UI thread:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
AnotherClass.Blah(this);
}
When I execute this code, I get the unsafe cross-thread exception. What's the safe way to do this operation?
What's the safe way to do this operation?
There is no real safe way to do this. The message box pops out of nowhere, without any direct connection to a command that the user gave. One failure mode is that the user continues working with your UI, clicking the mouse or pressing the space bar. And your message box pops up a millisecond before he clicked the mouse or pressed a key. He'll never see the message.
So something was supposed to be done, it didn't get done and the user is completely unaware of it. Not a good thing. You'll need to doctor your UI so this condition can never occur. Clearly that will require that you do error reporting a different way than by using a temporary message box. Many possible alternatives of course, could be as simple as a Label that reports state. StatusStrip is good for this.
The actual exception is a bogus one. It is triggered by the built-in diagnostics that checks that code uses UI in a thread-safe way. The underlying winapi call is GetParent(), one of the very few user32 Windows functions that can safely be called, and used, from a worker thread. The only legitimate reason I know where using Control.CheckForIllegalCrossThreadCalls to work around the problem is okay. But fix the real problem instead.
I'm not condoning the design, but you can pass in the Form to Blah() and then Invoke() against the referenced form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
button1.Enabled = false;
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
SomeClass AnotherClass = new SomeClass();
AnotherClass.Blah(this);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
MessageBox.Show("Done!");
}
}
public class SomeClass
{
public void Blah(Form frm)
{
int item_quantity = 5;
for (int i = 0; i < item_quantity; i++)
{
try
{
//File.Delete(item[0, i]);
Console.WriteLine("i = " + i.ToString());
throw new Exception("duh");
}
catch (Exception ex)
{
frm.Invoke(new Action(() =>
{
DialogResult result = MessageBox.Show(frm, String.Format("Error while accessing {0}\n{1}", "something", ex.Message), "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
if (result == DialogResult.Retry)
{
i--;
}
}));
}
}
}
}
You are trying to do UI work on a background thread, hence the cross-thread exception. RunWorkerCompletedEventArgs has a property called Error that will hold any exception that gets thrown by the RunWorkerAsync delegate. Set up a handler for RunWorkerCompleted on your BackgroundWorker and check if the Error property has a value. If it does, prompt the MessageBox in the handler because you will be on the UI thread at that point. Call the BackgroundWorker's RunWorkerAsync method again on the DialogResult.Retry scenario.
(You will probably have to tweak your BackgroundWorker and AnotherClass.Blah to take in the value of i to prime your loop condition for that second call to your BackgroundWorker. The DoWorkEventArgs has a property called Argument that you can use to pass in that value.)
You need to execute UI code like this when calling it from another thread:
// must use invoke because the timer event is running on a separate thread
this.Invoke(new Action(() =>
{
MessageBox.Show("Message");
}));

Catch child background threads within calling method in foreground thread

I want to call a method once by a specified interval (e.g. 3 seconds), but I have problem catching exception within the calling method (e.g. Start) thrown by the called method (timer_Elapsed)
Update
public void Start()
{
Timer timer = new Timer(PingPeriod); //System.Timers
try
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
}
catch (Exception ex)
{
//I want to catch exception thrown by timer_Elapsed
}
//Logic here that keeps the method running
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
throw new exception("Catch this exception please");
}
Update:
Can anyone please confirm that the start() will NOT catch exception thrown from timer_Elapsed, or when trying to invoke timer_Elapsed if exception occurred.
Is there a better alternative, which can catch exception from a called method or get notified?
Any idea would be very much appreciated!
This just isn't possible, the Elapsed event handler runs later, well after your Start() method has exited. Furthermore, that event has the nasty habit of swallowing all exceptions without any diagnostic.
You'll need to handle exceptions within the event handler. Certainly not easy to do. Do consider using the System.Threading.Timer class instead. At least your program will terminate when the callback throws an exception.
This is not possible because the exception is thrown on a different execution context (thread) from the one that set up the timer.
You should handle any exceptions in your callback method. If you want your logic separated from your exception handling, simply create a separate method.
private void TimerElapsedCallback(object sender, ElapsedEventArgs e)
{
try
{
this.DoSomething();
}
catch (Exception ex)
{
// handle
}
}
private void DoSomething()
{
// logic goes here and can be agnostic of any exceptions it throws, if desired
}

Categories