Visual Basic Concepts
Error Handling Hierarchy
An enabled error handler is one that was activated by executing an On Error statement and hasn't yet been turned off — either by an On Error GoTo 0 statement or by exiting the procedure where it was enabled. An active error handler is one in which execution is currently taking place. To be active, an error handler must first be enabled, but not all enabled error handlers are active. For example, after a Resume statement, a handler is deactivated but still enabled.
When an error occurs within a procedure lacking an enabled error-handling routine, or within an active error-handling routine, Visual Basic searches the calls list for another enabled error-handling routine. The calls list is the sequence of calls that leads to the currently executing procedure; it is displayed in the Call Stack dialog box. You can display the Call Stack dialog box only when in break mode (when you pause the execution of your application), by selecting the View, Call Stack menu item or by pressing CTRL+L.
Searching the Calls List
Suppose the following sequence of calls occurs, as shown in Figure 13.2:
An event procedure calls Procedure A.
Procedure A calls Procedure B.
Procedure B calls Procedure C.
Figure 13.2 A sequence of calls
While Procedure C is executing, the other procedures are pending, as shown in the calls list in the Call Stack dialog box.
*For More Information* For more information, see "Monitoring the Call Stack" later in this chapter.
Figure 13.3 shows the calls list displayed in the Call Stack dialog box.
Figure 13.3 The calls list when procedures are pending
If an error occurs in Procedure C and this procedure doesn't have an enabled error handler, Visual Basic searches backward through the pending procedures in the calls list — first Procedure B, then Procedure A, then the initial event procedure (but no farther) — and executes the first enabled error handler it finds. If it doesn't encounter an enabled error handler anywhere in the calls list, it presents a default unexpected error message and halts execution.
If Visual Basic finds an enabled error-handling routine, execution continues in that routine as if the error had occurred in the same procedure that contains the error handler. If a Resume or a Resume Next statement is executed in the error-handling routine, execution continues as shown in the following table.
|Resume||The call to the procedure that Visual Basic just searched is re-executed. In the calls list given earlier, if Procedure A has an enabled error handler that includes a Resume statement, Visual Basic re-executes the call to Procedure B.|
|Resume Next||Execution returns to the statement following the last statement executed in that procedure. This is the statement following the call to the procedure that Visual Basic just searched back through. In the calls list given earlier, if Procedure A has an enabled error handler that includes a Resume Next statement, execution returns to the statement after the call to Procedure B.|
Notice that the statement executed is in the procedure where the error-handling procedure is found, not necessarily in the procedure where the error occurred. If you don't take this into account, your code may perform in ways you don't intend. To make the code easier to debug, you can simply go into break mode whenever an error occurs, as explained in the section, "Turning Off Error Handling," later in this chapter.
If the error handler's range of errors doesn't include the error that actually occurred, an unanticipated error can occur within the procedure with the enabled error handler. In such a case, the procedure could execute endlessly, especially if the error handler executes a Resume statement. To prevent such situations, use the Err object's Raise method in a Case Else statement in the handler. This actually generates an error within the error handler, forcing Visual Basic to search through the calls list for a handler that can deal with the error.
In the VerifyFile procedure example in the Errors.vbp sample application, the number originally contained in
Err.Number is assigned to a variable,
intErrNum, which is then passed as an argument to the Err object's Raise method in a Case Else statement, thereby generating an error. When such an error occurs within an active error handler, the search back through the calls list begins.
Allocating Errors to Different Handlers
The effect of the search back through the calls list is hard to predict, because it depends on whether Resume or Resume Next is executed in the handler that processes the error successfully. Resume returns control to the most recently executed call out of the procedure containing the error handler. Resume Next returns control to whatever statement immediately follows the most recently executed call out of the procedure containing the error handler.
For example, in the calls list shown in Figure 13.3, if Procedure A has an enabled error handler and Procedure B and C don't, an error occurring in Procedure C will be handled by Procedure A's error handler. If that error handler uses a Resume statement, upon exit, the program continues with a call to Procedure B. However, if Procedure A's error handler uses a Resume Next statement, upon exit, the program will continue with whatever statement in Procedure A follows the call to Procedure B. In both cases the error handler does not return directly to either the procedure or the statement where the error originally occurred.
Guidelines for Complex Error Handling
When you write large Visual Basic applications that use multiple modules, the error-handling code can get quite complex. Keep these guidelines in mind:
While you are debugging your code, use the Err object's Raise method to regenerate the error in all error handlers for cases where no code in the handler deals with the specific error. This allows your application to try to correct the error in other error-handling routines along the calls list. It also ensures that Visual Basic will display an error message if an error occurs that your code doesn't handle. When you test your code, this technique helps you uncover the errors you aren't handling adequately. However, in a stand-alone .exe file, you should be cautious: If you execute the Raise method and no other procedure traps the error, your application will terminate execution immediately, without any QueryUnload or Unload events occurring.
Use the Clear method if you need to explicitly clear the Err object after handling an error. This is necessary when using inline error handling with On Error Resume Next. Visual Basic calls the Clear method automatically whenever it executes any type of Resume statement, Exit Sub, Exit Function, Exit Property, or any On Error statement.
If you don't want another procedure in the calls list to trap the error, use the Stop statement to force your code to terminate. Using Stop lets you examine the context of the error while refining your code in the development environment.
*Caution* Be sure to remove any Stop statements before you create an .exe file. If a stand-alone Visual Basic application (.exe) encounters a Stop statement, it treats it as an End statement and terminates execution immediately, without any QueryUnload or Unload events occurring.
Write a fail-safe error-handling procedure that all your error handlers can call as a last resort for errors they cannot handle. This fail-safe procedure can perform an orderly termination of your application by unloading forms and saving data.
*For More Information* See the "Inline Error Handling," "Design Time, Run Time, and Break Mode," and "Testing Error Handling by Generating Errors" topics later in this chapter.