• Aucun résultat trouvé

Handling Errors in Code

Dans le document Part I: Database Design (Page 190-198)

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.

Dans le document Part I: Database Design (Page 190-198)