Professional applications need to include error handling to trap unexpected errors. By using a consistent error handler, you can make sure that when crashes occur, the user is properly informed and your program exits gracefully. Basic error handling just hides the default behavior and exits the program. Advanced error handling can include all sorts of features such as saving information about the cause of the error and the environment at the time, attempts to address the problem, and information for the user on what they need to do next.
Verify Error Handling Setting
Before you can use error handling, you need to understand the Error Trapping setting. Visual Basic 6.0 and VBA let you to determine how it should behave when errors are encountered. From the IDE, look under the Tools Options setting.
Figure 2. Setting error trapping/handling options for Visual Basic and VBA
Make sure that error trapping is not set to Break On All Errors. That setting will cause your code to stop on every error, even errors you are properly handling with On Error Resume Next.
Break on Unhandled Errors works in most cases but is problematic while debugging class modules. During development, if Error Trapping is set to Break on Unhandled Errors and an error occurs in a class module, the debugger stops on the line calling the class rather than the offending line in the class. This makes finding and fixing the problem difficult.
I recommend using Break in Class Modules, which stops on the actual crashing line. However, be aware that this does not work if you use raise errors in your classes via the Err.Raise command. This command actually causes an “error” and makes your program stop if Error Trapping is set to Break in Class Modules.
Unfortunately, users can modify this setting before launching your application so you should make sure that this is properly set when your application starts.
Programmatically, the option settings can be viewed and modified by using the Application.GetOption and Application.SetOption methods.
Function GetErrorTrappingOption() As String
Dim strSetting As String
Select Case Application.GetOption("Error Trapping")
Case 0
strSetting = "Break on All Errors"
Case 1
strSetting = "Break in Class Modules"
Case 2
strSetting = "Break on Unhandled Errors"
End Select
GetErrorTrappingOption = strSetting
End Function
Always include code in your startup routines to set the appropriate error handling level.
Sub SafeStart()
Application.SetOption "Error Trapping", 1
End Sub
Make Sure that Every Procedure Has Error Handling
Once the Error Trapping issue is resolved, you need to add error handling to your application. Unfortunately, Visual Basic 6.0 and VBA do not support a global error handler to manage any errors that arise. You actually have to set error handling in every procedure.
Without you explicitly adding error handling, Visual Basic and VBA show the default error message and then allow the user to debug your code, or just crash.
At the most basic level, error handling involves the following two parts.
Error Enabler
The following section invokes the error handler.
If an error occurs in the procedure, the code jumps to the line where the label “PROC_ERR” is defined. For consistency, use the same label name in every procedure.
Error Handler
The following section is where the code goes if an error occurs in the procedure.
PROC_ERR:
MsgBox "Error: (" & Err.Number & ") " & Err.Description, vbCritical
Here you can manage the error and determine what to do next. Examine the error object (Err) to see what occurred. For example, Err.Number is the error number, Err.Description is the error description, and so on.
Disabling Error Handling
In some situations, you need to turn off error handling. For example, you might want to see if a file exists. By looking for it and managing the error if it can’t be found, you can determine whether it exists or not.
Disable error handling with the following code.
Turn Off Error Handling During Development and Testing
Without error handling, if an error is encountered, the debugger automatically stops on the offending line. This is great for debugging and correcting mistakes. However, if error handling exists in the procedure, when an error occurs, rather than stopping on the offending line, the code in the Error Handling section is invoked. This makes debugging much more difficult.
An easy way to avoid this problem is to add a global constant or variable that controls when error handling is active. So instead of using the following code…
…use this code…
If gcfHandleErrors Then On Error GoTo PROC_ERR
…and then define a global constant by using the following code.
Public Const gcfHandleErrors As Boolean = False
Set this constant to False during development, and then to True when you deliver your application. That way, your users get the benefit of the error handling and you can get your work done without it.
Getting Information from the Error Object
When an error occurs, get information about the problem in the Error Object. This object is named Err and contains several properties. The following are the properties that you should check:
- Number The error number, which is useful for testing. A value of zero means no error.
-
Description The built-in description of the error. Sometimes this doesn’t exist and this text “Application-defined or object-defined error” is given.
The error object lets you easily inform the user of the problem. For example, rather than display a simple message that an error occurred, you can specify the exact error number and message to display.
MsgBox "Error: (" & Err.Number & ") " & Err.Description, vbCritical
The user still might not understand it, but it can be very helpful in diagnosing the problem.
Clearing the Error Object
There might be situations where you test for an error number but cannot be sure the Err object doesn’t already contain an error. In such cases, use the Clear method to clear the object.
Alternatively, you can set the error number to zero (Err.Number = 0), but is not as effective as the Clear method since it does not clear the description property.
Using Error Handling for Testing
Error handling can also be used to test a condition. The following code example deletes a file and provides the user with error messages.
Sub DeleteFile(strFileName As String)
Dim lngSaveErr As Long
Dim strSaveErr As String
Const clngErrNoFile As Long = 53
Const clngErrFileInUse As Long = 75
On Error Resume Next
Kill strFileName
lngSaveErr = Err.Number
strSaveErr = Err.Description
On Error GoTo PROC_ERR
Select Case lngSaveErr
Case 0
' No error
Case clngErrNoFile
MsgBox "The file " & strFileName & " does not exist."
Case clngErrFileInUse
MsgBox "The file " & strFileName & " is in use."
Case Else
MsgBox "Unknown error: " & strSaveErr
End Select
PROC_EXIT:
Exit Sub
PROC_ERR:
MsgBox "Error " & Err.Number & " " & Err.Description
Resume PROC_EXIT
End Sub
Notice how the following syntax allows the program to continue (that is, go to the next line) even when a command cannot be executed.
The Kill command triggers an error if the file being deleted doesn’t exist or is locked. We don’t care whether the object exists or not. We just want to delete it if it does. Therefore, the command to ignore the error (Resume Next) is appropriate.
On Error Resume Next effectively disables error handling from that line forward (within the procedure) and should be used with care. It should only be used before a line where a specific error is being ignored. To reset error handling, use the following code.
Alternatively, the following standard error handler has an extra clause to handle situations where error handling is not being used.
If gcfHandleErrors Then
On Error GoTo PROC_ERR
Else
On Error GoTo 0
End If
Notice that a test of the error number is conducted to determine if a specific error occurred. That is, we consider it okay if the object could not be found. However, there are other reasons that might cause a failure to delete an object that exists (for example another user has the object open, insufficient rights to delete it, and so on).
What Error Handling Cannot Trap
Error handling only handles well-behaved errors; that is, errors that trigger an error number in code. Technically, these are the only types of errors you can have, but we all know that Access can crash with an IPF or GPF. Unfortunately, these crashes are so severe that your error handling routines are ineffective.