When writing any piece of software, at some stage errors occur when the application is run. This is through no fault in the programming of the code, but rather through unexpected actions on the part of the user. Unexpected actions may include selecting a file that does not exist or entering a letter where a number is expected.
These unexpected actions can be handled in one of two ways, either by preemptive programming or by using exceptions. The .NET Framework provides an Exception class in the System namespace to handle exceptions.
Preemptive programming requires the programmer to think about everything that could possibly go wrong when the application is run. Let us consider the first of the two examples cited above. To find out if a file exists, we can invoke the Exists() function of the File class, passing to it the string supplied to us by the user:
if ( !File.Exists (file_name))
{
// The source file does not exist
}
else
{
// The source file exists
}
The if statement catches the error; but, what should we do with it? What about if the if statement was not inverted? This would cause a programming bug that would not be caught until runtime and would cause an exception. When a program error occurs, either the system or the currently executing application reports it by throwing an exception containing information about the error. Once thrown, an exception is handled by the application or by the default exception handler.
Even if you remove all bugs and anticipate all user errors, you will still run into predictable but unpreventable problems, such as running out of memory or attempting to open a file that no longer exists. You cannot prevent exceptions, but you can handle them so that they do not bring down your program.


Benefits of exceptions

Exceptions provide the ability to handle or clean up code in a localized place. Also, they allow clean-up code to execute in case of error. The application becomes easier to write and maintain when clean-up code is in a centralized place.
Also—and maybe more importantly—, exceptions can be used to find bugs within the code; because, the CLR walks up the stack of the thread to get the call history. With this, it is possible to find the location of the failure within the code. Further, it is possible to add additional information within the exception, so that a developer can describe the kind of error more precisely.
The biggest problem with exceptions is performance because of some information overhead. So, they should be used only where preemptive programming is inappropriate. Unfortunately, most developers tend to use exceptions improperly—i.e. catch (Exception e); which will be discussed later—or too seldom, so debugging proves harder.


Using exceptions

Execptions are used within the try-, catch-, and finally-code blocks. The Exception is created and thrown within the try-block.
If the Exeption is thrown, the code within the try-block stops and the finally-block executes.
After this, the first match to a sequence of catch-blocks gets executed, bringing the application into a stable state; because, the code in the try-block didn't finish.
The structure of this looks like the following:
void example()
{
try
{
// Here is where the Exception would be thrown
}
catch (ArgumentOutOfRangeException e)
{
// This block executes if the Exception is of type
// ArgumentOutOfRangeException. Otherwise, the program
// jumps to the next catch-block.
}
catch (InvalidCastException e)
{
// This block executes if the Exception is of type
// InvalidCastException. The Exception is stored
// within the Variable "e". This is needed e.g. to query
// and extend the Error Message of the Exception.
}
catch (Exception e)
{
// Because all Exceptions in .NET derive from Exception,
// this block will handle all .NET Exceptions.
 
// This kind of Exception cannot be handled and must be
// rethrown. Think of an OutOfMemoryException, where
// creating a Window showing the Exception makes it even
// worse.
 
throw;
}
catch
{
// Catches all kinds of exceptions, even when they are not
// CLS-comform, which can appear only when calling
// unmanaged code.
 
// This can not be handled correctly, either.
throw;
}
finally
{
// This code will execute in any case,
// whether an Exception was thrown or not.
}
}
The catch block can handle the Exception in one of three ways:
  • Rethrow the same exception to notify the code which is prior in the call stack.
  • Throw another exception with more precise information. The current Exception can be appended.
  • Handle the exception and continue with the code after the try/catch/finally-block.
Important:
Never handle an Exception of type SystemException, because this is not—or, at least, hardly—possible. Examples are:
  • OutOfMemoryException
    Occurs when the CLR has no free memory available to create new objects on the Heap. Even the garbage collector does not help at this point. The best thing you can do is to terminate the program (thread). If you want to develop a server application, it is best to develop also a watch dog program or to register the program as a Windows service, so that it will restart the program or even the entire server.
  • StackOverflowException
    Similar to the OutOfMemoryException; but this time, there is no space on the Stack. This makes it also impossible to call the finally-block. Therefore the program can be in an undefined state and should be terminated.

  • ExecuteEngineException
    This Exception throws when the CLR detects an error like a corrupted data-structure (e.g. a buffer overflow) or a bug within the CLR itself. Anyway, in that case, the CLR will call a debugger or terminate the whole process without executing any catch- or finally-blocks.

Also, catching such an Exception and checking the type of it afterwards is possible; but, it is not a pretty programming style.

The System.Exception class

The System.Exception class is small class representing information about the kind of Exception. The properties are:
  • Message
    A string representing a human-readable message.

  • Source
    The name of the assembly where the exception was thrown. Not available in the Mobile .Net Framework.

  • StackTrace
    Names of the calling functions as found on the call stack.

  • TargetSite
    The method that throws the Exception originally. Not available in the Mobile .Net Framework.

  • HelpLink
    A link to a resource (e.g. a Web site or a help file) which provides further information. Not available in the Mobile .Net Framework.

  • InnerException
    This field is normally set to null. If not, this indicates that this Exception was thrown because of another Exception which is now stored within this property. Additional to this property, you can also use the GetBaseException() function which returns the innermost Exception.

  • HResult
    This is the HResult which will get returned from a COM object. This property is only needed when interoperating with COM code.

Handling exceptions

As stated before we have three possibilities to handle a Exception:
  • Rethrow
  • Throw new exception
  • Handle the exception
We will look at some examples in the following sections.

Rethrow an Exception

One way to use Exception is to catch an Exception and run exception-specific code. When this is done, the same exception is thrown again and "bubbling up" the call stack.
public String ReadFirstLine (String FileName)
{
try
{
FileStream fs = new FileStream (FileName, FileMode.Open);
}
catch (Exception e)
{
// override the Message of the Exception
e.Message = "Could not open the file.";

// rethrow the same exception
throw;
}

// the file is open now

try
{
String FirstLine = fs.ReadLine();
}
catch (Exception e)
{
e.Message = "Could not read first line of file.";
throw;
}
finally
{
// close the FileStream in each case
fs.Close();
}
return FirstLine;
}
Even if we catch a System.Exception here, it is not a problem, because we can simply rethrow it. Another problem you should respect within catch(Exception e) blocks is that you should avoid creating new objects within the heap (reference types). Remember the OutOfMemoryException example.
Sometimes, it is necessary to set data back to its original state when an exception occurs. In that case, you create a backup of the properties you want to change and restore them in case of a exception.
private Byte[10] ReadNextTenBytes(FileStream fs)
{
Int64 PositionBeforeOutput = fs.Position;
try
{
var Output = new Byte[10];
fs.Read (Output, fs.Position, 10);
}
catch
{
fs.Position = PositionBeforeOutput;
throw;
}
return Output;
}

Throw a new Exception

Sometimes the Exception thrown by a function does not describe the core of the problem well enough. In such a case, you catch the exception and throw a more describing one instead.
public Int32 Inverse(Int32 Number)
{
Int32 Inverse;
try
{
Inverse = 1 / Number;
}
catch (DivideByZeroException e)
{
ArgumentOutOfRangeException ae =
new ArgumentOutOfRangeException (
"Number can't be Zero", e);
throw ae;
}
return Inverse;
}
Notice that when throwing a new exception or when rethrowing the original Exception using code like catch (Exception e){throw e;} properties like StackTrace and Source will get overridden by the information of the current function.
Also, in most cases, you should set the InnerException property (second argument of the constructor) for further reference.

Handle the Exception

In some special cases, it is possible to bring the application into a stable state, even when an Exception has occurred.
public Int32[] ParseNumbers(String[] Numbers)
{
Int32[] ParsedNumbers = new Int32[];
Int32 ParsedNumber;
int elements = 0;
 
foreach (Int32 Number in Numbers)
{
try
{
ParsedNumber = Int32.Parse(Number);
}
catch (FormatException e)
{
continue;
}
catch (OverflowException e)
{
continue;
}
ParsedNumbers[elements++] = ParsedNumber;
}
return ParsedNumbers;
}
This code takes an array of integers which are presented as a String array. The code tries to parse every string within this array one after the other. If this fails, the Int32.Parse() method will probably throw an Exception of type FormatException or OverflowException. This means the string is not parseable. This does not matter in that case; and, we simply repeat with parsing the next String in the array.
Other exceptions will not be handled. They continue bubbling up the call stack.
Notice, that in this example, we do not need access to the Exception; so, we could rewite, e.g. catch (FormatException e) as catch(FormatException).

Catch filter

In C#, the catch block filters for a specific type of Exception and runs the first match. But, the .NET CLR supports also more complex catch filters which you can use from Visual Basic .NET, C++ with managed extensions, or IL.
In VB .Net this might look like:
Catch e as Exception When x = 0
In that example, the catch code executes only if the Exception is of type System.Exception and the variable x is equal to zero. In C#, you need to rewrite this using an additional if block.

Creating and throwing an Exception

The first thing when wanting to create a Exception is to find the best fitting one for your purpose. Therefore you can use the List Exceptions Program to find all the common ones. Then, read the result and select one. If you want to be more specific by providing a more meaningful name or additional properties, derive a new Exception from the next best fitting one.
Note, that in the worst case the best fitting Exception is not System.Exception or System.SystemException, which is a reserved behavior for the .NET Framework internal exceptions from which you should derive, but System.ApplicationException.
Custom exceptions should always be serializeable, so that the exception can also used in an distributed environment like a Web service.
If you decided on one or created an exception, you can create a new exception like any other class. Then you simply throw that Exception using the throw-Keyword.
For example:
ArgumentOutOfRangeException e = 
new ArgumentOutOfRangeException();
e.Message = "Value must be within 1 and 100";
throw e;
or
ArgumentOutOfRangeException e = 
new ArgumentOutOfRangeException
("Value must be within 1 and 100");
throw e;
or
throw new ArgumentOutOfRangeException
("Value must be within 1 and 100");

Performance

As previously stated, exception handling comes with a performance penalty. Using a HRESULT or a similar special return value is more efficient but inflexible when it comes to management, debugging, or reuse of the code.
Exception handling in a managed runtime environment like the .NET or Java runtimes is more efficient that in an unmanaged environment like C++; because, the C++ Compiler has to write bookkeeping code (i.e. to track objects on the heap), whereas in a managed environment much of this bookkeeping code is implicit (i.e. garbage collection). The exact overhead for Exception handling in .NET is hard to predict, because it depends how the compiler on the target platform implements it. Some compilers are using lookup tables, others generate special methods for this task. When it comes to performance tests you should do them on the target platform.
In Microsoft Windows, you can use PerfMon.exe, or the System Monitor ActiveX control. When using PerfMon, you select .Net CLR Exceptions as Performance Object and then select the statistics which you are most interrested in.

Unhandled Exceptions

Unhandled Exception Dialog
Enlarge
Unhandled Exception Dialog
Visual Studio 2005 Exceptions Handling
Enlarge
Visual Studio 2005 Exceptions Handling
Visual Studio 2005 Exceptions Dialog
Enlarge
Visual Studio 2005 Exceptions Dialog
If a exception occurs the CLR traverses up the call stack looking for a matching catch expression. If the CLR doen't finds a matching one, or the Exception gets re thrown each time, the Exception bubbles out of the Main() method. In that case Windows handles the Exception.
First, Windows is searching for a fitting registered debugger like Visual Studio which can handle the Exception. Visual Studio has very powerfully mechanisms, so i.e.. for a debug-compiled version Visual Studio jumps directly to the place within the source where the Exception has occurred. Further Visual Studio 2005 provides mechanisms to look at the current values of all the Variables which provides an immense programming-advance compared to most other debuggers. Release compiled Version of your Program doesn't include the additional debug-information needed to map the code to the source. This leads to faster execution, but on the one hand you have to debug within IL. At best it is possible to find the right function within the source, based on the information within the exception (Exception.StackTrace). Also the mapping of the value of the variables gets lost if they are not stored within the exception too (remember Exception.ParamName and Exception.ActualValue).
The debugger of Microsoft's Visual Studio 2005 offers special exception handling support. The Dialog providing the settings for this can be found in Debug/Exceptions from the Menu bar. A detailed description can be found within the documentation of Visual Studio.


Logging Exceptions

You can log exceptions with System.Diagnostics.EventLog if the user uses Windows 2k/XP/2003/Vista. A very good thing when working with public available versions of your program is to set up a server running a public available EventLog, or - on Unix Systems - a special developed WebService to which you can report. On uncritical Exceptions you can also present the user a special Window providing text-fields to give additional information (E-Mail Address for contact; Information about the user has done with your Application; etc. - but don't ask for things like the used operating system; figure it out by code where possible). At least it is a much smarter approach than providing just a E-Mail Address or a Webforum, as most developers do, because the error-report is more problem oriented and goes directly to the responsible programmer. If you want to do remote logging let the user decide if she really wants to send the report or not.
Important: It is not possible to create a new window when there is ie. a out-of-memory Exception. If you try it you will get a new out-of-memory Exception, trying to create a new window, creating a new Exception.... This can cause a hangup of the operating-system.

Exception policies

It is also possible to register an Event Handler for unhandled exceptions. The code below gives an example, how this might look like. The Example works also within threads, so you don't really have to terminate the whole application which can give you the possibility to recover, make an failure report, and maybe re spawn the thread.
using System;
using System.Diagnostics;
using System.Windows.Forms;
 
class Application
{
static void Main()
{
// Register an Event Handler which
// handles the unhandled Exception
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler
(OnUnhandledExceptionPolicy);
try
{
// Application code comes here
}
finally
{
// Finalization code comes here
}
}

static void OnUnhandledExceptionPolicy
(Object Sender, UnhandledExceptionEventArgs e)
{
String InformationForLogging;
Exception ex = e.ExceptionObject as Exception;

if (ex != null)
{
// The unhandled Exception is CLS compliant
// Extract the Information you want to know
InformationForLogging = ex.ToString();
}
else
{
// The unhandled Exception is not CLS compliant
// You can only handle this Exception as Object
InformationForLogging = String.Format(
"Type: {0}{2}String: {1}",
e.ExceptionObject.GetType(),
e.ExceptionObject.ToString(),
Environment.NewLine)
}

#if DEBUG
if (!e.IsTerminating)
{
// Exception occurred in a thread pool or finalizer thread
// Debugger launches only explicitly
} else
{
// Exception occurred in managed thread
// Debugger will also launch when not launched explicitly
}
// explicitly launch the debugger (Visual Studio)
Debugger.Launch();

#else

// This is a final release;
// logging is done to Syslog and over the Web if possible
// Debugger is mostly not available

if (!e.IsTerminating)
{
// Exception occurred in a thread pool or finalizer thread
// Application keeps open
}
else
{
// Exception occurred in managed thread
// Application is closing, so you should log now
}
#endif
}
}

Controlling CLR unhandled Exception behavior

When a managed thread throws a unhandled Exception, the CLR looks into the registry for the kind of debugging. The Registry Key for this is named DbgJITDebugLaunchSetting and can be found in HKEY_LOCAL_MACHINE\Software\Microsoft\.NetFramework\.
DbgJITDebugLaunchSetting
Value Behavior
0 Displays a dialog asking the user for debugging.
  • If the user chooses to do not, the UnhandledException gets thrown within the AppDomain. If there is no EventHandler for this the CLR displays the Stacktrace.
    In the case of a CUI-Application (Console), the Stacktrace will get shown on the console. A GUI-Application (WinForms) shows the Stacktrace within a new window.
  • If the user chooses to debug, the CLR looks for the Key DbgManagedDebuger with can be found also in HKEY_LOCAL_MACHINE\Software\Microsoft\.NetFramework\ and starts this debugger.
1 No dialog box is presented to the user. The UnhandledException gets thrown immediately.
2 No dialog box is presented to the user. The CLR starts the debugger.
16 in the .NET Framework version 2.0 only, the runtime presents the message box described above for processes that are interactive with the desktop. For processes that are not interactive, the runtime spawns the debugger listed in the DbgManagedDebugger registry key. This is the default setting for the .NET Framework version 2.0. Ref: http://msdn.microsoft.com/en-us/library/2ac5yxx6%28VS.80%29.aspx
Next to the settings for the debugger in the registry it is also possible to enable or disable an JIT-Debugger by setting jitDebugging within the applications .config file.

Unhandled exceptions in different application types

Where and how the output of an unhandled Exception occurs depends on the application type. There are Console Applications (CUI), WinForms Applications (GUI; including DirectX), ASP.Net Applications, and ASP.Net WebServices.

Console

Event Handling of Console Applications is the easiest to understand, because there is no special Handling by the CLR. The Exception is leaving the Applications Thread if not caught. The CLR opens a window asking for debug or exit the application. If the user chooses to debug, the debugger starts. If the user chooses to close, the Application exits and the Exception is serialized and written to the console.
see also: Controlling CLRs Unhandled Exception Behavior

Windows Forms

In a Windows Forms application, the Run-method of the System.Windows.Forms.Application-Class runs a loop within a new thread. This loop dispatches messages of the type System.Windows.Forms.NativeWindow which contains a try/catch block. Within the try-block of this, Microsoft Windows WndProc-Method is called to create the Window.
The catch block of a Window calls the virtual OnThreadException-Method of the window when a CLR-Excception occurs. In the case of a control this method calls the OnThreadException-Method from the Application. By default this calls a Window giving the user the possibility to continue or quit the application. If the user continues the application will be in an undefinded state, so he should save his work when possible - at best in a new file to avoid corruption of the original file - and restart the application.
It is possible to override this window by providing an event handler of type System.Threading.ThreadExceptionEventHandler and registering it for the ThreadException-Event of the Application.
Because WindowsForms handle only CLR-Exceptions you might want to create also another ThreadExceptionEventHandler for this and register it for the UnhandledException-Event of the Application.

ASP.Net Web Forms

In ASP.Net Web Forms, ASP.Net itself catches most Exceptions using its own try-block.
Therefore it is possible to register a callback method using the Error-Event of System.Web.UI.TemplateControl. The TemplateControl-Class itself is the base class of System.Web.UI.Page and System.Web.UI.UserControl, so each Page and Control exposes this Event too.
On the other hand it is possible to receive notifications about unhandled exceptions for a page by registering a callback method. You can register this method Application wide within the Error-Property of the System.Web.Application-Class.
Also, ASP.Net offers options to create a dump about the Exception to a Webpage.

ASP.Net Web services

Web services have also a easy way of handling Exceptions. ASP.Net catches the Exception and throws a new System.Web.Services.Protocols.SoapException. This SoapException is serialized to XML, representing a SOAP-Fault. This gets understood by other SOAP Clients independently of the Platform or the Programming Environment.
If the client is a .NET Framework application the SOAP-Fault is deserialized into a SoapException and thrown at the place where the WebService-Call occurred. This Exception can then get handled like any other exception.