GOTCHA 6 Exceptions may go unhandled


GOTCHA #6 Exceptions may go unhandled

No one likes an application to crash. It's embarrassing if your application presents the user with an unhandled exception dialog like the one in Figure 1-6.

Figure 1-6. Unhandled exception dialog


There is a low-tech solution: comb through your code to make sure that you are handling all exceptions properly. But that is a lot of work, and what if you miss something? Can you protect your application from the exception that slips through? And if not, do you want the unhandled exception to jump up abruptly in your users' faces? Wouldn't you rather have it presented to them more gracefully, and maybe reported to you by logging, emailing, or some other means?

You can register a method to catch unhandled exceptions. There are two ways to achieve this. In a Windows application, you add a THReadExceptionEventHandler to the Application.ThreadException delegate. In a console application, you add an UnhandledExceptionEventHandler to AppDomain.CurrentDomain.UnhandledException.

Examples 1-8 through Example 1-11 show a console application that uses a class in a library.

Example 1-8. Exception that goes unhandled (C# library)

C# (HandleException), library code

 //Utility.cs part of ALibrary.dll using System; namespace ALibrary {     public class Utility     {         public double Operate(int value1, int value2)         {             // Some operation             // Of course, this is an enormous programming error             // Never do division without making sure the denominator             // is not zero.             // We're just doing it here for the sake of example.             double val = value1 / value2;             return Math.Sqrt(val);         }     } } 

Example 1-9. Exception that goes unhandled (C# client)

C# (HandleException), client code

 //Program.cs part of UnhandledExceptionConsoleApp.exe using System; using ALibrary; using System.Threading; namespace UnhandledExceptionConsoleApp {     class Program     {         private static void Worker()         {             Console.WriteLine(                 "Enter two numbers separated by a return");             int number1 = Convert.ToInt32(Console.ReadLine());             int number2 = Convert.ToInt32(Console.ReadLine());             double result = new Utility().Operate(number1, number2);             Console.WriteLine("Result is {0}", result);         }         [STAThread]         static void Main(string[] args)         {             try             {                 //AppDomain.CurrentDomain.UnhandledException                 //    += new UnhandledExceptionEventHandler(                 //        CurrentDomain_UnhandledException);                 new Thread(new ThreadStart(Worker)).Start();             }             catch(Exception ex)             {                 Console.WriteLine("Exception: " + ex.Message);             }         }         private static void CurrentDomain_UnhandledException(             object sender, UnhandledExceptionEventArgs e)         {             Console.WriteLine("Send the following to support");                 Console.WriteLine("Unexpected error:");             Console.WriteLine(e.ExceptionObject);             Console.WriteLine("Is CLR terminating: {0}",                 e.IsTerminating);         }     } } 

Example 1-10. Exception that goes unhandled (VB.NET library)

VB.NET (HandleException), library code

 'Utility.vb part of ALibrary.dll Public Class Utility     Public Function Operate( _         ByVal value1 As Integer, ByVal value2 As Integer) As Double         'Some operation         ' Of course, this is an enormous programming error         ' Never do division without making sure the denominator         ' is not zero.         ' We're just doing it here for the sake of example.         Dim val As Double = value1 / value2         If Double.IsInfinity(val) Then             Throw New DivideByZeroException( _                 "Attempted to Divide by Zero")         End If         Return Math.Sqrt(val)     End Function End Class 

Example 1-11. Exception that goes unhandled (VB.NET client)

VB.NET (HandleException), client code

 'Program.vb part of UnhandledExceptionConsoleApp.exe Imports ALibrary Imports System.Threading Module Program     Private Sub Worker()             Console.WriteLine( _                 "Enter two numbers separated by a return")         Dim number1 As Integer = Convert.ToInt32(Console.ReadLine())         Dim number2 As Integer = Convert.ToInt32(Console.ReadLine())         Dim result As Double = New Utility().Operate(number1, number2)         Console.WriteLine("Result is {0}", result)     End Sub     Public Sub Main()         Try             'AddHandler AppDomain.CurrentDomain.UnhandledException, _             '     New UnhandledExceptionEventHandler( _             '    AddressOf CurrentDomain_UnhandledException)             Dim aThread As New Thread(AddressOf Worker)             aThread.Start()         Catch ex As Exception             Console.WriteLine("Exception: " + ex.Message)         End Try     End Sub     Private Sub CurrentDomain_UnhandledException( _         ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)         Console.WriteLine("Send the following to support")         Console.WriteLine("Unexpected error:")         Console.WriteLine(e.ExceptionObject)         Console.WriteLine("Is CLR terminating: {0}", _          e.IsTerminating)     End Sub End Module 

In this example, you have a Utility class with an Operate() method that throws a DivisionByZeroException if its second parameter is zero. The method is invoked from a thread in Program. You don't have a try-catch block within the Worker() method to handle exceptions. When you execute the above code, the output shown in Figure 1-7 is produced.

Figure 1-7. Output from Example 1-8


The exception thrown from the thread is reported as an unhandled exception. If you uncomment the first statement in Main(), thereby registering your own handler for uncaught exceptions, you get the output shown in Figure 1-8.

Figure 1-8. Output from Example 1-8 after registering for the UnhandledException event


By adding your handler to the AppDomain.CurrentDomain.UnhandledException event, you let the CLR know that it should send unhandled exceptions to the subscribed method.

Of course, you should not use this as a substitute for using good try-catch logic where necessary. Furthermore, your code should have finally blocks where actions have to be taken regardless of exceptions.

If your application is a Windows application, you register a handler for the Application.ThreadException event instead. Consider a simple WinForm application with one button named RunButton. The handler for that button's Click event is shown in Example 1-12, along with the Main() method and the exception-handler code.

Example 1-12. Taking care of unhandled exceptions in a WinForm app

C# (HandleException)

         static void Main()         { //            Application.ThreadException //                += new ThreadExceptionEventHandler( //                    Application_ThreadException);             Application.Run(new Form1());         }         private void RunButton_Click(             object sender, System.EventArgs e)         {             MessageBox.Show(                 new Utility().Operate(1, 0).ToString());         }         private static void Application_ThreadException(             object sender,             System.Threading.ThreadExceptionEventArgs e)         {             MessageBox.Show(                 "Send the following to support: " +                 e.Exception);         } 

VB.NET (HandleException)

     Public Shared Sub Main()         'AddHandler Application.ThreadException, _         '    New ThreadExceptionEventHandler( _         '    AddressOf Application_ThreadException)         Application.Run(New Form1)     End Sub     Private Sub RunButton_Click( _         ByVal sender As System.Object, _         ByVal e As System.EventArgs) _         Handles RunButton.Click         MessageBox.Show(New Utility().Operate(1, 0).ToString())     End Sub     Private Shared Sub Application_ThreadException( _     ByVal sender As Object, _     ByVal e As System.Threading.ThreadExceptionEventArgs)         MessageBox.Show( _          "Send the following to support: " & _          e.Exception.ToString())     End Sub 

When you click the Run button, you get the output in Figure 1-6. If you uncomment the Application.ThreadException registration code in Main(), you see something more like Figure 1-9.

From the event handler, you may take an appropriate action such as logging the exception for future diagnostics or sending the details to your support team. This can prove useful in development, testing, and even after deployment.

Unlike this example, you shouldn't let your application keep running after an unhandled exception, because it's in an inconsistent state.


Refer to Jason Clark's article "Unexpected Errors in Managed Applications" (see the section "On the Web" in the Appendix).

Figure 1-9. Handling the Unhandled Exception


IN A NUTSHELL

Properly handle exceptions in your application. You can guard against unhandled exceptions by registering a handler for them. In a Windows application, you add a ThreadExceptionEventHandler to the Application.ThreadException delegate. In a console application, you add an UnhandledExceptionEventHandler to AppDomain.CurrentDomain.UnhandledException.

SEE ALSO

Gotcha #61, "Exceptions thrown from threads in the pool are lost."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net