Thursday, September 23, 2010

Exception handling in .Net

Overview

In Win32 API as well as COM, whenever an exceptional condition occurred during the execution of code, there was no explicit notification send to the caller to notify about the problem. Instead it was left to the caller to check if the call made was successful or not e.g. most Win 32 API functions return FALSE to indicate that something is wrong and then the caller needs to call GetLastError to find the details of the problem. And similarly in COM, if the first bit of HRESULT is 1 then the remaining bits would give you details of the violation. Thus it is up to the caller to make explicit checks to find out any violations and if the caller forgets to do so, the state of the application could be in-deterministic down the line.

.Net has changed the scenario around this. If any assumption is violated in a method, it throws an appropriate Exception which needs to be caught either in the calling method OR somewhere in the call stack. Else if there is no handler for the Exception, the CLR terminates the application rather than leaving it as it is with unpredictable results down the line.

Define Exception

How do you define Exception? Most common answer would be that Exception is an error or response to an error. That means if we are calling a method and the method throws an Exception, something went wrong in the execution of the method. To exemplify, let us say we have a method

Public Int Divide (int a, int b)
{
Return a/b;
}

Now if the caller of this method passed the value Zero or Null for b, the method would throw a DividebyZeroException (). Is this error due to some execution fault by the method OR incorrect value passed to it. Likewise CLR too throws some Exceptions like OutofMemoryException; StackOverflowException; etc which are not due to errors in code.

Now how would you define Exception? I found a definition by Jeffrey Richter to be very appealing. "Exception is defined as non conformance to an assumption implied by the programmatic interface". In above method, Divide(), it was assuming b to be non-zero and non-null and when the caller violated that assumption, the appropriate exception was thrown back to the caller.

Exception handling in .Net

Exception handling in .Net is done via try...catch and finally blocks of code i.e. place the code that you expect or anticipate an exception in Try block and Catch those Exceptions in Catch block for the necessary action you may wish to take on those exceptions.

Finally block contains code (mostly some sort of clean up code) that is executed irrespective whether the Exception occurs in try block or no.

Public Void SomeMethod()
{

FileStream fs = null;

try {

fs = new FileStream(pathname, FileMode.Open)

……

}

catch (FileLoadException fle) {

//put the code here to handle FileLoadException or any exception derived from FileLoadException

}

catch (FileNotFoundException fnfe) {

//put the code here to handle FileNotFoundException or any exception derived from FileNotFoundException

}

catch (IOException ioe) {

//put the code here to handle IOException or any exception derived from IOException

}

catch (Exception e) {

//put the code here to handle any CLS compliant Exception.

}

catch {

//put the code here to handle any Exception, whether CLS compliant or not.

}

finally () {….

…..

}

}

The above shows a method which has a try block followed by few Catch blocks and one Finally block. Any Exception that originates in the Try block will be matched with the Catch blocks for the matching Exception or any of derivatives of the stated Exception. If not found, it goes to the next catch block and so on. After all the catch blocks are compared, if none caters to the Exception thrown then the Exception is passed to the next method up the call stack and the same procedure is repeated.

If any of the Catch blocks of the methods in the call stack can handle the thrown Exception, then all the Finally blocks from where the Exception was thrown up till the matching Catch block are executed and then the code in the matching Catch block filter is executed. Then the code in Finally block corresponding to the Catch block which handled the Exception is caught, is executed.

If the code in the Catch block does not throw/rethrow the exception and also no exception occurs in the code in the Finally block, the execution will fall to the code immediately after Finally block (or after Catch block, if there is no Finally block). Also if exception is thrown while executing code in Final block, it is treated as if thrown at end of the final block.

The last catch block does not specify any exception. It is meant for catching any exception, not catered by any of the catch blocks above it, not even catch(Exception e) block– which catches any CLS compliant exception. Generally this catch block is meant for catching any non-cls compliant exception, though there is no way of knowing what the exception was.

To know how exception handling impacts performance of the code, you can use PerfMon.exe or System Monitor ActiveX control that comes with Windows NT 4, Windows 2000, Windows XP, and the Windows .NET Server product family. Various exception related counters get installed when .Net Framework is installed.

Hierarchy of Catch Blocks

Catch blocks with specific exceptions need to be first, followed by the catch blocks with the generic exceptions. The reason for this is because if the catch block catch(Exception E) is the first one, then since every exception is derived from Exception class, this block would be the only one to catch all CLS compliant exceptions and none of the catch blocks below it would be ever executed.

Should all methods have Exception Handling?

Unfortunately it is a common practice to have catch blocks at end of most, if not all, methods.
Not only is this detrimental to performance but is also grossly incorrect and conceives the truth .

Exception handling should be put only in places where:

1. The exception can be handled by the code and efforts can be made to work around the
exception.

2. The exception needs to be wrapped into a more meaningful one and then re-thrown.
Example would be if a type provides facility of finding Phone Numbers to its users. Now if
the Phone numbers are being maintained in Files and if there is a FileException, it would
not be prudent to throw the file exception back to the user, as it may not make sense to
them. Better would be to wrap these exceptions in a more meaningful custom exception
and then re-throw it.

3. If a message is to be displayed to the user on occurrence of the Exception.

Just catching an Exception without any purpose and swallowing it would not only hurt the performance but also leave the application in an unpredictable state, as some assumption/condition failed and nothing has been done about it.

Also note that not all exceptions can be handled by the application. If the CLR finds that it is OutofMemory for its internal purpose, it will display a message on Console and just terminate the application. None of the handlers will be called. Similarly if the StackOverflowException occurs in the internals of CLR, this exception will not be caught by code and none of the Finally blocks will be executed, and the process would be killed. But if the StackOverflowException occurs in the code of the application, this exception can be caught by the application but the code in the Finally blocks will not execute as there is no space to execute the Finally block on the Stack.

StackTrace in Exceptions

The Exception class from which all exception classes inherit has a public read-only property called StackTrace. Accessing this property actually access code in CLR; the property doesn’t simply return a string. If you create a Custom Exception derived from Exception and try to access this property, you will get NULL. When an Exception is thrown CLR records the point where the exception was thrown. When a catch filter accepts the thrown Exception, CLR records where the exception was caught. And now inside the catch filter if exception’s StackTrace property is accessed, the code implementing this property calls into the CLR which using the recorded start and end points of the thrown exception builds a string listing all the methods between the place where the exception was thrown to the place where it was caught.

If an exception is thrown i.e.throw e, the exceptions StackTrace is reset But if the exception is re –thrown i.e. throw e; the StackTrace property is not reset.

The StackTrace property only includes Method Names till the point where the exception is caught by the catch filter; none of the above methods in the call stack from the point where the exception is caught are included in the StackTrace property. To include the methods from the call stack above the catch point, use System.Environment.StackTrace() (static method) and merge the two strings.

Sometimes not all the methods of the call stack appear in the StackTrace property. This is because the JIT compiler may optimize and inline some of the methods to avoid the overhead of calling and returning from a separate method. Many compilers offer a /debug command line switch, which when turned ON makes the compiler embed information in the assembly that tell the JIT compilers not to inline the method so that the stack trace are more complete and useful to the developer debugging the application.

Applying the attribute System.Runtime.CompilerServices.MethodImplAttribute on top of the method forbids the JIT complier from in lining the method for both debug and release builds.

Exception Hierarchy and Custom Exceptions

All CLR compliant exceptions inherit from the class Exceptions. Initially Microsoft was advocating the strategy that all System Exceptions would inherit from the class System.Exception while all application Exception should inherit from ApplicationException. And both these exceptions would inherit from the base class Exception.
But in the course of building the FCL (Framework Class Library) Microsoft violated their own strategy, some reflection-related exception types are derived from ApplicationException instead of SystemException.

Checking against SystemException or ApplicationException may not be very practical. At the same time checking for individual exceptions too may be impractical. This where the Exception hierarchy kicks in and may be more useful. For e.g. ArgumentNullException; ArgumentOutOfRangeException; DuplicateWaitObjectException; all these inherit from ArguementException.

Checking against the ArguementException rather than each of the derived exceptions may be more helpful.

To define a custom Exception you would either need to inherit from Exception base class OR take any of the existing exception classes in the hierarchy and derive the custom exception from it. Whether to choose Exception class or one of the existing classes as base, depends entirely upon the policy and decisions of the Exception design of the application. If you derive a new Exception from ArguementException, then all the places in the code where ArguementException is being caught may need to consider handling this new exception type, unless you have thought and designed/coded with this consideration.

Exception class has 3 constructors, Blank; Accepting a string (which sets a descriptor) ; Accepting a string and an inner exception. In cases where the thrown exception is wrapped by a new or custom exception, the inner exception of this new exception class is set to the original thrown exception.

A custom exception can have its own data fields too besides the one provided by Exception class. But the caveat to watch here is that in case the Exception needs to be Serialized, serialization code for such additional fields in the custom exception classes needs to be coded. To provide serialize facilities to such custom exceptions class, annotate the class with Serialize attribute and also inherit the class from ISerialize interface and implement the methods for Serialization and Deserialization().

Unhandled Exceptions (AppDomain)

Exceptions which are not handled by the method propagate all the way up. These exceptions may be CLS compliant i.e. derived from Exception class or may be non-CLS compliant. These exceptions can be handled by attaching an event to AppDomain as shown below

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionCallbackMethod);

This callback receives a System.UnhandledExceptionEventArgs object which has two public read-only properties: ExceptionObject (System.Object) and IsTerminating (type System.Boolean). Check Exception.IsTerminating to know whether CLR is going to kill the Appdomain.

Normally, for manual threads, pool threads, and the finalizer thread, the CLR swallows any unhandled exceptions and either kills the thread, returns the thread to the pool, or moves on to call the Finalize method of the next object. If an unhandled exception occurs in any of these kinds of threads, the IsTerminating property will be false.

But if an application’s main thread or an unmanaged thread has an unhandled exception, IsTerminating will be true.

There is also Registry Entry whose value influences the handling of these Unhandled Exceptions. The registry entry is
HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\ DbgJITDebugLaunchSetting.

Value 0: Displays a dialog box asking the user whether he would like to debug the process.

Value 1: No dialog box is displayed to the user and CLR fires the AppDomain’s UnhandledException event.

Value 2: No dialog box is displayed to the user and AppDomain’s UnhandledException event doesn’t fire. The CLR just spawns the debugger attaching it to the application.

Unhandled Exception (Winforms)

To handle unhandled exceptions in Winforms define a method that matches the System.Threading.ThreadExceptionEventHandler delegate and register it with the Application type’s static ThreadException event.

Windows Forms deals only with CLS-compliant exceptions; non-CLS-compliant exceptions continue to propagate outside the thread’s message loop and up the call stack. To display or log both CLS-compliant and non-CLS-compliant exceptions, define two callback methods and register one with the Application type’s ThreadException event and register the other with AppDomain type’s UnhandledException event.

Unhandled Exceptions (ASP.Net)

Unhandled exception in ASP.Net can be handled for a particular Web page or for all Webpages.

To register a callback method that will receive notifications for unhandled exceptions for each WebPage, register the callback method using the Error event offered by the System.Web.UI.TemplateControl class; this class is the base class of the System.Web.UI.Page and System.Web.UI.UserControl classes.

To register a callback method that will receive notifications of unhandled exceptions on any page, register the application-wide callback method using the Error event offered by the System.Web.HTTPApplication class in the Global.asax file.

Unhandled Exceptions (WebServices)

ASP.NET catches the exception and throws a new SoapException object (System.Web.Services.Protocols). A SoapException object is serialized into XML, representing a SOAP fault. This SOAP fault XML can be parsed and understood by any machine acting as an XML Web service client. This allows for XML Web service client/server interoperability.

1 comment: