Exceptions are runtime bugs that occur due to illegal action taken by the user. The Exception occurs whenever the object state is corrupted and which causes the program to terminate. Based on the severity of the exception, we choose either to continue with the program by proper handling the code or rethrow the exception again to the parent block and eventually terminate the whole program.
In this post, I am not going to talk about how to throw/re-throw exceptions or even write the best practices on Exception handling, but I will go on with some of the hidden facts of exception handling which you might want to know and later on will involve IL to check how exceptions are generated.
The Basics
Exception handling is one of the weakest sections of CLR. Even though I like most of the things that CLR brings to us, but I definitely disagree if the exception handling is one amongst it. Even the exception handling as being evolved with the system is enhanced very well recently with the introduction of Uncatchable Exceptions, RuntimeWrappedException, code contacts, etc. Let's discuss some of the major enhancements to the exception system.
The .NET exception handling is made up of three sections:
try
: In this segment, you need to write only the portion which can potentially throw errors. catch
: This section can overload based on the type of Exception and will be used to handle the exception which occurred in the Try
block. finally
: This block executes irrespective of try
and catch
. We write the clean up tasks here as it ensures to run even if anything occurs.
You must note, CLR even does not allow a Thread to abort when the thread is running a finally
block. So it is recommended to write the most sensitive code in finally
block, if not it can eventually throw
exception to its parent block or even been left unhandled.
try
{
Console.WriteLine("This is In Try");
}
catch (SystemException sex)
{
Console.WriteLine("This is catch with system exception : {0}", sex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("This is in Catch with exception: {0}", ex.ToString());
}
finally
{
Console.WriteLine("In finally");
}
Now in the above code, you can see, I have intentionally placed two catch
blocks, one with SystemException
and another with Exception
even though the try
block has no possibility to throw exceptions. Please note, the overloading for exception matches the exact exception type of probable base type from top to bottom. As System.Exception
is the base to all exceptions in .NET, you should leave it as the last exception overload. Apart from the try
/catch
, finally
is optional block which lets you write code which executes irrespective of try
/catch
.
Note: If you don't want to handle the exception in your code, you can still use try / finally. .NET allows you to keep your finally with try block to ensure that every exception that occurs in your code will be handled by the caller. So if you keep catch, finally is optional, but if you omit catch, finally becomes mandatory. So in other words, try comes with either catches or finally or both.
Based on the initial idea of Exception handling, Microsoft thought of categorizing exception into:
System.SystemException
System.ApplicationException
with System.Exception
being the base of the two and all system generated exceptions are categorized into SystemException
and all the exceptions that comes under Application are derived from ApplicationException
. But on a sad note, this is not followed properly. Some system generated exceptions are derived from ApplicationException
(like TargetInvocationException
) even some application thrown exceptions are derived from SystemException
(like FormatException
). Thus, this makes no sense to continue with the initial categorizing exceptions into two types and as System.Exception
yet allows us to inherit, application programmers usually like to create custom Exception Type deriving directly from System.Exception
rather than System.ApplicationException
.
Uncatchable Exceptions
Now, this is truly an interesting enhancement to Exception handling. Uncatchable exceptions are those which are treated as a bug in the system, and the process gets terminated when this exception occurs in your application. The Uncatchable exceptions cannot be handled using catch
block, and you should also note these exceptions don't even allow the finally
block to execute.
StackOverflowException
: CLR 2.0 does not allow you to catch StackOverflowException
for your application. It is treated as a bug in your application which you need to fix immediately. The application gets terminated when this exception occurs.
static void Main(string[] args)
{
try
{
Console.WriteLine("This is In Try");
Program p = new Program();
p.ExecuteOverflow(new Program());
}
catch (SystemException sex)
{
Console.WriteLine("This is catch with system exception : {0}", sex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("This is in Catch with exception: {0}", ex.ToString());
}
finally
{
Console.WriteLine("In finally");
}
}
public void ExecuteOverflow(Program p)
{
p.ExecuteOverflow(new Program());
}
Just if I replace the code with the one above, you will see that the application will terminate without writing "In finally
" and no catch
(even though Exception
is the base for StackOverflowException
) never being caught.
In CLR 4.0, Microsoft added another uncatchable exception called AccessViolationException
which can also never be caught.
When an uncaught exception occurs in the system, Microsoft by default writes the entire exception in System's event log before terminating the process. You can see the log from Control Panel -> Administrative Tools -> Event Viewer. A sample exception log is shown below:
Here, it shows that stack overflow occurred in ExceptionHandlingDemo
with the helplink for Microsoft Support.
Note: You should note, even though some people say OutOfMemoryException is also uncatchable, but it isn't. CLR still allows you to catch OutOfMemoryException which lets you handle memory related issues.
Can I make an Uncatchable Exception?
This is the most general question that appears in mind while talking about Uncatchable Exceptions. Yes, there is an option. Environment.FailFast
is a method which lets you to create an UncatchableException
. If your application produces a corrupted state which could not be recovered, you could either use AppDoman.Unload
to unload the domain in which the Thread is running, or use Environment.FailFast
to terminate the entire process with an entry in EventLog
.
Dealing with Corrupted State Exceptions in .NET 4.0
Well, if you are reading this article from the beginning, I have told you that CLR will not allow finally
block to execute when the Corrupted State Exception like StackOverflowException
, AccessViolationException
, etc. With the rarely used class HandleProcessCorruptedStateExceptions
you will have now the option to log the Corrupted State Exceptions and continue running the process (even though it is not recommended). If you want to learn more about it, you can read this post.
Note: CLR is also very smart enough to wrap around the runtime exception that comes from unmanaged code and wrap around into RuntimeWrappedException.
The Internals
Now moving back to internals of Exception handling, IL has a specific section for try
/catch
/finally
. Just let's see the IL for the code specified first:
The IL is very straightforward. You can see every block has a leave.s
which lets the control to jump L_003c
which is finally
block. Hence if no exception occurs, the control will jump from L_000e
to L_003c
. The last section of the IL lists all the possible try
/catch
paths which the program can traverse. Each catch
block has a handler associated with respective .try
. The one that first lists is System.SystemException
while the last lists the normal execution.
As when the exception is encountered, the CLR finds the proper handler based on the list from top to bottom, it is essential to have more specific exception above in the list.
Another important point here to note, try/catch puts a scope around the instruction sets. There are lots of C# constructs also using try/catch/finally to actually take its advantage. using statement is actually a try/finally with Dispose called inside finally block, even foreach is also the same thing. lock statement on the other hand invokes Monitor.Exit inside finally.
Conclusion
Even though there are many things that I have to discuss before point actually comes to an end, I will share them in a separate post. Exception handling is a very important section for any program. I hope you like the post and stay tuned for more about it.
Thank you for reading.