11.2. Runtime Error HandlingVisual Basic supports both unstructured error handling and structured exception handling. This discussion starts with a look at unstructured error handling , the same error-handling support provided in VB 6. You are not required to include any error handling in a VB procedure. If you do include such handling in a procedure, you must include either the structured or unstructured variety; a single procedure cannot include both types of error-handling methods. 11.2.1. Unstructured Error HandlingError-handling techniques that revolve around the various On Error... statements are referred to as unstructured error-handling techniques. These techniques generally involve the Err object and the Visual Basic call stack. 11.2.1.1. The Err objectVisual Basic's built-in error object, called Err, has several useful properties and methods, as shown in Tables 11-1 and 11-2, respectively.
11.2.1.2. Dealing with runtime errorsVisual Basic detects a runtime error as soon as it occurs, sets the properties of the Err object, and directs the flow of execution to a location that the programmer has specified by the most recent On Error statement. This location can be one of the following:
If none of these methods exist or are enabled (and no structured exception handlers are in use), VB issues an error message and terminates the application. The remainder of this section describes each option in more detail. Inline error handling. Code execution will be "redirected" to the line following the offending line of code if the most recent On Error statement is: On Error Resume Next This is referred to as inline error handling . The following example demonstrates this type of error handling in the process of renaming a file. The code specifically examines any error number (Err.Number) generated by the Rename function. Dim oldName As String Dim newName As String On Error Resume Next ' ----- Prompt the user for the old and new names. oldName = InputBox("Enter the file name to rename.") newName = InputBox("Enter the new file name.") ' ----- Rename the file. Err.Clear( ) Rename("c:\" & oldName, "c:\" & newName) ' ----- Deal with any error. If (Err.Number = 53) Then ' ----- File not found error. MsgBox("File '" & oldName & "' not found.") Exit Sub ElseIf (Err.Number <> 0) Then ' ----- All other errors. MsgBox("Error " & Err.Number & ": " & Err.Description) Exit Sub End If Centralized error handling. Centralized error handling keeps error detection and error handling in the same procedure, but it places all error-handling code in a common location within that procedure. This is especially useful when code detection for a common set of errors occurs multiple times throughout a large procedure. Execution is redirected to the central error-handling code block using the statement: On Error GoTo label This is outlined in the following code template: Public Sub Example( ) On Error GoTo ErrorHandler ' ----- If a run-time error occurs here, Visual Basic ' directs execution to the ErrorHandler label. Exit Sub ErrorHandler: ' ----- Code can be placed here to handle errors directly. ' Err.Number, Err.Description, and Err.Source can be ' examined here. End Sub Once the On Error GoTo ErrorHandler statement is executed, the error handler beginning at the label ErrorHandler is active. There are several possibilities for dealing with the error. The most common possibility is simply to handle the error in the active error handler, perhaps by displaying an error message asking the user to take corrective action. Another common approach is to pass information about an error to the calling procedure through parameters or through the return value of the offending function. For instance, if a function is designed to rename a file, the function might return an integer error code indicating the success or failure of the operation. This method is quite common among the Win32 API functions. In particular, the error code might be 0 for success, -1 if the file does not exist, -2 if the new filename is invalid, and so on. A third possibility is to pass the error to the calling procedure by invoking the Err.Raise method within the active error handler: Err.Raise(Err.Number, Err.Source, Err.Description, _ Err.HelpFile, Err.HelpContext) This triggers the next procedure up in the call stack to process the error using its own active error handler. This process is called regenerating or reraising the error. If that procedure does not have an active error handler, the raised error continues up the call stack until it finds a procedure with an active error handler. No enabled error handler. When you first enter any procedure, error handling is disabled in that procedure until the code encounters some sort of On Error or structured exception-handling statement. Using On Error GoTo label enables centralized error handling, but what if you want to turn off the central error- handling block code? The following statement takes just such an action, restoring the error-handling state to what it was when you first entered the procedure. On Error GoTo 0 Without an enabled error handler, all errors cause execution to move up the call stack to the next procedure, continuing up until an enabled exception handler is found. A lack of exception handlers causes Visual Basic to display an error message and terminate the application. A third variation of the On Error statement disables an error handler from inside of an error handler: On Error GoTo -1 This statement can be used within a block of error-handling code. It causes the procedure to forget that it is in an error handler; Resume statements will have no impact after using this statement. To reinstate error handling, you must use another On Error statement. New in 2005. Visual Basic 2005 adds support for a global, central error-handling routine. This feature is only available in Windows Forms applications, and it is accessed through the My.Application.UnhandledException event. The actual procedure for this event is found in the application's ApplicationEvents.vb file, which is hidden from the Solution Explorer view by default. (Click the Show All Files button in the Solution Explorer to display this and other hidden files.) The event procedure has the following form. Namespace My Class MyApplication Private Sub MyApplication_UnhandledException( _ ByVal sender As Object, _ ByVal e As UnhandledExceptionEventArgs) _ Handles Me.UnhandledException ' ----- Special error handling code goes here. End Sub End Class End Namespace This special event handler is ignored when running your application within the Visual Studio IDE. 11.2.2. Structured Exception HandlingStructured exception handling uses the TRy...Catch...Finally construct to handle local errors. This is a much more object-oriented approach, involving objects of the System.Exception class and its derived classes. The syntax of the try...Catch...Finally construct is: Try tryStatements [Catch [exception [As type]] [When expression] catchStatements [Exit Try]] [Catch [exception [As type]] [When expression] catchStatements] ... [Catch [exception [As type]] [When expression] catchStatements] [Finally finallyStatements] End Try The tryStatements (which are required) constitute the try block and are the statements that are monitored for errors by VB. Error handling is active within the try block. The Catch blocks (you can include zero or more of these) contain code that is executed in response to VB "catching" a particular type of error within the TRy block. Thus, the Catch blocks consist of the error handlers for the TRy block. The exception [As type] and [When expression] clauses are referred to as filters. An exception is either a variable of type System.Exception or one of its derived classes. It is by using one of these derived classes that you can narrow the scope of a Catch clause to a specific error. The clause: Catch ex As Exception will catch any exception for handling within that Catch block. The clause: Catch ex As DivideByZeroException catches divide-by-zero division errors, but no others. The DivideByZeroException class is one of the classes derived from System.Exception. The When filter is typically used with user-defined errors. For instance, the code in the following TRy block raises an error if the user does not enter a number. Dim userInput As String Try userInput = InputBox("Enter a number.") If Not IsNumeric(userInput) Then Err.Raise(1) Catch When Err.Number = 1 MsgBox("Supplied data was non-numeric.") End Try The following code will not catch an error, since no true error is generated: Dim testNumber As Integer Try testNumber = 5 Catch When testNumber = 5 ' ----- Execution will never come here. MsgBox(testNumber & " is not right.") End Try The Finally clause in a try statement includes any code that must be executed, whether an error occurs or not. This final code can be used for cleanup in the event of an error. The Exit Try statement can appear anywhere within a try...Catch...Finally block to jump out of the statement immediately. Any remaining Finally clause statements are skipped. As with unstructured error handling, VB may pass an error up the call stack when using structured exception handling. This happens in the following situations:
11.2.2.1. Exception classesThe following list includes many of the common exception types derived from the System.Exception class. Classes are indented under the class from which they are derived. Exception ApplicationException SystemException AccessException FieldAccessException MethodAccessException MissingMemberException MissingFieldException MissingMethodException AppDomainUnloadedException AppDomainUnloadInProgressException ArgumentException ArgumentNullException ArgumentOutOfRangeException DuplicateWaitObjectException ArithmeticException DivideByZeroException NotFiniteNumberException OverflowException ArrayTypeMismatchException BadImageFormatException CannotUnloadAppDomainException ContextMarshalException CoreException ExecutionEngineException IndexOutOfRangeException StackOverflowException ExecutionEngineException FormatException InvalidCastException InvalidOperationException MulticastNotSupportedException NotImplementedException NotSupportedException PlatformNotSupportedException NullReferenceException OutOfMemoryException RankException ServicedComponentException TypeInitializationException TypeLoadException EntryPointNotFoundException TypeUnloadedException UnauthorizedAccessException WeakReferenceException URIFormatException In general, the derived classes provide no additional class members; it is the class instance itself that provides the information needed to distinguish between different types of errors. (It is possible to access the exception class for an error even when you are using the On Error unstructured error-handling methods. The Err object includes a GetException method that returns an instance of the exception class related to the error.) Some of the derived classes do include additional information that can be useful to your error-processing actions. For instance, the ArgumentException class includes a ParamName property that returns the name of the parameter that causes the exception. The following example demonstrates its use. Sub CopyAnImportantFile( ) Dim sourceFile As String = "c:\temp.txt" Dim destFile As String = "I am not a file path!" Try FileCopy(sourceFile, destFile) Catch ex As ArgumentException MsgBox(ex.Message & " Parameter: " & e.ParamName) End Try End Sub The System.Exception class includes several important members, as listed in Table 11-3.
The following example displays the type of content you can except from these class properties. It generates an ArgumentNullException exception manually, which is handled two procedures further up the call stack. Public Sub Level1Routine( ) Dim info As String Try Level2Routine( ) Catch ex As Exception info = "Message: " & ex.Message & vbCrLf & _ "Source: " & ex.Source & vbCrLf & _ "Stack: " & ex.StackTrace & vbCrLf & _ "Target: " & ex.TargetSite.Name & vbCrLf & _ "ToString: " & ex.ToString Debug.WriteLine(info) End Try End Sub Sub Level2Routine( ) Level3Routine( ) End Sub Public Sub Level3Routine( ) Throw New ArgumentNullException( ) End Sub In Level3Routine, an ArgumentNullException is thrown using the Throw statement, passing a new instance of the exception up the call stack. The output from the call to Level1Routine is as follows (slightly formatted for readability): Message: Value cannot be null. Source: WindowsApplication1 Stack: at WindowsApplication1.Form1.Level3Routine( ) in C:\temp\Form1.vb:line 26 at WindowsApplication1.Form1.Level2Routine( ) in C:\temp\Form1.vb:line 22 at WindowsApplication1.Form1.Level1Routine( ) in C:\temp\Form1.vb:line 10 Target: Level3Routine ToString: System.ArgumentNullException: Value cannot be null. at WindowsApplication1.Form1.Level3Routine( ) in C:\temp\Form1.vb:line 26 at WindowsApplication1.Form1.Level2Routine( ) in C:\temp\Form1.vb:line 22 at WindowsApplication1.Form1.Level1Routine( ) in C:\temp\Form1.vb:line 10 |