I have experienced an odd behavior of the BackgroundWorker
or the .NET framework when rethrowing an Exception in the RunWorkerCompleted
event.
Exceptions occuring in the background thread are passed to RunWorkerCompleted
. There I do a object temp = e.Result
to rethrow the exception. Since this is supposed to happen in the main UI thread, I expect the exception to be propagated up to Application.Run(...)
, which I surrounded with a try-catch block.
When running the application in Visual Studio, this works fine. However, when I execute the exe file outside Visual Studio, the Exception is not handled and makes the application crash.
Could it be that the RunWorkerCompleted
event is not executed in the main UI thread?
These posts are not duplicates but rather confirm that my situation is weird:
- C# BackgroundWorker RunWorkerCompleted Event
- Events raised by BackgroundWorker not executed on expected thread
This is an extremly simplified code example that reproduces the bug: Create a WinForms project and add:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
Form form = new Form();
form.Load += delegate
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, e) =>
{
// do nothing
};
bw.RunWorkerCompleted += (sender, e) =>
{
throw new Exception("Booo!");
};
bw.RunWorkerAsync();
};
Application.Run(form);
}
catch (Exception ex)
{
while (ex is TargetInvocationException && ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Then try:
- Run it in Visual Studio. You'll get a friendly message box before the application quits.
- Run the compiled executable outside Visual Studio. You'll get a .NET framework "unhandled exception" message.
The interesting part is that if I hit "continue" in 2, the application persists, with the Form being displayed and taking user input. This must mean that instead of the main UI thread, a background thread crashed. The main thread remains alive.
(Why do I want to do all these things? I display a splash screen, and while some application startup work is done in background, the splash screen displays the progress by means of a label and a progress bar. So I have to create and display the splash screen and then grab the main UI thread to start the BackgroundWorker
. In the original code it reports progress back to the main thread which updates the splash form. If during startup an exception occurs, I want to catch it. In certain cases they are "business exceptions" which have a specific meaning, e.g. "you are not authorized to use this application". In these cases I display a friendly message box before I let the application die. Further I have a finally
block to clean up resources. I'm not sure whether the .NET framework "unhandled exception" dialog executes the finally block before killing the app.)