There is a little-known facility whereby you can take over exception handling within your application.
All you have to do is set up a ThreadException handler.
Assuming you are starting from a standard forms project, add a textbox and a button. All you have to do is to add a suitable handler to the Form class:
namespace WindowsFormsApp1 { public partial class Form1 : Form { public void threadErrorHandler( object sender, ThreadExceptionEventArgs e) { this.textBox1.Text= "hello unhandled exception"; } public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e) { int a = 10; int b = 0; int result = a / b; } } }
You also need to add:
using System.Threading;
Next you need to open the Program.cs file and change the application run to read:
static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.SetUnhandledExceptionMode( UnhandledExceptionMode.CatchException); Form1 form = new Form1(); Application.ThreadException += new ThreadExceptionEventHandler( form.threadErrorHandler); Application.Run(form); }
The two new statements set the UnhandledException mode so that any unhandled exceptions are passed to the event handler and set up the event handler.
You also need to add the same using statement and raise a suitable exception in the application to test things - just click the button and you will get a divide by zero error.
Now if you try the application out you will discover that it doesn't work! The reason is that running the application in the debugger overrides the event handling and the debugger shows you the exception to give you a chance to fix the problem. To see the event handler in action the simplest thing to do is build a release version of the application, navigate to the bin/release directory and run it without the intervention of Visual Studio.
Alternatively if you plan to develop something like this further set the Visual Studio option Debugging to disable the exception assistant. After this you will be told that an exception has occurred but you have the choice of terminating or continuing. Selecting Continue calls the event handler.
It is clear that exception handling is far from easy and far from a solved problem. Exploratory testers often say that a vague error message along the lines of
"Something is wrong - I have to close now"
is a sign that it was generated by an exception and a precise error message e.g.
"Price cannot be negative"
is a a sure sign that it was generated by a specific conditional test. This gives you a very clear idea of the state-of-the-art in exception handling.
We can do better.
Careful design of program logic and using exceptions only when absolutely essential is good advice.
Casting is one of the most confusing aspects of any modern language and it often makes beginners think hard. But if you know why you are doing it, then the how makes a lot more sense. We have encounte [ ... ]
Interfaces - what are they for? Not quite inheritance yet they seem to fit the same purpose. Find out in this extract from my new book, Deep C#: Dive Into Modern C#.
Mike James delves deep into the topic of exception handling
Take Exception to everything
BY MIKE JAMES
Once upon a time we wrote programs that worked and they were designed not to crash. Of course this is a fairy story and what we actually did was to write programs that worked as long as nothing unusual happened - that is as long as the input and operating conditions were as "expected" by the programmer. If this wasn’t the case well - what could you expect - the program was broken and beyond our control. The runtime system generally ended the program with an "abnormal end" or "abend".
Of course today this would be a ridiculous way to handle errors but programs still fail at runtime because eliminating runtime errors is usually something of an afterthought.The solution to the problem is generally accepted to be structured exception handling - that is try-catch to you and me. However the whole subject is trickier than you might imagine. Let's take a look at how it all works and how it all might be made to work.
Try-catch
The basic syntax and operation of exception handling is very simple. You surround any block of code that you think might fail with a try clause. If something does go wrong the block of code generates, or throws, an exception which is caught by the catch clause. For example:
int a=10;
int b=0;
try
{
int result = a / b;
}
catch
{
MessageBox.Show("division by zero isn't allowed");
}
In this case it is clear that b is zero and so a/b cannot be computed and so the CLR throws a runtime exception. This is caught by the catch clause and the message is displayed.
This is a very common way to use the try-catch but it has a problem. Suppose the exception is caused by something other than a divide by zero error. The catch clause operates no matter what the problem is with the try block leading the user and the programmer trying to fix the problem to believe that it is a division by zero problem. The solution is to always specify what exception you are handling with a conditional catch clause. For example:
catch(DivideByZeroException)
{
MessageBox.Show("division by zero isn't allowed");
}
Notice that the catch clause now only handles exceptions of type DivideByZeroException.
The idea of using types to define exceptions is a perfectly reasonable one as many exceptions include an object of the specified type customised with details of the exception. If you need to process the exception object just include it as a parameter in the catch:
catch(DivideByZeroException myException)
{
MessageBox.Show("division by zero isn't allowed "
+ myException.Source);
}
All exception types are ultimately derived from SystemException. If you don't need the exception object don't include it in the catch - just the type.Notice also that derived types match the parent type. So for example, all exceptions match the SystemException type specification.
Of course you can have multiple catch clauses, each dealing with a different type of exception and there is always the finally clause, which is always executed whether or not an exception occurs. For example:
try
{
int result = a / b;
}
catch (DivideByZeroException myException)
{
MessageBox.Show("division by zero isn't allowed " +
myException.Source);
}
catch (OverflowException)
{
MessageBox.Show("Number too big");
}
finally
{
MessageBox.Show("Something might be wrong");
}
This now handles two types of exception - divide by zero and overflow - but whichever happens the finally clause always displays "Something might be wrong" even if an exception has been handled by one of the previous clauses. It is important to realise that the finally clause isn't a catch-all that handles any exception that you can't be bothered to write a specific catch clause for. Its purpose is to ensure that code is executed irrespective of whether or not an exception occurs. Typically this code does a clean up job that is specific to the code in the try block - e.g. it closes files that are no longer used or disposes of other resources that might have been created. It is also worth knowing that a try block can have a finally even if it doesn't have any catch blocks simply to do a clean up job after the system has displayed a default error message.
Another interesting situation that often puzzles the newcomer to exception handing is the simple fact that any variables declared in the try block are local to that block. Similarly any objects created in the block cannot be used within a catch or finally block because the compiler generates an initialised local variable error. What this means is that any objects or variables that you plan to use within the clauses of a try-catch have to be created outside of the block.
Using
The need to clean up after an exception is so common a requirement that there is another way to do it for objects that implement the IDisposable
Bitmap B = newBitmap(10, 10);
try
{
MessageBox.Show(B.Size.ToString());
}
finally
{
if (B != null)
{
((IDisposable)B).Dispose();
}
}
which can also be expressed with using:
Bitmap B;
using(B = newBitmap(10, 10))
{
MessageBox.Show(B.Size.ToString());
}
What is more you can have multiple objects within the using statement and create objects within it:
using(Bitmap B = newBitmap(10, 10))
{
MessageBox.Show(B.Size.ToString());
}
If a finally block isn't about doing things that always have to be done how do you handle any exceptions that don't have specific catch clauses? The solution is to include a catch all clause at the end of the all of the specific catch clauses. For example
try
{
int result = a / b;
}
catch (DivideByZeroException myException)
{
MessageBox.Show("division by zero isn't allowed " +
myException.Source);
}
catch (OverflowException)
{
MessageBox.Show("Number too big");
}
catch
{
MessageBox.Show("something else wrong");
}
finally
{
MessageBox.Show("Something might be wrong");
}
If none of the other catch clauses handle the exception then the final catch and the finally are executed. If you place the general catch clause higher in the list of catch clauses then you will see a compile time error message to the effect that you can't have specific catches after a general one. This is an example of the more general rule that more specific catch clauses have to come before more general ones. The reason is that as soon as a catch clause matches the type specification the try catch is complete and no more catch clauses are checked. So a more general catch clause occurring before a more specific one means that the more specific one will never be used.
What is an exception?
Most exceptions are generated by the CLR, i.e. the runtime. In many ways the whole idea of an exception is to provide a way for the runtime to tell the application that something is wrong. You can think of the archetypal exception as being either hardware generated - a disk error - or one stage removed from being a hardware problem - shortage of memory. The whole point is that these are external conditions or errors which have nothing much to do with the logic of your program. Why should you build allowances for hardware faults into your program logic? It is in this sense that an exception is exceptional.
What has happened in practice is that this idea has drifted into a weaker idea of "things that don't happen very often". For example, is a divide by zero error something exceptional or should your program logic expect it, test for it and deal with it? You could just as easily add an if statement in front of the division as surround the code with a try catch. On the other hand you would be hard pressed to consider a statement such as:
if(!file error) {}
or any condition that mentioned the hardware state, as reasonably part of your program logic - but we do regard it as reasonably as part of an exception handling routine.
So despite the idea that exceptions are a way for the lower level hardware and software to communicate to our application any difficulties they might be having exceptions have become the norm as a way of signalling almost any sort of error. As a result the framework and third party classes raise exceptions and so can any application you care to write.
To give you some idea of how arbitrary the division between error, exception and perfectly normal behaviour is - consider the checked keyword. When you are doing integer arithmetic the chances are good that you will generate an overflow at some point. Is this an exception?
If you surround the arithmetic by checked() then an overflow does raise an exception but surrounding it by unchecked() ignores any overflow. Notice that checking for arithmetic overflow using prior if statements would be a difficult task, so in this case the distinction is between perfectly normal code and an exception. Notice that the reason why overflow isn't always an exception is that low level algorithms often use integer arithmetic to do arithmetic modulo a power of 2. This is a very grey area and perhaps a high level language really shouldn't reflect what goes on under the bonnet in quite this way. The good news is that by default overflow produces an exception which is how it should be.
The checked keyword gives us the best illustration of the distinction between an error and an exception:
·An error is something that you could easily check for
·An exception is something that is difficult to check for before the instruction that actually fails.
Notice that this definition depends on what facilities the high level language gives you to check for error conditions. For example, all we have to do to convert numeric overflow from an exception to an error is provide an IsTooBig predicate.
Custom exceptions
To raise an exception all you have to do is define a custom exception type or use one of the existing types and simply use throw exception type. For example:
thrownewNullReferenceException();
You can also use throw within a catch block to pass the exception on to a higher level handler - if there is one. It is always worth being clear that throwing an exception from within a handler means that you are giving up on the exception and allowing some other part of the system deal with it. The possible consequence of this is that it isn't handled and a runtime error occurs. For example:
catch (DivideByZeroException myException)
{
MessageBox.Show("division by zero isn't allowed " +
myException.Source);
throw myException;
}
passes the exception on to some other handler.
Of course if you want to throw a completely customised exception you need to create your own type that derives from another exception class. You also have to do the work in constructing an object of the type. It's accepted practice to end all exception classes with Exception so, for example, if you wanted to raise a custom exception "TooLongException",in case a task is taking too long you would define something like:
classTooLongException : Exception
{
public TooLongException(){}
public TooLongException(string message):base(message){}
public TooLongException(string message,
Exception inner):base(message,inner){}
}
Exceptions usually have three constructors but you don't have to follow this rule. The default constructor simply returns a new object of the type and allows the exception to be raised simply as:
thrownewTooLongException();
The second constructor allows a message to be included when the exception is raised:
thrownewTooLongException(
"much much too long");
and finally the third constructor that allows a thrown exception to be passed on as part of the exception:
NullReferenceException innercause=
newNullReferenceException();
thrownewTooLongException(
"much much too long", innercause);
Of course in a real example the inner exception would have been generated elsewhere and passed up to the catch handler that is throwing the TooLong exception. This allows the next handler to determine why the TooLong exception might have occurred.
Considering the building of a custom exception brings us to the subject of what information should be packaged into an exception object. There are some standard properties that are inherited from Exception but you can add as many extras as seem appropriate. The most commonly used are:
·the message property to pass on an error message to the user
·Helplink which is the URL of a helpfile
·InnerException which stores any exception passed to the exception
·Source and TargetSite which give the object and function that caused the exception.
More complicated is the HResult property which is used to return the standard Windows error code if the exception was caused by an API.
The most complicated piece of information that an exception object contains is the stack trace - a string containing a list of what method called what to get you to the location where the exception occurred.You can add to the properties and methods of the exception class but most programmers don't and there are some good reasons why not.
The state problem
Mention of the stack trace brings us to the central problem of exception handling. Exceptions in C# and Windows in general are "structured" in the sense that they are nested according to the call stack. If a method raises an exception and doesn't have an exception handler then the system looks back through the call stack to find the first method up the chain that does. This is perfectly reasonable behaviour and in fact without out it exception handling would be a waste of time. For example, suppose you write a try block:
try
{
do things
}
and a set of carefully constructed catch blocks that handle every possible exception. Now suppose that "do things" makes method calls. If any exception that occurred within a method that didn't handle it wasn't passed up the call stack then our efforts at comprehensive exception handling would be nonsense.
However exception handling has some deep problems if you are planning to allow the program to continue. In practice most programmers simply use exception handling as a way of allowing the application to make a soft landing rather than crash. Typically they provide a more informative, or more friendly, error message that tells the user who to contact and what information to provide. Of course this is all Windows dressing because if the exception really is an exception and not an error state that the programmers were too lazy to detect then there will be nothing that can be done to correct it - other than improve or fix the hardware.
Suppose, however, that you are trying to use exceptions to allow an application to continue. This is a much more difficult problem. The first idea you need to accept is exception safe code. If the code in the try block raises an exception then you have to ask what side effects are there when you reach the catch block. Side effects are the change in state that the try block has implemented up to the point that it raised the exception. For example, if the try block opened a file then it is still open. Side effects include deeper problems such as memory leaks and orphaned resources in general. Notice that the try block has its own scope so if you stick to the rule that the code in a try block will not access anything not created in the block it is almost (but not) guaranteed to be exception safe.
Notice that the very act of propagating the exception up the call stack of necessity unwinds the state back to the method that handles the exception. That is, if you have a catch block that is triggered by an exception thrown by a method called within the try block then all trace of the internal state of that method has gone. This doesn't make cleaning up after the method an attractive proposition.Basically if your aim it to continue the application you need to handle exceptions at the lowest level and avoid unwinding the call stack.
A two-step process
The best way to think of implementing exception safe code in practice is probably to think of it as a two step - commit and rollback operation. The try block attempts the commit and the catch implements the rollback. In this scenario all of the objects have to be created before the try-catch block - otherwise the try and catch blocks couldn't operate on the same variables. This still leaves you open to some exceptions - for example running out of memory while creating objects. There seems to be no way around the scope problems that try catch introduces.
Consider the response to a timeout exception for a moment. Perhaps what you want to do is give the user a chance to correct some data - a website address, say- and then try again. This leads to the idea of Resume or Resume Next which are attractive propositions - until you realise the scope for misunderstanding its use could make, and has made, a mess of many an application. Basic, but not VB .NET made Resume available and in unskilled hands it didn't work well and was the reason why VB .NET has structured exception handling. However the basic response"something strange has happened where can we resume application execution"is stillreasonable.. Using try-catch you can create the same effect as Resume Next if you are prepared to do a lot of work. For example, suppose the correct result of a/0 is deemed to be 0 - don’t take this example too seriously in its detail - then you can "resume next" using:
int a=10;
int b=0;
int result;
try
{
result = a / b;
}
catch (DivideByZeroException)
{
result = 0;
}
MessageBox.Show(result.ToString());
Once again note that all the variables have to be defined before the try block. This just makes use of the basic behaviour of the try-catch - that execution continues after the last catch or the finally clause. Notice that you can't protect multiple arithmetic statements or method calls that do the same arithmetic unless you are prepared to add try-catch around each one. In other words, there is no global way of resuming after an exception. Many are of the opinion that such a thing would be a bigger exception waiting to happen but some just think that we haven't figured out how to implement such a facility in the right way.
If you want to resume, i.e. retry, the statement that caused the problem then this is really difficult. Using the same manual method you could simple write the statement out again:
try
{
result = a / b;
}
catch (DivideByZeroException)
{
b=2;
result = a/b;
}
but now the second attempt isn't protected. You could use:
try
{
result = a / b;
}
catch (DivideByZeroException)
{
try { b = 2; result = a / b; }
catch (DivideByZeroException)
{ result = 0; }
}
MessageBox.Show(result.ToString());
although now what to do in the inner try-catch is becoming a little contrived. It makes more sense to consider retrying in this way following a timeout or a file read error but the basic idea is sound and leads to constructions like:
bool flag=false;
do
{
try
{
result = a / b;
flag = true;
}
catch (DivideByZeroException)
{
result = 0;
flag = true;
}
} while (!flag);
You can think of this as try, put it right, and try again. In practice you would need to add something that terminated the loop when the retry had been attempted sufficiently often. There is also the problem of making sure that the try (or the catch) does not leave side effects that are accumulated with each repetition.
The ultimate custom exception handler
There is a little-known facility whereby you can take over exception handling within your application. All you have to do is set up a ThreadException handler. Assuming you are starting from a standard forms project all you have to do is to add a suitable static handler to the Form class:
publicstaticvoid threadErrorHandler(
object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show("hello unhandled exception");
}
You also need to add:
using System.Threading;
Next you need to open the Program.cs file and change the application run to read:
The two new statements set the UnhandledException mode so that any unhandled exceptions are passed to the event handler and set up the event handler. You also need to add the same using statement and raise a suitable exception in the application to test things.
Now if you try the application out you will discover that it doesn't work! The reason is that running the application in the debugger overrides the event handling and the debugger shows you the exception to give you a chance to fix the problem. To see the event handler in action the simplest thing to do is build the application, navigate to the bin/release directory and run it without the intervention of Visual Studio. Alternatively if you plan to develop something like this further set the Visual Studio option Debugging to disable the exception assistant. After this you will be told that an exception has occurred but you have the choice of terminating or continuing. Selecting Continue calls the event handler.
It is clear that exception handling is far from easy and far from a solved problem. Careful design of program logic and using exceptions only when absolutely essential is good advice.
Mike James has over 20 years of programming experience, both as a developer and lecturer, and has written numerous books including Foundations of Programming. His PhD is in computer science.