Part IV: Visual Basic for Applications
Chapter 12. Built-in Functions and Statements
12.5 Handling Errors in Code
I discussed the various types of errors in Chapter 9, but I have scrupulously avoided the question of how to handle runtime errors in code. Indeed, VBA provides several tools for handling errors (OnError, Resume, the Err object, and so on), and we could include an entire chapter on the subject in this book.
Proper error handling is extremely important. Indeed, if you are, or intend to become, a professional application developer, then you should familiarize yourself with
error-handling procedures.
On the other hand, if your intention is to produce Access VBA code for your own
personal use, then the reasons for adding error-handling routines are somewhat mitigated.
When an error occurs within one of your own programs, VBA will stop execution, display an error message, and highlight the offending code. This should enable you to debug the application and fix the problem. (It would be unreasonable to expect another user of your program to debug your code, however.)
Let us undertake a brief discussion of the highlights of error handling. (For more details, may I suggest my book Concepts of Object-Oriented Programming in Visual Basic, published by Springer-Verlag. It has a detailed chapter on error handling.)
12.5.1 The On Error Goto Label Statement
The OnError statement tells VBA what to do when a runtime error occurs. The most common form of the statement is:
On Error GoTo label
where label is a label. For instance, consider the following code:
Sub RecordCt()
On Error GoTo ERR_EXAMPLE
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("Name") MsgBox rs.RecordCount
Exit Sub
ERR_EXAMPLE:
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub
End Sub
The purpose of this procedure is simply to display the number of rows in a table.
However, the database does not happen to have a table called Name. Hence, when VBA encounters the line:
Set rs = CurrentDb.OpenRecordset("Name")
a runtime error will occur.
To deal with this possibility in a friendly manner, we add some error checking. The line:
On Error GoTo ERR_EXAMPLE
tells VBA to move execution to the label ERR_EXAMPLE if an error does occur. The code following this label is called the error-handling code. If an error should occur, the next line executed is the MsgBox line, in which case the dialog box in Figure 12-3 will be displayed. This message gives a description of the error, obtained from the error object, which we discuss in the next section.
Figure 12-3. An error dialog box
It is important to note the:
Exit Sub
line just before the ERR_EXAMPLE label. Without this statement, the error-handling code will always be executed, even when there is no error! Omitting this line is a common mistake. Note also that labels always end with a colon.
The process of adding error-handling code to a procedure is sometimes referred to as error-trapping.
12.5.2 Handling Errors in the Calling Procedure
Consider the following version of the RecordCt function:
Function RecordCt(TableName As String) As Integer On Error GoTo ERR_EXAMPLE
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset(TableName) RecordCt = rs.RecordCount
rs.Close Exit Function
ERR_EXAMPLE:
RecordCt = -1 ' Indicates error rs.Close
Exit Function
End Function
In this case, if there is an error, the function will simply return the value -1, rather than displaying a message box. This behavior is better than that of the previous version, because in this case the calling procedure can decide what to do.
Here is a procedure that calls RecordCt:
Sub Main()
On Error GoTo Err_Main
Dim rc As Long
rc = RecordCt("Object")
If rc = -1 Then
' code here to handle error Else
' code here for no error End If
Exit Sub
Err_Main:
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub
End Sub
Note that a return value of -1 is not perceived by VBA as an error at all, so we need to handle the error using code such as:
If rc = -1 Then
12.5.3 The Calls Stack
What happens if we do not trap errors in a procedure?
If the procedure was not called by another procedure, but rather was called directly by the user, or if the procedure is an event procedure—that is, code that executes in response to a user manipulating a control on a form (for instance, clicking on a command
button)—then VBA just displays an error message and halts the program.
However, if the procedure in which the error occurred was called by another procedure, then VBA passes the error to the calling procedure, just as though the calling procedure had caused the error.
To illustrate this, consider the following procedures:
Function RecordCt2(TableName As String) As Integer Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset(TableName) RecordCt2 = rs.RecordCount
rs.Close End Function ' --- Sub Main2()
On Error GoTo Err_Main Dim rc As Long
rc = RecordCt2("Objects")
' More code here Exit Sub
Err_Main:
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub
End Sub
The RecordCt2 function has no error-trapping code. If Main2 calls RecordCt2 with a bad table name, the error in RecordCt2 will be passed to Main2, whose error-trapping code will execute. Thus, we will get an error message from Main2. (This may be just fine.) More generally, if ProcedureA calls ProcedureB, which calls ProcedureC, and so on, then an error in any one procedure will be passed up the call stack (list of procedures in reverse order of execution) until a procedure with error-handling code is encountered. If none is encountered, then VBA will issue its own error message and terminate the program.
Incidentally, you can view the call stack while in break mode by choosing Call Stack from the View menu.
12.5.4 The Error Object
The error object, denoted by Err, belongs to the VBA object model. The most important properties of this object are:
Number
The VBA error number Source
The name of the current VBA project Description
A description of the error Thus, for instance, the line:
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical
displays the error number and its description.
The Err object has a Clear method:
Err.Clear
that will clear all of the properties of the Err object, setting its Number property to 0
(which indicates the absence of an error).
12.5.5 The On Error GoTo 0 Statement
The statement:
On Error GoTo 0
turns off any previous OnErrorGoTolabel statements. Any error occurring subsequently will be handled by VBA in its own inimitable way.
12.5.6 The On Error Resume Next Statement
The syntax:
On Error Resume Next
tells VBA to continue executing the code immediately following the line that caused the error. There are two important uses for this form of OnError. The first is to cause VBA to ignore an error. For instance, the code:
Sub example()
On Error Resume Next MsgBox rs.RecordCount End Sub
will report the record count when rs is a valid recordset and do nothing otherwise.
Another important use for the OnErrorResumeNext syntax is for in-line error checking, where we check for errors immediately following the line that may have caused an error.
For instance, another way to handle errors in the RecordCount property is as follows:
Sub example()
On Error Resume Next MsgBox rs.RecordCount
If Err.Number <> 0 Then
' code to handle error here End If
End Sub
12.5.7 The Resume Statement
It is also possible to include the Resume statement in the error-handling portion of the code. This will cause VBA to resume execution at the line that follows the one that caused the error. Thus, the previous code is equivalent to the following:
Sub example()
On Error GoTo ERR_EXAMPLE MsgBox rs.RecordCount
' An error will cause execution to resume here after ' displaying an error message
Exit Sub ERR_EXAMPLE:
MsgBox Err.Description, vbCritical Resume Next
End Sub
There are three variations on the Resume statement:
• Resume
• ResumeNext
• ResumeALabel
The first version will cause VBA to resume with the line that caused the error. This is useful if your error-handling code actually repairs the error condition and you want the line that caused the original error to be executed again.
To illustrate, if the procedure in Example 12-2 encounters an error, it branches to an error handler. This handler checks for error number 3078, which is the "Can't find table" error.
If this is the error, then the procedure displays a dialog box asking for a new table name.
If the user enters a new name, the Resume statement is executed, and so the line:
Set rs = CurrentDb.OpenRecordset(TableName)
is repeated. Note that it is vital to give the user a way out, however. This is done by letting the user leave the dialog box blank. (Incidentally, I got the correct error number 3078 by simulating the error and reading the resulting error-message dialog box.) Example 12-2. Error handling with the Resume statement
Function RecordCt3(TableName As String) As Integer On Error GoTo ERR_EXAMPLE
Dim rs As Recordset
RecordCt = rs.RecordCount
rs.Close Exit Function
ERR_EXAMPLE:
If Err.Number = 3078 Then ' Can't find table
sTable = InputBox("Can't find table " & sTable & _
". Please enter table name again or leave blank to end.") If sTable = "" Then
rs.Close
TableName = sTable Exit Function Else
Resume End If Else
' Unknown error
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical rs.Close
Exit Function End If
End Function
The third variation:
Resume ALabel
causes VBA to resume execution at the line labeled ALabel.