Errors are usually placed into three "categories"-compile time, run time, and logic. Compile-time errors are typically syntax errors such as missing double quotation marks that prevent your macro from compiling. Compile-time errors are the easiest to deal with because they are found immediately and the IDE shows you which line caused the problem. Run-time errors compile properly but cause an error when the macro runs. For example, dividing by a variable that at some point evaluates to zero will cause a run-time error. The third type, logic errors, are mistakes in the business logic of the program: They compile and run okay, but generate the wrong answers. They're the worst kind because you have to find them yourself-the computer won't help you at all. This section is about run-time errors: how to deal with them and how to correct them.
An error handler is a piece of code that runs when an error occurs. The default error handler displays an error message and stops the macro. OOo Basic provides a mechanism to modify this behavior (see Table 18 ). The first form, On Error Resume Next , tells OOo to ignore all errors: No matter what happens, keep running and pretend everything is fine. The second form, On Error GoTo 0, tells OOo to stop using the current error handler. Ignoring error handler scope issues, as explained later, think of On Error GoTo 0 as restoring the default method of handling errors: Stop running and display an error message. The final form, On Error GoTo LabelName, allows you to define code to handle errors the way that you want. This is called "setting up an error handler."
Form | Usage |
---|---|
On Error Resume Next | Ignore errors and continue running at the next line in the macro. |
On Error GoTo 0 | Cancel the current error handler. |
On Error GoTo LabelName | Transfer control to the specified label. |
When an error occurs, the code that was running stops running, and control is transferred to the current error handler. The error handlers use the functions in Table 19 to determine the cause and location of the error.
Function | Use |
---|---|
Erl | Integer line number of the last error. |
Err | Integer error number of the last error. |
Error | Error message of the last error. |
Compatibility | Visual Basic uses an Error object and does not support the functions in Table 19 . |
All error handlers must be declared in a routine and are local to the containing subroutine or function. When an error occurs, OOo Basic starts working backward through the call stack until it finds an error handler. If it doesn't find an error handler, it uses the default handler. The default handler prints an error message and halts the program. The error information, such as Erl, indicates the line number in the current routine that caused the error. For example, if the current routine calls the function b() at line 34 and an error occurs in b(), the error is reported as occurring at line 34. Listing 57 contains an example of this, and Figure 17 shows the call stack. Another example is shown in Listing 60.
![]() |
x = x + 1 'Assume that this is line 33 Call b() 'Error in b() or something b() calls (line 34) Exit Sub 'Leave the subroutine ErrorHandler: 'No other handlers between here and the error Print "error at " & Erl 'Prints line 34
![]() |
Tip | Calls made to an external DLL will not generate an error that is catchable by an error handler. Check the return code from the called DLL instead. |
Error handling, in some cases, means ignoring errors. The On Error Resume statement tells OOo Basic that if a standard error occurs, it should ignore the error and resume running the code at the next line in the macro (see Listing 58 ). The error information is cleared, so it isn't possible to check if an error occurred following the statement.
![]() |
Private zero% sub ExampleErrorResumeNext On Error Resume Next Print 1/Zero% If Err <> 0 Then Print Error$ & " at line " & Erl 'Err was cleared End Sub
![]() |
Use the statement On Error GoTo 0 to clear an installed error handler. This is usually done inside an error handler or after the code that used one. If an error occurs inside an error handler, it isn't handled and the macro stops. See Listing 59 .
![]() |
Private zero% sub ExampleErrorResumeNext On Error Resume Next Print 1/Zero% On Error GoTo 0 ... End Sub
![]() |
Compatibility | Some versions of Visual Basic also support On Error GoTo -1, which functions the same as On Error GoTo 0. |
To specify your own error handler, use On Error GoTo Label. To define a label in OOo Basic, type some text on a line by itself and follow it with a colon . Line labels are no longer required to be unique; they must only be unique within each routine. This allows for consistency in naming error-handling routines rather than creating a unique name for every error handler (see Listing 60 and Figure 18 ). When an error occurs, execution is transferred to the label.
![]() |
Private zero% Private error_s$ Sub ExampleJumpErrorHandler On Error GoTo ErrorHandler JumpErrorl JumpError2 Print 1/Zero% MsgBox error_s, 0, "Jump Error Handler" Exit Sub ErrorHandler: error_s = error_s & "Error in MainJumpErrorHandler at line " & Erl() &_ " : " & Error() & CHR$(10) Resume Next End Sub Sub JumpErrorl REM Causes a jump to the handler in ExampleJumpErrorHandler. REM The main error handler indicates that the error location is REM at the call to JumpErrorl and not in JumpError1. Print 1/zero% error_s = error_s & "Hey, I am in JumpError1" & CHR$(10) End Sub Sub JumpError2 On Error GoTo ErrorHandler Print 1/zero% Exit Sub ErrorHandler: error_s = error_s & "Error in JumpError2 at line " & Erl() &_ " : " & Error() & CHR$(10) Resume Next End Sub
![]() |
A routine can contain several On Error statements. Each On Error statement can treat errors differently. The error handlers in Listing 60 all used Resume Next to ignore the error and continue execution at the line following the error. Using multiple error handlers, it is possible to skip sections of code when an error occurs (see Listing 61 ).
![]() |
On Error GoTo PropertiesDone 'Ignore any errors in this section. a() = getProperties() 'If unable to get properties then DisplayStuff(a(), "Properties") 'an error will prevent getting here. PropertiesDone: On Error GoTo MethodsDone 'Ignore any errors in this section. a() = getMethods() DisplayStuff(a(), "Methods") MethodsDone: On Error Goto 0 'Turn off current error handlers.
![]() |
Bug | The help included with OOo incorrectly states that error handling must occur at the start of a routine. |
When you write an error handler, you must decide how to handle errors. The functions in Table 19 are used to diagnose errors and to display or log error messages. There is also the question of flow control. Here are some error-handling guidelines:
Exit the subroutine or function using Exit Sub or Exit Function.
Let the macro continue to run and ignore the error (see Listing 61).
Use Resume Next to continue running the macro at the line following the error (see Listing 62 and Figure 19 ).
Listing 62: ExampleResumeHandler is found in the ErrorHandler module in this chapter's source code files as SC02.sxw.
![]() |
Sub ExampleResumeHandler Dim s$, z% On Error GoTo Handler1 'Add a message, then resume to Spot1 s = "(0) 1/z = " & 1/z & CHR$(10) 'Divide by zero, so jump to Handler1 Spot1: 'Got here from Handler1 On Error GoTo Handler2 'Handler2 uses resume s = s & "(1) 1/z = "&1/z & CHR$(10) 'Fail the first time, work the second On Error GoTo Handler3 'Handler3 resumes the next line z = 0 'Allow for division by zero again s = s & "(2) 1/z = "&1/z & CHR$(10) 'Fail and call Handler3 MsgBox s, 0, "Resume Handler" Exit Sub Handler1: s = s & "Handler1 called from line " & Erl() & CHR$(10) Resume Spot1 Handler2: s = s & "Handler2 called from line " & Erl() & CHR$(10) z = 1 'Fix the error then do the line again Resume Handler3: s = s & "Handler3 called from line " & Erl() & CHR$(10) Resume Next End Sub
![]() |
Use Resume to run the same statement again. If the problem is not fixed, the error will occur again. This will cause an infinite loop (see Listing 62).
Use Resume LabelName to continue running at some specified location (see Listing 62).
Note | Errors that occur in an error handler are not handled; the macro just stops running. |
When I run a macro and it crashes, I usually understand the sometimes cryptic error messages and recognize how to deal with them. When others run my macros and an error occurs, I usually hear about it because they don't know how to handle it. This is a good indicator that I have not used proper error handling.
Tip | You do not have to write an error handler for each routine. If the current routine doesn't have an error handler, but the routine that called it does, the caller's error handler is called. For example, imagine that sub 1 has an error handler and it calls sub2 that does not. If an error occurs in sub2, the error handler in sub1 is called. |
When you use an error handler, you control how and when a user is notified of an error. Error handlers have uses other than controlling when a user is informed of an error. A properly used error handler can reduce the size of your macro. Consider, for example, the mathematical operations. It is cumbersome to check every mathematical operation before it is used.
If x <> 0 Then y = z / x If x > 0 Then y = Log(x) If i% < 32767 Then i% = i% + 1
Even with my paranoid code checking the parameters, I might still have a numeric overflow. Even worse , nothing is done if an error occurs, and processing continues as normal. Sometimes you can't check anything to avoid the error. For example, the DimArray function returns an invalid empty array. The LBound and UBound functions generate exceptions with these invalid empty arrays. You can write a function to safely generate the LBound and UBound even if an error occurs. Consider the following cases:
The argument is not an array.
In an empty array, UBound < LBound; -1 and 0, for example.
There are no problems if the array is not an invalid empty array.
Should the optional dimension be considered ?
The code in Listing 63 demonstrates a simple error handler that is able to simply ignore errors. The function returns True if the lower bound is less than or equal to the upper bound-in other words, if the array contains data. If an error occurs, either because the argument is not an array or because this is an invalid empty array, the line does not finish running so the assignment never takes place. If the assignment never takes place, the original default Boolean value of False is used. This is the correct answer. Writing a safe upper-bound or lower-bound routine is left as an exercise for the reader-the safe versions will not be required when the fixed Ubound and Lbound functions are released, probably in OOo 2.0.
![]() |
Sub ExampleArrayHasStuff Dim a() , b(3), v Print ArrayHasStuff(a()) 'False, because empty Print ArrayHasStuff(v) 'False, not an array, so use error handler Print ArrayHasStuff(DimArray()) 'False, bad array, so error handler called Print ArrayHasStuff(DimArray(3))'True, this is not empty Print ArrayHasStuff(b()) 'True, this is not empty End Sub Function ArrayHasStuff(v) As Boolean REM default value for a Boolean is False, so the default answer is False REM If an error occurs, then the statement never completes! REM This is a good time to use On Error Resume Next On Error Resume Next ArrayHasStuff = CBool(LBound(v) <= UBound(v)) End Function
![]() |
An error handler can even be interactive. The code in Listing 64 attempts to copy a file that doesn't exist to a location that doesn't exist. Needless to say, an error is generated. An error message is displayed and the user is asked if the copy should be tried again. The user is given an opportunity to correct any errors and continue.
![]() |
Sub ExampleCopyAFile() CopyAFile("/I/do/not/exist.txt", "/neither/do/I.txt") End Sub Sub CopyAFile(Src$, Dest$) On Error GoTo BadCopy: 'Set up the error handler FileCopy(Src$, Dest$) 'Generate the error AllDone: 'If no error, then continue here Exit Sub 'Resume to the AllDone label from the handler BadCopy: 'Display an error dialog Dim rc% 'Ask if should try again rc% = MsgBox("Failed to copy from " & Src$ & " to " & Dest$ & " because: "&_ CHR$(10) & Error() & " at line " & Erl & CHR$(10) &_ "Try again?", (4 OR 32), "Error Copying") If rc% = 6 Then 'Yes Resume 'Try the command again End If If rc% = 7 Then 'No Resume AllDone 'Go to the AllDone label End If End Sub
![]() |