(Print this page)

Global Error handling in a .Net application
Published date: Thursday, March 28, 2013
On: Moer and Éric Moreau's web site

In the last few weeks, I reviewed an application written by somebody else. One of the first thing I was told was that the application was not stable at all. It was crashing many times a day without understanding why. The users were really not happy of that behavior.

Because the app was not mine, I just didn’t want to add error handlers everywhere. I was looking for a more global way of at least give a something to user so they can at least report a specific error message to me with as much details as possible.

This article will show you how to a global error handler for any unhandled errors of your application. You should never rely only on this error handler for all the features of your application. It should only be used for unexpected errors.

This month demo code

This month demo code is provided in both VB and C#. The solution has been created with Visual Studio 2012 but I am pretty sure you can reuse everything here starting with the .Net Framework 2.0 (VS 2005).

Exceptions basis

This article won’t dig the Exceptions topic as it has already been covered in my July 2008 article.

Creating the project

The project I created is a plain Windows Forms application.

If you are programming in VB, the first thing to do is to create the startup class (C# already has the Program.cs class with its Main metod). So I have added a regular class (I named mine cStartup) to my project and changed it to inherit from the WindowsFormsApplicationBase class like this:

Public Class cStartup
    Inherits Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase

We now have to make sure that the application will start with this class in order to be able to support a global error handler. Open the project properties and make sure that the “Enable application framework” is unchecked. Still in the properties, change the “Startup object” to your new class name.

Figure 1: Changing the startup object

Now that we have this setup, we are ready to add code to this class to show the default form of our application (mine is still the default Form1). This is the code we need to add to the cStartup class:

Public Shared Sub Main()
    Dim prog As New cStartup
    prog.MainForm = New Form1()
    Dim para As String() = New String() {Application.ExecutablePath}
    prog.Run(para)
End Sub

Finally, we need to add code to the cStartup class to handle unhandled exceptions. To attach your own code to the UnhandledException event, you can use the Startup event of the class (because we have inherited from WindowsFormsApplicationBase). This is the required code:

Private Sub cStartup_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
    AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf AppDomain_UnhandledException
End Sub

Finally, we need to write the method to call when this event will be triggered. I have added a simple form with a big textbox to show the error. You can do whatever you do in this form. For example, you could log the error in a database, log in a text file, send an email … Your imagination is the limit but remember that if this form is visible, something unexpected happened. The database might not be accessible, the network connection might have been lost, … This form as to be very simple to start with and may offer some more feature (like an email to your support team) if the state of the application allows it. This is the required code to show this form:

Private Sub AppDomain_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    'Display the output form
    Dim f As New fError(DirectCast(e.ExceptionObject, Exception))
    f.ShowDialog()

    'Exit the application - Or try to recover from the exception:
    Environment.Exit(0)
End Sub

Figure 2: The exception form

Testing the error handling

We are now ready to add some code to test if our error handler works as expected.

On my default form (Form1), I have added 2 buttons.

 

Figure 3: The test form

The first one is named btnLocalHandling and I use it to confirm that if I have some error handling in that event, the global one won’t be called. This is the code I use to test it:

Try
    Throw New Exception("Exception triggered from btnLocalHandling")
Catch ex As Exception
    MessageBox.Show("Error was trapped: " + ex.Message)
End Try

Because I have a Try … Catch, the global error handler will never be called unless you re-throw the exception in the Catch block (see http://emoreau.com/Entries/Articles/2008/07/Exceptions.aspx).

Now the second button (named btnGlobalHandling) has only one line of code to simulate an error:

Throw New Exception("Exception triggered from btnGlobalHandling")

Because no Try…Catch is handling the error at this level (and this is my top level), the global error handler from the cStartup class should trapped it.

Conclusion

Adding this global error handler does not make your application more robust. Your users will appreciate to see your error handler form instead of the generic one. They will feel that you are more in control of the situation (but you and I know that you are not really!).

A couple of months ago, I was sitting in a room where Brian Laguna from Infragistics was doing a wonderful presentation and at some point he said: “I don’t write bugs, they just show up”. This is exactly why you need to use a global error handling trap.


(Print this page)