In this topic
- Exception handling
- Using exceptions
- Try, Catch, and Finally construct
- Code without exception handling
- Errors from COM components
- Throwing errors and the exception hierarchy
- Writing your error handler
Structured exception handling implementation is straightforward, and the same concepts are applicable to VB.NET or C#. VB.NET allows backward compatibility by also providing unstructured exception handling via the familiar OnError GoTo statement and Err object, although this model is not discussed in this topic.
Exceptions are used to handle error conditions in Visual Studio .NET and provide information about the error condition. An exception is an instance of a class that inherits from the System.Exception base class. Many different types of exceptions are provided by the .NET Framework, and it is also possible to create your own exceptions. Each type extends the basic functionality of the System.Exception by allowing further access to information about the specific type of error that has occurred. An instance of an exception is created and thrown when the .NET Framework encounters an error condition. You can handle exceptions by using the Try, Catch, and Finally construct.
This construct allows you to catch errors that are thrown within your code. An example of this construct is shown below. An attempt is made to rotate an envelope, which throws an error. See the following code example:
[C#]
try
{
IEnvelope env = new EnvelopeClass();
env.PutCoords(0D, 0D, 10D, 10D);
ITransform2D trans = (ITransform2D)env;
trans.Rotate(env.LowerLeft, 1D);
}
catch (System.Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
{
// Clean up the code.
}
[VB.NET]
Try
Dim env As IEnvelope = New EnvelopeClass()
env.PutCoords(0D, 0D, 10D, 10D)
Dim trans As ITransform2D = env
trans.Rotate(env.LowerLeft, 1D)
Catch ex As System.Exception
MessageBox.Show("Error: " + ex.Message)
' Clean up the code.
End Try
Place a Try block around code that can fail. If the application throws an error within the Try block, the point of execution switches to the first Catch block. The Catch block handles a thrown error. The application executes the Catch block when the Type of a thrown error matches the error Type specified by the Catch block. You can have more than one Catch block to handle different kinds of errors. The following code example verifies if the exception thrown is a DivideByZeroException:
[C#]
catch (DivideByZeroException divEx)
{
// Perform divide by zero error handling.
}
catch (System.Exception ex)
{
// Perform general error handling.
}
...
[VB.NET]
...
Catch divEx As DivideByZeroException
' Perform divide by zero error handling.
Catch ex As System.Exception
' Perform general error handling.
...
If you do have more than one Catch block, the more specific exception Types should precede the general System.Exception, which will always succeed the type check.
The application always executes the Finally block after the Try block completes, or after a Catch block if an error was thrown. Therefore, the Finally block should contain code that must always be executed, for example, to clean up resources such as file handles or database connections.
If you do not have any cleanup code, you do not need to include a Finally block.
If a line of code that is not contained in a Try block throws an error, the .NET runtime searches for a Catch block in the calling function, continuing up the call stack until a Catch block is found.
If a Catch block is not specified in the call stack, the exact outcome can depend on the location of the executed code and the configuration of the .NET runtime. Therefore, it is advisable to at least include a Try, Catch, Finally construct for all entry points to a program.
The structured exception handling model differs from the HRESULT model used by the component object model (COM). C++ developers can easily ignore an error condition in an HRESULT.
The .NET runtime handling of errors from COM components works in the following way: If a .NET program calls a function in a COM component (through the COM interop services), and returns an error condition as the HRESULT, the HRESULT is used to populate an instance of the COMException. This is then thrown by the .NET runtime, where it can be handled in the usual way by using a Try, Catch, Finally block.
Therefore, it is advisable to enclose all code that can raise an error in a COM component within a Try block, with a corresponding Catch block to catch a COMException. The following code example is the first example rewritten to check for an error from a COM component:
[C#]
{
IEnvelope env = new EnvelopeClass();
env.PutCoords(0D, 0D, 10D, 10D);
ITransform2D trans = (ITransform2D)env;
trans.Rotate(env.LowerLeft, 1D);
}
catch (COMException COMex)
{
if (COMex.ErrorCode == - 2147220984)
MessageBox.Show("You cannot rotate an Envelope");
MessageBox.Show("Error " + COMex.ErrorCode.ToString() + ": " + COMex.Message);
}
catch (System.Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
...
[VB.NET]
Dim env As IEnvelope = New EnvelopeClass()
env.PutCoords(0D, 0D, 10D, 10D)
Dim trans As ITransform2D = env
trans.Rotate(env.LowerLeft, 1D)
Catch COMex As COMException
If (COMex.ErrorCode = -2147220984) Then
MessageBox.Show("You cannot rotate an Envelope")
MessageBox.Show _
("Error " + COMex.ErrorCode.ToString() + ": " + COMex.Message)
End If
Catch ex As System.Exception
MessageBox.Show("Error: " + ex.Message)
...
The COMException belongs to the System.Runtime.InteropServices namespace. It provides access to the value of the original HRESULT via the ErrorCode property, which you can test to determine the error condition that occurred.
If you are coding a user interface, you may want to correct the error condition in the code and try the call again. Alternatively, you may want to report the error to users to let them decide the course of action to take. You can make use of the message property of the exception to identify the problem.
However, if you are writing a function that is only called from other code, you may want to deal with an error by creating a specific error condition and propagating this error to the caller. You can do this using the Throw keyword.
To throw the existing error to the caller function, write your error handler using the Throw keyword. See the following code example:
[C#]
catch (System.Exception ex)
{
throw;
}
...
[VB.NET]
Catch ex As System.Exception
...
If you want to propagate a different or more specific error back to the caller, create a new instance of an exception, populate it appropriately, and throw this exception back to the caller. The following code example uses the ApplicationException constructor to set the message property:
[C#]
catch (System.Exception ex)
{
throw new ApplicationException("You had an error in your application", ex);
}
...
[VB.NET]
Catch ex As System.Exception
Throw New ApplicationException _
("You had an error in your application")
...
However, if you do this, the original exception is lost. To allow complete error information to be propagated, the exception includes the InnerException property. This property should be set to equal the caught exception before the new exception is thrown. This creates an error hierarchy. Again, the following code example uses the ApplicationException constructor to set the InnerException and message properties:
[C#]
catch (System.Exception ex)
{
System.ApplicationException appEx = new ApplicationException(
"You had an error in your application", ex);
throw appEx;
}
...
[VB.NET]
Catch ex As System.Exception
Dim appEx As System.ApplicationException = _
New ApplicationException("You had an error in your application", ex)
Throw appEx
...
In this way, the function that eventually deals with the error condition can access all the information about the cause of the condition and its context. If you throw an error, the application executes the current function's Finally clause before control is returned to the calling function.
The best approach to handling an error depends on what error is thrown and in what context. For more information, see Best Practices for Handling Exceptions on the MSDN Web site.