Handling the Unexpected

Life is full of surprises. Some are good, some are bad. When it comes to software though, surprises are usually a bad thing.

clip_image001

Case in point: Unhandled Exceptions.
A good programmer uses try-catch clauses to cover dangerous/problematic areas of the code.
A better programmer knows that this might not be enough.
Exceptions, as the name implies, are not expected to occur during normal software operation. But they might happen at any given time virtually anywhere in your code.
So, a better programmer goes ahead and wraps the entry point of the application with a try-catch block.
This covers all of the user code and thus catches all possible exceptions, right? WRONG!

Some exceptions occur outside of user-code or in a different thread context. Two prominent examples are asynchronous operations (operations that run on different threads) and missing DLL files.

So, how do we handle those unhandled exceptions? Well, it's quite easy actually.

In a WinForm application you need to listen on these two events (see examples inside):
Application.ThreadException Event
AppDomain.UnhandledException Event
You must attach the handlers before the call to the Application.Run method.

In WPF applications it's easier, just listen on this single event:
Application.DispatcherUnhandledException Event
You can easily set the handler inside the App.xaml file like we did here (line #6):

    1:  <Application x:Uid="Application_1" x:Class="MyApplication.App"
    2:      xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3:      xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    4:      StartupUri="MainWindow.xaml"
    5:      Startup="Application_Startup"
    6:      DispatcherUnhandledException="App_DispatcherUnhandledException">
    7:      <Application.Resources/>
    8:  </Application>

Also note that in WPF you can actually prevent Windows from displaying that horrible "Stopped Working" dialog by setting the "Handled" flag of the event args.

Any WinForm/WPF application should have these events handled, and handled carefully. Since this event is the "last line of defense" it is important to carefully avoid calling any method that might throw an exception.

Here is an implementation example for WPF. Note the following important facts:
All of the "dangerous" code (lines 9-11) is enclosed in a try-catch clause for safety.
Primitive defaults (lines 4-5) are used as a "plan B" fallback, in case an exception is thrown inside the handler.
Also, in WPF apps, don't forget to set the Handled property of the event (line 19) or you will end up with the default dialog in addition to your own handling.

    1:  private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    2:  {
    3:      // defaults, in case we fail getting the strings from resources.
    4:      string message = "An error has occured and the application needs to be closed. Please contact support.";
    5:      string title = "Application Error";
    6:      // try to log this, and get the localized strings.
    7:      try
    8:      {
    9:          Logger.Fatal("Unhandled exception: " + e.Exception);
   10:          message = ReportViewer.Properties.Resources.UnhandledExceptionMessage;
   11:          title = ReportViewer.Properties.Resources.UnhandledExceptionTitle;
   12:      }
   13:      catch (Exception logEx)
   14:      {
   15:          Trace.WriteLine("Cannot log unhandled exception, the logger failed with the following exception:" + logEx);
   16:      }
   17:      Trace.WriteLine(e.Exception);
   18:      MessageBox.Show(message,title, MessageBoxButton.OK, MessageBoxImage.Error);
   19:      e.Handled = true;
   20:  }

That’s all for this topic. Comments are most welcome.