• Managing 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 catchingSystem.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 catchSystem.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.

     Collapse
    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.

     Collapse
    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.

     Collapse
    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.

     Collapse
    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 anAbortIgnoreRetry 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

     Collapse
    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

     Collapse
    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:

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

    And the event handler looks like this.

     Collapse
    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:

     Collapse
    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());
    }

    Quote:http://www.codeproject.com/KB/dotnet/unhandledexceptions.aspx


    作者:Angelo Lee
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    解决计算机改名无法连接TFS的问题
    MysqlHelper使用反射机制智能推算数据类型以及属性名称
    Cakephp中使用JavaScriptHelper来引入js文件
    CakePHP程序员必须知道的21条技巧
    cakephp文件结构
    去掉字符串前后所有空格
    小程序 支持html富文本吗
    2018年五月博客整理
    Angular cookies
    webstorm 快捷键
  • 原文地址:https://www.cnblogs.com/yefengmeander/p/2887691.html
Copyright © 2020-2023  润新知