Click here to Skip to main content
11,645,087 members (69,042 online)
Click here to Skip to main content

Managing Unhandled Exceptions in .NET

, 26 Sep 2002 397.5K 155
Rate this:
Please Sign up or sign in to vote.
How to manage unhandled exceptions in .NET

Introduction

One of the things that impressed me when I first started learning .NET was its enhanced exception-handling functionality. By this I mean such features as easy access to the type of exception thrown, full stack trace and inner exceptions. This makes it easy to still get full information in those places where you just catch a top-level System.Exception. I find this convenient since, if you don't need to take specific action on a particular type of exception, it is tedious to have successive catch handlers for each type of exception that may be thrown. In any case, even if you do catch specific exceptions you usually also need to catch System.Exception just to cover yourself and prevent program crashes. Thus I find that I end up catching System.Exception all over the place in my code. A typical scenario is that in Windows Forms and ASP.NET Web Forms applications, all of my non-trivial event handlers end up containing try-catch System.Exception blocks.

The trouble is that this does still clutter up the code somewhat and doesn't really seem quite right. Is there a better way?

The better way

A couple of months ago someone posted a message on one of Microsoft's .NET newsgroups asking just this question. In having catch System.Exception all over the place what you're really doing is a "catch-all," i.e., you're trying to catch unhandled exceptions - exceptions that you don't know about. 

One of Microsoft's developers responded to the post and pointed out that we should not use exception blocks to catch unhandled exceptions. Instead, depending on our type of application, Console, Windows or ASP.NET, we should use one of .NET's unhandled exception event handlers. This means that we'll just have one error handler to handle all unhandled exceptions. When such an exception is generated we can provide the user with the option of continuing or aborting the application.

This is documented in the .NET Help but it doesn't really stand out in the various exception handling topics. You can find some discussion for ASP.NET in the article, Exception Management in .NET. In the discussion that follows I describe how to manage unhandled exceptions in Windows Forms applications.

Consider a simple Windows Forms application containing a single form with two buttons and a text box. The discussion is in C# but a full example follows in both C# and Visual Basic .NET.

Sample Image

The Add button just adds my name to the text box. The Remove button clears it. Suppose the Add button throws an exception. Hitherto, I would have done something like this.

private void btnAdd_Click(object sender, System.EventArgs e)
{
  try
  {
    txtName.Text = "Kevin";
    throw new InvalidOperationException("Invalid operation.");
  }
  catch (System.Exception ex)
  {
    DisplayError(ex);
  }
}

(Normally, I would only do this if the Add function were performing some elaborate operation - typically calling other non-trivial routines - but I'm here just illustrating the process.)

But by writing an unhandled exception event handler delegate we can dispense with the try-catch block above. The signature for the delegate looks like this.

public static void Application_ThreadException(
    object sender, ThreadExceptionEventArgs e)
{
  // Handle exception. 
  // The exception object is contained in e.Exception.
}

And this is how we hook it up.

static void Main() 
{
    // Subscribe to thread (unhandled) exception events
    // (Alternatively, could do this in Form_Load)
    Application.ThreadException += new ThreadExceptionEventHandler(
        Application_ThreadException);

     // Load the form
     Application.Run(new Form1());
}

So the Add function can now look like this.

private void btnAdd_Click(object sender, System.EventArgs e)
{
    txtName.Text = "Kevin";
    throw new InvalidOperationException("Invalid operation.");
}

Example

Here is the complete example in C# and Visual Basic .NET (with Windows Form designer generated code omitted). I have also moved the exception-handling event into an assembly-wide class so that it can be accessed by other forms. When an unhandled exception is received an AbortIgnoreRetry dialog is displayed giving a full description of the error. Of course, in a production version we would just inform the user of an application error and log the details.

Sample Image

C# Implementation

using System;
using System.Threading;

public class Form1 : System.Windows.Forms.Form
{
    // ...(omitted)

    /// 
    /// The main entry point for the application.
    /// 
    [STAThread]
    static void Main() 
    {
        // Subscribe to thread (unhandled) exception events
        ThreadExceptionHandler handler = 
            new ThreadExceptionHandler();

        Application.ThreadException += 
            new ThreadExceptionEventHandler(
                handler.Application_ThreadException);

        // Load the form
        Application.Run(new Form1());
    }

    /// 
    /// Adds default name to text box.
    /// 
    private void btnAdd_Click(object sender, EventArgs e)
    {
        
        // Generate handled exception
        //Add(); 
        
        // Generate unhandled exception
        AddWithUnhandledException();
    }

    /// 
    /// Removes name from text box.
    /// 
    private void btnRemove_Click(object sender, EventArgs e)
    {
        txtName.Clear();
    }

    /// 
    /// Adds default name to text box.
    /// Throws handled exception.
    /// 
    private void Add()
    {
        try
        {
            txtName.Text = "Kevin";
            throw new InvalidOperationException(
                "Invalid operation.");
        }
        catch (System.Exception ex)
        {
            DisplayError(ex);
        }
    }

    /// 
    /// Adds default name to text box.
    /// Throws unhandled exception.
    /// 
    private void AddWithUnhandledException()
    {
        txtName.Text = "Kevin";
        throw new InvalidOperationException(
            "Invalid operation.");
    }

    /// 
    /// Displays exception message.
    /// 
    private void DisplayError(Exception ex)
    {
        MessageBox.Show(ex.GetType() + "\n\n" + 
            ex.Message + "\n\n" + 
            ex.StackTrace, 
            "Error", 
            MessageBoxButtons.AbortRetryIgnore, 
            MessageBoxIcon.Stop);
    }

} // End Form1

/// 
/// Handles a thread (unhandled) exception.
/// 
internal class ThreadExceptionHandler
{
    /// 
    /// Handles the thread exception.
    /// 
    public void Application_ThreadException(
        object sender, ThreadExceptionEventArgs e)
    {
        try
        {
            // Exit the program if the user clicks Abort.
            DialogResult result = ShowThreadExceptionDialog(
                e.Exception);

            if (result == DialogResult.Abort) 
                Application.Exit();
        }
        catch
        {
            // Fatal error, terminate program
            try
            {
                MessageBox.Show("Fatal Error", 
                    "Fatal Error",
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Stop);
            }
            finally
            {
                Application.Exit();
            }
        }
    }

    /// 
    /// Creates and displays the error message.
    /// 
    private DialogResult ShowThreadExceptionDialog(Exception ex) 
    {
        string errorMessage= 
            "Unhandled Exception:\n\n" +
            ex.Message + "\n\n" + 
            ex.GetType() + 
            "\n\nStack Trace:\n" + 
            ex.StackTrace;

        return MessageBox.Show(errorMessage, 
            "Application Error", 
            MessageBoxButtons.AbortRetryIgnore, 
            MessageBoxIcon.Stop);
    }
} // End ThreadExceptionHandler

Visual Basic .NET Implementation

Imports System.Threading

Public Class Form1
    Inherits System.Windows.Forms.Form

    Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

        ' Subscribe to thread (unhandled) exception events
        Dim handler As ThreadExceptionHandler = _
            New ThreadExceptionHandler()

        AddHandler Application.ThreadException, _
            AddressOf handler.Application_ThreadException
    End Sub

    '''
    ''' Adds default name to text box.
    '''
    Private Sub btnAdd_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles btnAdd.Click
        
        ' Generate handled exception
        Add()

        ' Generate unhandled exception
        'AddWithUnhandledException()
    End Sub

    '''
    ''' Removes name from text box.
    '''
    Private Sub btnRemove_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles btnRemove.Click
        
        txtName.Clear()
    End Sub

    '''
    ''' Adds default name to text box.
    ''' Throws handled exception.
    '''
    Private Sub Add()
        Try
            txtName.Text = "Kevin"

            Throw New InvalidOperationException( _
                "Invalid operation.")

        Catch ex As System.Exception
            DisplayError(ex)
        End Try
    End Sub

    '''
    ''' Adds default name to text box.
    ''' Throws unhandled exception.
    '''
    Private Sub AddWithUnhandledException()
        txtName.Text = "Kevin"
        Throw New InvalidOperationException( _
            "Invalid operation.")
    End Sub

    '''
    ''' Displays exception message.
    '''
    Private Sub DisplayError(ByVal ex As Exception)
        MessageBox.Show(ex.GetType().ToString() & _
            vbCrLf & vbCrLf & _
            ex.Message & vbCrLf & vbCrLf & _
            ex.StackTrace, _
            "Error", _
            MessageBoxButtons.AbortRetryIgnore, _
            MessageBoxIcon.Stop)
    End Sub
    
End Class ' Form1

'''
''' Handles a thread (unhandled) exception.
'''
Friend Class ThreadExceptionHandler

    '''
    ''' Handles the thread exception.
    '''
    Public Sub Application_ThreadException( _
        ByVal sender As System.Object, _
        ByVal e As ThreadExceptionEventArgs)

        Try
            ' Exit the program if the user clicks Abort.
            Dim result As DialogResult = _
                ShowThreadExceptionDialog(e.Exception)

            If (result = DialogResult.Abort) Then 
                Application.Exit()
            End If
        Catch
            ' Fatal error, terminate program
            Try
                MessageBox.Show("Fatal Error", _
                    "Fatal Error", _
                    MessageBoxButtons.OK, _
                    MessageBoxIcon.Stop)
            Finally
                Application.Exit()
            End Try
        End Try
    End Sub

    '''
    ''' Creates and displays the error message.
    '''
    Private Function ShowThreadExceptionDialog( _
        ByVal ex As Exception) As DialogResult

        Dim errorMessage As String = _
            "Unhandled Exception:" _
            & vbCrLf & vbCrLf & _
            ex.Message & vbCrLf & vbCrLf & _
            ex.GetType().ToString() & vbCrLf & vbCrLf & _
            "Stack Trace:" & vbCrLf & _
            ex.StackTrace

        Return MessageBox.Show(errorMessage, _
            "Application Error", _
            MessageBoxButtons.AbortRetryIgnore, _
            MessageBoxIcon.Stop)
    End Function

End Class ' ThreadExceptionHandler

Console and ASP.NET Applications

For Console applications you should use the System.AppDomain.UnhandledException event. To hook it up you would write:

Thread.GetDomain().UnhandledException += new 
    UnhandledExceptionEventHandler(Application_UnhandledException);

And the event handler looks like this.

public static void Application_UnhandledException(
    object sender, UnhandledExceptionEventArgs e)
{
  // Handle exception. 
  // The exception object is contained in e.ExceptionObject.
}

For ASP.NET applications you use the System.Web.HttpApplication.Error event which is placed in the Global.asax file. This might look something like:

protected void Application_Error(Object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();

    // Stop error from displaying on the client browser
    Context.ClearError();

    Response.Write("Application_Error");
    Response.Write("Error Message: " + ex.ToString());
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Kevin McFarlane
Web Developer
United Kingdom United Kingdom
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralThanks Pin
RockingDownTheHighway9-Feb-11 16:53
memberRockingDownTheHighway9-Feb-11 16:53 
QuestionHelp! I've not been able to handle this error! Pin
mnongkhlaw26-Jun-08 3:13
membermnongkhlaw26-Jun-08 3:13 
GeneralThis is bad practice Pin
Jon Raynor14-Nov-07 6:11
memberJon Raynor14-Nov-07 6:11 
GeneralRe: This is bad practice Pin
Kevin McFarlane15-Nov-07 2:39
memberKevin McFarlane15-Nov-07 2:39 
GeneralRe: This is bad practice Pin
Jon Raynor15-Nov-07 3:57
memberJon Raynor15-Nov-07 3:57 
GeneralRe: This is bad practice Pin
Judah Himango11-Apr-08 9:58
memberJudah Himango11-Apr-08 9:58 
GeneralRe: This is bad practice Pin
Elder Benassi26-Mar-08 13:43
memberElder Benassi26-Mar-08 13:43 
GeneralRe: This is bad practice Pin
NikoTanghe8-Jul-08 5:24
memberNikoTanghe8-Jul-08 5:24 
QuestionWhat about running with the debugger? Pin
barbq20008-Aug-07 0:30
memberbarbq20008-Aug-07 0:30 
AnswerRe: What about running with the debugger? Pin
kckn4fun16-Oct-07 6:18
memberkckn4fun16-Oct-07 6:18 
QuestionHow we do it for .Net 2.0 in windows forms Pin
Rupesh Burad6-Jul-07 1:27
memberRupesh Burad6-Jul-07 1:27 
GeneralAnother Implementation Pin
LongPlay28-Jan-07 21:35
memberLongPlay28-Jan-07 21:35 
GeneralWindows apps - default form exceptions Pin
davej.smith18-Apr-06 5:25
memberdavej.smith18-Apr-06 5:25 
GeneralWhat's the difference between . . . Pin
Dave Midgley9-Feb-06 7:00
memberDave Midgley9-Feb-06 7:00 
GeneralRe: What's the difference between . . . Pin
EFEaglehouse3-May-11 9:26
memberEFEaglehouse3-May-11 9:26 
GeneralMake threads reference Application Error in web app Pin
LarryPiegza2-Oct-05 3:39
memberLarryPiegza2-Oct-05 3:39 
GeneralDoesn't cover exceptions from User Controls in Forms Pin
SubaruWagon25-Apr-05 6:47
memberSubaruWagon25-Apr-05 6:47 
QuestionHow trap unhandled errors from supporting classes Pin
Barry Watts5-Jan-05 0:39
memberBarry Watts5-Jan-05 0:39 
AnswerRe: How trap unhandled errors from supporting classes Pin
Michael Lee Yohe23-Jun-10 18:29
memberMichael Lee Yohe23-Jun-10 18:29 
GeneralDoes not work outside Development Env Pin
krprasad13-Feb-04 11:01
memberkrprasad13-Feb-04 11:01 
GeneralRe: Does not work outside Development Env Pin
Anonymous1-Nov-04 13:32
sussAnonymous1-Nov-04 13:32 
GeneralNo-Touch-Deployment and Security Pin
Rick Kellogg10-Jun-03 7:57
sussRick Kellogg10-Jun-03 7:57 
GeneralRe: No-Touch-Deployment and Security Pin
Heath Stewart28-Nov-03 8:31
editorHeath Stewart28-Nov-03 8:31 
GeneralRe: No-Touch-Deployment and Security Pin
John N9-Feb-04 19:03
memberJohn N9-Feb-04 19:03 
GeneralRe: No-Touch-Deployment and Security Pin
Heath Stewart10-Feb-04 3:19
editorHeath Stewart10-Feb-04 3:19 
GeneralDosn't work anymore Pin
JimCryer20-Mar-03 4:00
memberJimCryer20-Mar-03 4:00 
GeneralRe: Dosn't work anymore Pin
JimCryer20-Mar-03 9:35
memberJimCryer20-Mar-03 9:35 
GeneralRe: Dosn't work anymore Pin
PrashantB3-Dec-03 15:02
memberPrashantB3-Dec-03 15:02 
GeneralRe: Dosn't work anymore Pin
togil15-Jan-04 0:09
membertogil15-Jan-04 0:09 
GeneralRe: Dosn't work anymore Pin
Wizard_0119-Sep-04 11:16
memberWizard_0119-Sep-04 11:16 
GeneralRe: Dosn't work anymore Pin
Anonymous1-Nov-04 13:29
sussAnonymous1-Nov-04 13:29 
GeneralRe: Dosn't work anymore Pin
The_Mega_ZZTer4-Oct-09 18:45
memberThe_Mega_ZZTer4-Oct-09 18:45 
GeneralMaking the class available to other Forms Pin
macrel12-Mar-03 7:45
membermacrel12-Mar-03 7:45 
GeneralRe: Making the class available to other Forms Pin
Jeffry van de Vuurst6-Sep-03 8:27
memberJeffry van de Vuurst6-Sep-03 8:27 
GeneralRe: Making the class available to other Forms Pin
kckn4fun16-Oct-07 6:20
memberkckn4fun16-Oct-07 6:20 
GeneralCrash Reporting Functionality in .NET Windows Forms Applications Pin
Deepak Kumar Vasudevan21-Jan-03 22:37
memberDeepak Kumar Vasudevan21-Jan-03 22:37 
GeneralRe: Crash Reporting Functionality in .NET Windows Forms Applications Pin
Branimir Betov28-Feb-03 6:55
memberBranimir Betov28-Feb-03 6:55 
GeneralException won't show up Pin
Anthony_Yio16-Jan-03 20:33
memberAnthony_Yio16-Jan-03 20:33 
GeneralRe: Exception won't show up Pin
Le_MuLoT4-Oct-05 5:02
memberLe_MuLoT4-Oct-05 5:02 
GeneralFile name and Line numbers don't show ... Pin
JimCryer4-Nov-02 10:27
memberJimCryer4-Nov-02 10:27 
GeneralRe: File name and Line numbers don't show ... Pin
JimCryer4-Nov-02 11:00
memberJimCryer4-Nov-02 11:00 
GeneralThreadExceptionEventArgs undefined Pin
clutch1-Oct-02 1:51
memberclutch1-Oct-02 1:51 
GeneralRe: ThreadExceptionEventArgs undefined Pin
Kevin McFarlane1-Oct-02 5:30
memberKevin McFarlane1-Oct-02 5:30 
GeneralRe: ThreadExceptionEventArgs undefined Pin
clutch2-Oct-02 0:21
memberclutch2-Oct-02 0:21 
GeneralCool Pin
Sijin27-Sep-02 21:03
memberSijin27-Sep-02 21:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150731.1 | Last Updated 27 Sep 2002
Article Copyright 2002 by Kevin McFarlane
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid