How to handle unhandled exceptions in Windows Forms
- Register a handler for the event
- Handle the exception in this event handler
[STAThread]If you want to reuse the default dialog box, you can use the following code:
static void Main()
{
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new FrmMain());
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show("Unhandled exception: "+e.Exception.ToString());
}
if (SystemInformation.UserInteractive)This is all fine and easy, but what if you have several threads? What if an exception is thrown in a thread other than the main one?
{
using (ThreadExceptionDialog dialog = new ThreadExceptionDialog(exception))
{
if (dialog.ShowDialog() == DialogResult.Cancel)
return;
}
Application.Exit();
Environment.Exit(0);
}
Well, the answer is simple: you won't be notified by the Application.ThreadException event.
Of course there is a solution: you can use the AppDomain.UnhandledException event instead. There is one thing to be aware of though: your event handler will be executed by the thread that threw the exception. This can be a problem if you want to display something or interact with graphical components, because all this kind of actions should be performed in the same thread (the main thread).
Here is a code sample that shows a complete solution that takes care of this:
private delegate void ExceptionDelegate(Exception x);Nota Bene: You'll have to register a handler for the UnhandledException event for each AppDomain.
static private FrmMain _MainForm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
_MainForm = new FrmMain();
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomain_UnhandledException);
Application.Run(_MainForm);
}
private static void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception exception;
exception = e.ExceptionObject as Exception;
if (exception == null)
{
// this is an unmanaged exception, you may want to handle it differently
return;
}
PublishOnMainThread(exception);
}
private static void PublishOnMainThread(Exception exception)
{
if (_MainForm.InvokeRequired)
{
// Invoke executes a delegate on the thread that owns _MainForms's underlying window handle.
_MainForm.Invoke(new ExceptionDelegate(HandleException), new object[] {exception});
}
else
{
HandleException(exception);
}
}
private static void HandleException(Exception exception)
{
if (SystemInformation.UserInteractive)
{
using (ThreadExceptionDialog dialog = new ThreadExceptionDialog(exception))
{
if (dialog.ShowDialog() == DialogResult.Cancel)
return;
}
Application.Exit();
Environment.Exit(0);
}
}
private void ThreadMethod()
{
throw new Exception("From new thread");
}
private void button1_Click(object sender, System.EventArgs e)
{
Thread thread;
thread = new Thread(new ThreadStart(ThreadMethod));
thread.Start();
}
Nota Bene 2: The UnhandledExceptionEventArgs parameter contains a IsTerminating property that indicates whether the common language runtime is terminating. Something to test in order to know what to do with the exception.
Thanks to Pierrick for the help.