ASP.NET Hosting

How to handle unhandled exceptions in Windows Forms

If you've already handled unhandled exceptions in your Windows Forms applications, your probably know the Application.ThreadException event. Thanks to this event, all you have to do is to:
  1. Register a handler for the event
  2. Handle the exception in this event handler
Here is an example:
[STAThread]
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 you want to reuse the default dialog box, you can use the following code:
if (SystemInformation.UserInteractive)
{
  using (ThreadExceptionDialog dialog = new ThreadExceptionDialog(exception))
  {
    if (dialog.ShowDialog() == DialogResult.Cancel)
      return;
  }
  Application.Exit();
  Environment.Exit(0);
}

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?
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);

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: You'll have to register a handler for the UnhandledException event for each AppDomain.
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.

No Comments