To Err Is Human (So Program Accordingly), Part 2 (Harry's Code Class Newsletter)
26 May, 2008Harry gets specific about how to include error catching in AutoLISP and VBA programs.
In the April edition of Harry's Code Class, we looked at some concepts behind error prevention in AutoCAD customization. This month we forge ahead, discussing the basics of error catching inside AutoCAD's VBA (Visual Basic for Applications) and Visual LISP programming environments and examining some of the specific error-related tools of the craft.
An error catch -- which is a general term -- can take on different appearances in your code based on the programming language as well as the type of error being caught. The basic idea is to set a trap for the unexpected or invalid. When the trap is sprung, you have some code in place to handle the problem.
Error-catching and processing tools are well documented in the VBA and Visual LISP online Help systems, so I won't reiterate that information here except to point you toward some of the tastier tidbits. From there, you can start your own experimentation in error catching.
VBA Error Catch
The BASIC programming language has a very powerful error-processing tool known simply as the "ON ERROR" statement to most programmers. This statement, when added to your code, advises the program how to act in the event of an error. The VBA implementation of BASIC supports ON ERROR, meaning that if you know BASIC, you most likely already know how to set and use the error-trapping system. In VBA programming, error processing is handled through an error object, which starts when the ON ERROR statement is encountered in a program function or subroutine.
When the error trap is turned on through the ON ERROR statement, you can access the ERR programming object to learn whether an error took place and if so, which error. For example, ERR.NUMBER will retrieve the error code number (0 if there is no current error) so you can test the value to see if an error occurred. Note that the ERR object must be established in the code through the ON ERROR statement inside every function or subroutine where an error might occur (because the scope of the ERR object is only the function or subroutine where the error initialized). To initialize the ERR object, use an ON ERROR statement. The most common variation is ON ERROR RESUME NEXT. In VBA, this tells the system to continue to the next statement in your code if it encounters a run-time error.
Caution: Unless you know exactly which errors can occur and where, including the ON ERROR RESUME NEXT statement could be dangerous because it might allow an error to slip by unnoticed. A test of the error object needs to exist after every possible source of an error once the RESUME NEXT option is active. Test the ERR object for a possible error situation by checking the value of the NUMBER property. This property will be zero if there were no errors; otherwise, the value will be an error code.
You can learn more about the error code from the ERR object by getting the DESCRIPTION property. Sometimes this is an excellent way to report problems to the user in the language of the VBA system so that the programmer can begin to find a remedy. For example, the description might be File Not Found, and if only one file is involved in the process, then it will be pretty obvious where the problem exists.
There are times when you might use the error object for other purposes. For example, error tests for missing files or undefined collections are often used to determine whether a new file or collection needs to be created. You will frequently see error tests for the existence of a pick set in the current drawing's pick set collection. If the set exists, the error code is zero. A nonzero error code indicates that a new pick set needs to be created.
Other ON ERROR options are detailed in the online Help, including examples of their use. You can also clear the error setting, turn it off, and change the way it is set up to work within a single subroutine or function.
Remember that the VBA error handler works only in the scope of the current function or subroutine. When another function or subroutine is called inside the current one, a new ERR object should be created for that function or subroutine that is active only while it is running. After control is returned to the calling function or subroutine, the original error handler resumes control.
Visual LISP Error Catch
Visual LISP has two options for error catching. The original AutoLISP error catch is global, applying to the entire program that is running. The other applies at the single-expression level, taking precedence over the global error catch when both are in play.
Global error catching is the most commonly used, has been around the longest, requires less coding -- and is not all that flexible. When the symbol *ERROR* (including the asterisks) is defined as a function of one parameter, control is transferred to this function when an error is detected. The single argument against using the *ERROR* function is that is supplies the description of the error as a string, and you have no idea as to which function caused the error.
You can set up your own, improved indicator in your programs using a global variable. Set that variable to different values as your program progresses. When an error occurs you can test that value and see where in your program the error took place.
The primary use of the function *ERROR* is to clean up your mess. You can run AutoCAD commands inside the *ERROR* function; however, be careful to check which state you left AutoCAD in before trying anything. You can reset system variables and reset your own global variable symbols in the *ERROR* function. The problem is that you cannot cleanly restart your application; once the *ERROR* function finishes, control is returned to the AutoCAD command prompt.
The Visual LISP error-testing function VL-CATCH-ALL-APPLY takes error trapping to the individual expression level. While the *ERROR* function trap is not focused on anything but recovery, the VL-CATCH-ALL-APPLY function may seem too specific. The reason is that expression-level error trapping is mostly used in conjunction with object references in Visual LISP. Even so, you can try it anywhere your program might encounter a problem.
The result from VL-CATCH-ALL-APPLY is an error object reference and must be saved to a symbol using SETQ. This is so you can test it and learn if an error occurred. When an error is the result of the VL-CATCH-ALL-APPLY, the symbol saved will be set to an error object. If there is no error in the catch, the result will be nil. Error objects contain the description of the error -- as both a code number and a string, like VBA -- and because you know the exact expression and where it was caught, you can take the appropriate action. VL-CATCH-ALL-APPLY can be pretty scary when you first look at it. The good news is that most of the time *ERROR* will facilitate an elegant exit from your programs.
Error catching is pretty easy in VBA and Visual LISP. You just have to jump in and use it. With practice, you will become more comfortable with the various options and how to best employ them in your applications. We have only scratched the surface.
Keep on programmin'.
Related Resources
Check out these resources (repeated from Part 1 of this series) for more information and examples of error handing:
- Visual LISP and Errors: Using *ERROR*, from the AfraLisp Web site.
- Error Trapping (in VBA): From the AfraLisp Web site.
- Handling Automation Errors in Visual LISP: Advice from Kean Walmsley, senior manager of Developer Technical Services at Autodesk.