UI Exception Handling vs. AppDomain Exceptions

I'm building an uber-exception handling system for all of our apps at work (basically handle unexpected exceptions and post them to out bug tracker, JIRA) and wanted to clear up some confusion on the differences between unhandled exceptions. As an FYI, this information is just for WinForm apps.

By default if you create a new WinForm app any unhandled exceptions are tossed into a dialog box like this:

image

Perhaps while you're debugging you've seen this:

image

That's the built-in exception assistant Visual Studio provides. It kicks in when running your app from inside the IDE and lets you inspect your system. At this point you're basically screwed and something terrible has happened, so this is your last chance to maybe see what went wrong.

The exception assistant is useful as you can edit your code on the fly, crack open the exception (and investigate other values), or just continue along your merry way. If the exception assistant really irks you, you can go into the Debugger options for visual studio and disable it. When you do this, you'll get a dialog that looks like this:

image

Not as descriptive as the exception assistant, but more intuitive if you just want to motor along (say to your own handler which is what we'll do).

Let's setup an unhandled exception catcher. Here's our main code before we add the handler:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

Now my kung-fu design skillz kick in and we'll build a highly sophisticated UI to drive our exception handler. Behold the mighty user interface to end all user interfaces:

image

To create a handler we create a method and attach it to the ThreadException event handler. In our app we'll throw some exception and let the system handle it. Here's the updated code:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.ThreadException += Application_ThreadException;
    Application.Run(new Form1());
}

static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
    MessageBox.Show("Something terrible has happened.");
}

We've tied into the ThreadException handler on the Application class with our own method that will dump the exception to a simple dialog box:

image

However in the AppDomain class there's an UnhandledException handler that you can tie into, just like we did with the ThreadException on the Application class above. The ThreadException is for dealing with exceptions thrown on that thread (and in this case, its our main form) but the AppDomain handler is for *any* unhandled exception thrown (for example, a SOAP call to a web service). So we should hook into that one as well like so:

[STAThread]
static void Main()
{
    ...
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    ...
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    MessageBox.Show("Something else has happened.");
}

You might also notice the signature is different. Rather then getting a ThreadExceptionEventArgs object we get an UnhandledExceptionEventArgs one. The differences are subtle:

  • ThreadExceptionEventArgs contains a property of type Exception that is the exception that was thrown
  • UnhandledExceptionEventArgs contains a property called ExceptionObject which is the exception thrown (except that it's of type object rather than an Exception)
  • UnhandledExceptionEventArgs also contains a bool property called IsTerminating which tells you if the CLR is about to shut down

The question is, does that make our ThreadException handler obsolete? Not really but there's different behaviour around the exception handler based on another setting. In the Application class there's a method called SetUnhandledExceptionMode which lets you control how thread exceptions are handled:

[STAThread]
static void Main()
{
    ...
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
    ...
}

Adding this to your setup can result in two different behaviours:

  1. If you set it to ThrowException, then your CurrentDomain_UnhandledException method is called (even before the Visual Studio IDE gets ahold of it) and the Application_ThreadException method is never called.
  2. If you set it to CatchException, then your Application_ThreadException method is called. If you're running inside Visual Studio, the IDE steps in before this with it's own exception handler first then control is passed onto your method if you continue with execution.

One little extra note, if you choose option #1 and call SetUnhandledExceptionMode to ThrowException your AppDomain handler gets called but running outside the IDE you'll get this lovely dialog box:

image

Your app generally shouldn't report information to Microsoft (unless you're really important), but this is what happens when running your app in normal user mode rather than developer mode. I'm not sure if there's a way to prevent having this bad boy popup on you when you hook into the AppDomain event handler (feel free to chime in on the blogs comments if you know how).

Hope that clears up a little on how exceptions work and the different behaviours you can get out of them. Happy exception handling!

4 Comments

  • Look up the usage of the legacyUnhandledExceptionPolicy option in your application's XML configuration file.

    When you enable legacy handling, the application will not terminate when unhandled exceptions occur. This allows your UnhandledException handler on the AppDomain to do whatever you like, including logging the exception and carrying on with execution.

    I prefer this method because it allows the application to give the user a chance to save data instead of just dropping all of the cards on the floor...

  • Great article Bil,
    Funny how this is a topic my team is also looking at.
    I am curious if you or anyone else has a need for non-exception error handling?

    We often have batch jobs completing tasks and audits where a validation error may hit, but we don't want to throw an exception.
    We still want the application / batch to continuing running, but log any validation errors.

    We are looking at creating delegates for errors that require user intervention on UI based application.
    On batch jobs, we are looking at creating a dictionary of validation error types.
    After the job completes, read the dictionary and process validation handling / logging on each type in the dictionary (each type may have different logging methods).

    Any comments would be appreciated.
    Gary

  • Bil, Good stuff, Nice post.

  • You can prevent an application from reporting to Microsoft by using the WerAddExcludedApplication function.

    As Daniel says, though, you can sign up for winqual.microsoft.com and get at your application's errors anyway.

Comments have been disabled for this content.