|  | Click Here!

Search the site:
EXPERT SEARCH

| Programming Languages
| Databases
| Security
| Web Services
| Network Services
| Middleware
| Components
| Operating Systems
| User Interfaces
| Groupware & Collaboration
| Content Management
| Productivity Applications
| Hardware
| Fun & Games

EarthWeb sites
Crossnodes
Datamation
Developer.com
DICE
EarthWeb.com
EarthWeb Direct
ERP Hub
Gamelan
GoCertify.com
HTMLGoodies
Intranet Journal
IT Knowledge
IT Library
JavaGoodies
JARS
JavaScripts.com
open source IT
RoadCoders
Y2K Info

#

- Day 14
  - Error Handling in Visual Basic 5
    - Error Handling in General
    - Error Handling in Visual Basic
    - The Built-In Visual Basic Error Objects
      - Working with the Err Object
      - Working with the Error Object and the Errors Collection
    - Listing 14.1. Enumerating the Errors collection.
    - Creating Your Own Error Handlers
      - Creating a Simple Error Handler
    - Listing 14.2. Writing a simple error handler.
      - Figure 14.1.
    - Handling Cascading Errors
    - Listing 14.3. Coding the CreateErr routine.
    - Listing 14.4. Coding the cmdCascadeErr routine.
      - Figure 14.2.
      - Figure 14.3.
    - Using Resume to Exit the Error Handler
    - Listing 14.5. Using the Resume keyword.
      - Figure 14.4.
    - Using Resume Next to Exit the Error Handler
    - Listing 14.6. Using the Resume Next keywords.
      - Using Resume label to Exit an Error Handler
        - Figure 14.5.
    - Listing 14.7. Using the Resume label keywords.
      - Figure 14.6.
    - Using the Exit or End Method to Exit an Error Handler
    - Listing 14.8. Using the End keyword.
      - Figure 14.7.
    - Using the Err.Raise Method to Create Your Own Error Conditions
    - Types of Errors
      - General File Errors
    - Listing 14.9. Adding code to the cmdControl_Click event.
      - Figure 14.8.
      - Figure 14.9.
    - Physical Media Errors
    - Listing 14.10. Trapping media errors.
      - Figure 14.10.
    - Program Code Errors
      - Database Errors with the Data Control
        - Figure 14.11.
        - Figure 14.12.
    - Listing 14.11. Coding the Data1_Error event.
      - Figure 14.13.
      - Figure 14.14.
    - Database Errors with Microsoft Data Access Objects
    - Listing 14.12. Adding code to the Form_Load event.
      - Figure 14.15.
    - Creating Your Error Handler OLE Server
      - Building the errHandler Class
    - Listing 14.13. Adding code to the Declaration section of the errHandler class.
    - Listing 14.14. Adding code to the general declaration section of the modErrHandler BAS module.
    - Listing 14.15. Adding code to the Class_Initialize event of the errHandler class.
    - Listing 14.16. Defining the Property Let and Property Get routines for the DefaultAction property.
    - Listing 14.17. Coding the Property Let and Property Get routines for the DefaultType property.
    - Listing 14.18. Coding the errHandler function.
    - Listing 14.19. Adding the errMsg support function.
      - Coding the frmErrDialog Form
        - Figure 14.16.
    - Listing 14.20. Coding the Form_Load event of the frmErrDialog form.
    - Listing 14.21. Coding the cmdBtn_Click event.
      - Testing the ErrHandler Object Library
        - Figure 14.17.
        - Figure 14.18.
    - Listing 14.22. Coding the Form_Load event.
    - Listing 14.23. Trapping the data control error.
      - Figure 14.19.
    - Listing 14.24. Creating a divide-by-zero error in code.
      - Figure 14.20.
    - Adding Error Logs to the Error Handler
    - Listing 14.25. Coding the Property Let and Property Get statements for the LogFileName and WriteLogFlag properties.
    - Listing 14.26. Coding the LogError method.
    - Listing 14.27. Updating the errHandler method.
    - Listing 14.28. Modifying the Form_Load event to include error logging.
    - Listing 14.29. Contents of the errTest.log file.
      - Adding a Module Trace to the Error Handler
    - Listing 14.30. Declaring the TraceObject variables.
    - Listing 14.31. Coding the Property Let and Property Get statements for the TraceFile and TraceLog properties.
    - Listing 14.32. Coding the Class_Initialize event.
    - Listing 14.33. Coding the Push and Pop methods of the TraceObject.
    - Listing 14.34. Coding the List and Clear methods of the TraceObject.
    - Listing 14.35. Coding the Dump method of the TraceObject.
    - Listing 14.36. Coding the Show method of the TraceObject.
    - Listing 14.37. Updating the Form_Load event to include module tracing.
    - Listing 14.38. Updating the Command1_click event to use module tracing.
      - Figure 14.21.
    - Other Error Handler Options
    - Summary
    - Quiz
    - Exercises

## Day 14

## Error Handling in Visual Basic 5

Today's lesson covers a very important aspect of programming--handling runtime errors. Although you should always work to make sure your program can anticipate any problems that might occur while a user is running your software, you can't account for every possibility. That's why every good program should have a solid error-handling system.

Today you learn just what an error handler is and why error handlers are so important. You also learn about some of the inner workings of Visual Basic and how they affect error handling.

You learn about the difference between local error-handling methods and global error-handling methods. You also learn the advantages and disadvantages of each method. You see the various types of errors your program is likely to encounter and get some guidelines on how to handle each type of error.

You also learn about the Err object and the Error collection and how to use these objects to improve the accuracy of error reporting within your application. You also learn how to use the `Raise` method of the Err object to flag errors from within custom controls or OLE Server objects.

You also learn how to create error logs to keep track of errors that occur in your program. You learn how to create a trace log to analyze your programs. And you learn how you can write your programs to turn these features on or off without having to rewrite program code.

Finally, you build an OLE Server DLL that contains an improved error handler, an error logging facility, and a module trace routine. You can use this new OLE Server in all your future VBA-compliant programming projects.

### Error Handling in General

Error handling is an essential part of any program. No program is complete unless it has good error handling. It is important to write your programs in a way that reduces the chance that errors occur, but you won't be able to think of everything. Errors do happen! Well-designed programs don't necessarily have fewer errors; they just handle them better.

Writing error handlers is not difficult. In fact, you can add consistent error handling to your program by adding only a few lines of code to each module. The difficult part of writing good error handlers is knowing what to expect and how to handle the unexpected. You learn how to do both in today's lesson.

Adding error handling to your program makes your program seem much more polished and friendly to your users. Nothing is more annoying--or frightening--to a user than seeing the screen freeze up, hearing a startling beep, or watching the program (and any file the user had been working on) suddenly disappear from the screen. This only needs to happen a few times before the user vows never to use your program again.

Polite error messages, recovery routines that allow users to fix their own mistakes or correct hardware problems, and opportunities for the user to save any open files before the program halts because of errors are all essential parts of a good error-handling strategy.

### Error Handling in Visual Basic

Writing error handlers in Visual Basic is a bit trickier than in most PC languages. There are several reasons for this. First, Visual Basic follows an event-driven language model, rather than a procedure-driven model like most PC languages. Second, Visual Basic uses a call stack method that isolates local variables. This means that when you exit the routine, you can lose track of the values of internal variables, which can make resuming execution after error handling difficult. Third, in Visual Basic all errors are local. If an error occurs, it's best to handle it within the routine in which the error occurred, which means you have to write a short error handler for each routine in your Visual Basic program.

> NOTE: Technically, Visual Basic does allow the use of a global error handler. However, after Visual Basic travels up the procedure stack to locate the error handler, it can't travel back down the stack to resume execution after the error has been corrected. (This is typical of most object-oriented languages.) For this reason, we highly recommend using local error handlers in your Visual Basic programs.

### The Built-In Visual Basic Error Objects

Visual Basic 5 has two built-in objects that can be used to track and report errors at runtime. The Err object is a built-in object that exists in all Visual Basic programs. This object contains several properties and two methods. Each time an error occurs in the program, the Err object properties are filled with information you can use within your program.

The second built-in object that helps in tracking errors is the Error object, and its associated Errors collection. These are available to any Visual Basic 5 program that has loaded one of the Microsoft data access object libraries. The Error object is a child object of the DBEngine. You can use the Error object to get additional details on the nature of the database errors that occur in a program.

> WARNING: The Error object is only available if you have loaded a Microsoft data-access object library. If you attempt to access the Error object from a Visual Basic program that does not have a Microsoft data access object library loaded, you receive an error.

The advantage of the Error object over the Err object is that the Error object contains more information about the database-related errors than the Err object. In some cases, back-end database servers return several error messages to your Visual Basic application. The Err object only reports the last error received from the back-end server. However, the Errors collection can report all of the errors received. For this reason, it is always a good idea to use the Error object when you are working with Visual Basic database applications.

#### Working with the Err Object

Visual Basic 5 has a built-in object called the Err object. This object holds all the information about the most recent error that occurred within the running application space.

> WARNING: There is a bit of confusion regarding the `Err` keyword in Visual Basic. Visual Basic 5 still supports the outdated `Err` and `Error` functions, but we do not advise you to use them in your programs. In some rare cases, the values reported by the `Err` and `Error` functions are not the same as those reported by the Err object. Throughout this book, when we mention "Err" we are referring to the Err object, not the `Err` function.

The Err object has several important properties. Table 14.1 shows these properties and explains their use.
Table 14.1. The properties of the Err object.

Property | Type | Value
Number | Long | The actual internal error number returned by Visual Basic.
Source | String | Name of the current Visual Basic file in which the error occurred. This could be an EXE, DLL, or OCX file.
Description | String | A string corresponding to the internal error number returned in the Number property, if this string exists. If the string doesn't exist, Description contains "Application-defined or object-defined error."
HelpFile | String | The fully qualified drive, path, and filename of the Help file. This help file can be called to support the reported errors.
HelpContext | Long | The Help file context (topic) ID in the help file indicated by the HelpFile property.
LastDLLError | Long | The error code for the last call to a dynamic-link library (DLL). This is available only on 32-bit Microsoft platforms.

When an error occurs in your Visual Basic program, the Err object properties are populated with the details of the error. You can inspect these values during your program execution and, if possible, use Visual Basic code to correct the error and continue the program.

For example, once an error occurs, you can inspect the properties of the object using the following code:

    Msgbox "<" & CStr(Err.Number) & "> " & Err.Description & "[" & Err.Source & "]"

Once the error occurs and the Err object properties are updated, the Err object values do not change until another error is reported or the error-handling system is re-initialized.

> NOTE: The error handling system is re-initialized each time a procedure exit or end occurs, or when the special error-handling keywords `Resume` or `On Error` are executed. You learn more about these keywords later in this chapter.

If the error that was reported has an associated help file and help context ID, these properties are also filled in. You can use the HelpFile and HelpContext properties of the Err object to display an online help topic to explain the error condition to the user.

> TIP: Although help file composition and the construction of context-sensitive help is beyond the scope of a database book, Appendix B at the back of this book shows you how to create help files and how to link them to your Visual Basic programs.

If your application has called a dynamic-link library (DLL), you may be able to use the LastDLLError property of the Err object to get additional information about an error that occurred in a DLL. This property is only available on the 32-bit platform and may not be supported by the DLL you are calling.

#### Working with the Error Object and the Errors Collection

In addition to the built-in Err object, Visual Basic 5 also has a built-in Error object for database errors. This object is a child object of the DBEngine object. For this reason, you can only access the Error object if you have loaded a Microsoft data access object library (select Project | References from the main menu).

The primary advantage of the Error object is that it can report additional error information not included in the standard Err object mentioned earlier in this chapter. Many times, your database application needs to depend on external processes such as ODBC data connections or OLE Server modules. When an error occurs in these external processes, they may report more than one error code back to your Visual Basic application.

The Err object is only able to remember the most recent error reported. However, the Error object (and its associated Errors collection) can remember all of the errors reported by external processes. That is why it is a good idea to use the Error object for reporting errors in all your Visual Basic database programs.

The properties of the Microsoft data access Error object are almost identical to the properties of the Visual Basic Err object (see the discussion earlier in this chapter). The only difference is that the Error object does not have the optional LastDLLError property. Therefore, the calling convention for the Error object is basically the same as that for the Err object:

    Msgbox "<" & CStr(Error.Number) & "> " & Error.Description & "[" & Error.Source Â& "]"

While the Error and Err objects are quite similar, there is one major difference worth noting. While the Err object stands alone, the Microsoft data access Error object belongs to the Errors collection. This is very important when dealing with back-end database servers, especially when your Visual Basic program is connected to databases through the Open Database Connectivity (ODBC) interface. When an error occurs during an ODBC transaction, the Err object always returns the same error message, `ODBC failed`. However, the Errors collection often contains more than one error message, which can tell you a great deal more about the nature of the problem. You can retrieve all the error information by enumerating all the Error objects in the Errors collection. The code in Listing 14.1 shows how that can be done.

### Listing 14.1. Enumerating the Errors collection.

    Dim objTempErr as Object
    Dim strMsg as String

    For Each objTempErr In Errors
       StrMsg = "<" & CStr(objTempErr.Number) & "> "
       StrMsg = strMsg & objTempErr.Description
       StrMsg = strMsg & " in [" & objTempErr.Source & "]" & vbCrLf
    Next
    Msgbox strMsg

The code in Listing 14.1 creates a single line of text (strMsg) that contains all the error messages reported by the back-end database server. You learn more about using both the Err and Error objects in the next section of the chapter.

### Creating Your Own Error Handlers

Before getting into the details of using the Err and Error objects in your Visual Basic programs, let's take a look at a basic error handler in Visual Basic. Error handlers in Visual Basic have three main parts:

- The `On Error Goto` statement

- The error handler code

- The exit statement

The `On Error Goto` statement appears at the beginning of the `Sub` or `Function`. This is the line that tells Visual Basic what to do when an error occurs, as in the following example:

    On Error Goto LocalErrHandler

In the preceding code line, every time an error occurs in this `Sub` or `Function`, the program immediately jumps to the `LocalErrHandler` label in the routine and executes the error handler code. The error handler code can be as simple or as complex as needed to handle the error. A very simple error handler would just report the error number and error message, like this:

    LocalErrHandler:
       MsgBox CStr(Err.Number) & " - " & Err.Description

In the preceding code example, as soon as the error occurs, Visual Basic reports the error number (Err.Number) and the error message (Err.Description) in a message box.

The third, and final, part of a Visual Basic error handler is the exit statement. This is the line that tells Visual Basic where to go after the error handler is done with its work. There are four different ways to exit an error handler routine:

- Use the `Resume` keyword to return to the location in the program that caused the error in order to re-execute the same instruction.

- Use the `Resume Next` keywords to resume execution at the Visual Basic code line immediately following the line that caused the error.

- Use the `Resume label` keywords to resume execution at a specified location within the routine that caused the error. This location could be anywhere within the routine--before or after the line that caused the error.

- Use the `Exit Sub` or `Exit Function` keywords to immediately exit the routine in which the error occurred.

Which exit method you use depends on the type of error that occurred and the error handling strategy you employ throughout your program. Error types and error handling strategies are covered later in this chapter.

Now that you have the basics of error handling, you can write some error-handling routines.

#### Creating a Simple Error Handler

To start, let's write a simple error-handling routine to illustrate how Visual Basic behaves when errors occur. Start up a new Standard EXE project in Visual Basic 5. Add a single command button to the default form. Set its Name property to cmdSimpleErr and its Caption Property to Simple. Now add the code in Listing 14.2 to support the command button.

### Listing 14.2. Writing a simple error handler.

    Private Sub cmdSimpleErr_Click()
        `
        ` a simple error handler
        `
        On Error GoTo LocalErr  ` turn on error handling
        `
        Dim intValue As Integer ` declare integer
        Dim strMsg As String    ` declare string
        intValue = 10000000     ` create overflow error
        GoTo LocalExit          ` exit if no error
        `
        ` local error handler
    LocalErr:
       strMsg = CStr(Err.Number) & " - " & Err.Description ` make message
       MsgBox strMsg, vbCritical, "cmdSimpleErr_Click"  ` show message
       Resume Next  ` continue on
       `
       ` routine exit
    LocalExit:
       `
    End Sub

Save the form as `BASICERR.FRM`, and save the project as `BASICERR.VBP`. Then execute the program and click the command button. You see the error message displayed on the screen (see Figure 14.1).

The example in Listing 14.2 exhibits all the parts of a good error handler. The first line in the routine tells Visual Basic what to do in case of an error. Notice that the name for the error-handling code is given as LocalErr. (Every local error handler written in this book is called LocalErr.) Next, the routine declares an integer variable and then purposely loads that variable with an illegal value. This causes the error routine to kick in.

The error routine is very simple. It is a message that contains the error number and the associated text message. The routine then displays that message along with the warning symbol and the name of the routine that is reporting the error.
Figure 14.1. Displaying the results of a simple error handler.
The next line tells Visual Basic what to do after the error is handled. In this case, Visual Basic resumes execution with the line of program code that immediately follows the line that caused the error (`Resume` at the `Next` line).

When Visual Basic resumes execution, the routine hits the line that tells Visual Basic to go to the exit routine (`Goto LocalExit`). Notice again the naming convention for the exit routine. All exit jump labels in this book are called LocalExit.

#### Handling Cascading Errors

What happens if you get an error within your error routine? Although it isn't fun to think about, it can happen. When an error occurs inside the error-handling routine, Visual Basic looks for the next declared error routine. This would be an error routine started in the previous calling routine using the `On Error Goto label` statement. If no error routine is available, Visual Basic halts the program with a fatal error.

As an example, let's add a new button to the BASICERR project and create a cascading error condition. Set the button's Name property to cmdCascadeErr and its Caption property to Cascade. First, create a new `Sub` procedure called `CreateErr`. Then enter the code from List-ing 14.3.

### Listing 14.3. Coding the CreateErr routine.

    Public Sub CreateErr()
        `
        ` create an internal error
        `
        On Error GoTo LocalErr
        `
        Dim strMsg As String
        Dim intValue As Integer
        `
        intValue = 900000 ` create an error
        GoTo LocalExit ` all done
        `
    LocalErr:
        strMsg = CStr(Err.Number) & " - " & Err.Description
        MsgBox strMsg, vbCritical, "CreateErr"
        `
        Open "junk.txt" For Input As 1 ` create another error
        Resume Next
        `
    LocalExit:
        `
    End Sub

Notice that this routine is quite similar to the code from Listing 14.2. The biggest difference is in the lines of code in the error-handling portion of the subroutine. Notice that Visual Basic attempts to open a text file for input. Since this file does not currently exist, this action causes an error.

Now add the code from Listing 14.4 to the `cmdCascadeErr_Click` event. This is the code that calls the `CreateErr` routine.

### Listing 14.4. Coding the cmdCascadeErr routine.

    Private Sub cmdCascadeErr_Click()
        `
        ` create an error cascade
        `
        On Error GoTo LocalErr
        `
        Dim strMsg As String
        `
        CreateErr ` call another routine
        GoTo LocalExit ` all done
        `
    LocalErr:
        strMsg = CStr(Err.Number) & " - " & Err.Description
        MsgBox strMsg, vbCritical, "cmdCascadeErr"
        Resume Next
        `
    LocalExit:
        `
    End Sub

Save the program and run it to see the results. When you first click the command button, you see the error message that announces the overflow error. Notice that the title of the message box indicates that the error is being reported by the `CreateErr` routine (see Figure 14.2).
Figure 14.2. Reporting the error from `CreateErr`.
When you click the OK button in the message box, you see another error message. This one reports an `Error 53-File not found` message, which occurred when `CreateErr` tried to open the nonexistent file (see Figure 14.3).
Figure 14.3. Reporting the `File not` `found` error.
Here's the important point. Notice that the second error message box tells you that the error is being reported from the `cmdCascadeErr` routine--even though the error occurred in the `CreateErr` routine! The error that occurred in the `CreateErr` error-handling routine could not be handled locally, and Visual Basic searched upward in the call stack to find the next available error handler to invoke. This action by Visual Basic can be a blessing and a curse. It's good to know that Visual Basic uses the next available error-handling routine when things like this happen, but it's also likely to cause confusion for you and your users if you are not careful. For all you can tell in this example, an error occurred in `cmdCascadeErr`. You must keep this in mind when you are debugging Visual Basic error reports.

#### Using Resume to Exit the Error Handler

The simplest way to exit an error handler is to use the `Resume` method. When you exit an error handler with the `Resume` keyword, Visual Basic returns to the line of code that caused the error and attempts to run that line again. The `Resume` keyword is useful when you encounter an error that the user can easily correct, such as attempting to read a disk drive when the user forgot to insert a diskette or close the drive door. You can use the `Resume` keyword whenever you are confident that the situation that caused the error has been remedied, and you want to retry the action that caused the error.

Let's modify the BASICERR project by adding a new button to the project. Set its Name property to cmdResumeErr and its Caption property to Resume. Now add the Visual Basic code in Listing 14.5 to support the new button's `Click` event.

### Listing 14.5. Using the Resume keyword.

    Private Sub cmdResumeErr_Click()
        `
        ` show resume keyword
        `
        On Error GoTo LocalErr
        `
        Dim intValue As Integer
        Dim strMsg As String
        `
        intValue = InputBox("Enter an integer:")
        GoTo LocalExit
        `
    LocalErr:
        strMsg = CStr(Err.Number) & " - " & Err.Description
        MsgBox strMsg, vbCritical, "cmdResumeErr"
        Resume ` try it again
        `
    LocalExit:
        `
    End Sub

Save and run the project. When you press the Resume button, you are prompted to enter an integer value. If you press the Cancel button or the OK button without entering data (or if you enter a value that is greater than 32,767), you invoke the error handler and receive an error message from Visual Basic (see Figure 14.4).
Figure 14.4. Reporting an error message from the input box.
When you click the OK button, Visual Basic redisplays the input prompt and waits for your reply. If you enter another invalid value, you see the error message, and then you see the prompt again. This is the `Resume` exit method in action. You can't get beyond this screen until you enter a valid value.

This can be very frustrating for users. What if they don't know what value to enter here? Are they stuck in this terrible error handler forever? Whenever you use the `Resume` keyword, you should give your users an option to ignore the error and move on or cancel the action completely. Those options are covered next.

#### Using Resume Next to Exit the Error Handler

Using the `Resume Next` method to exit an error handler allows your user to get past a problem spot in the program as if no error has occurred. This is useful when you use code within the error handler to fix the problem, or when you think the program can go on even though an error has been reported.

Deciding whether to continue the program even though an error has been reported is sometimes a tough call. It is usually not a good idea to assume that your program will work fine even though an error is reported. This is especially true if the error that occurs is one related to physical devices (missing diskette, lost communications connection, and so on) or file errors (missing, corrupted, or locked data files, and so on). The `Resume Next` keywords are usually used in error-handling routines that fix any reported error before continuing.

To illustrate the use of `Resume Next`, add a new command button to the project. Set its Name property to cmdResumeNext and its Caption property to Next. Now enter the code in Listing 14.6 behind the button's `Click` event.

### Listing 14.6. Using the Resume Next keywords.

    Private Sub cmdResumeNextErr_Click()
        `
        ` show use of resume next
        `
        On Error GoTo LocalErr
        `
        Dim intValue As Integer
        Dim strMsg As String
        Dim lngReturn As Long
        `
        intValue = InputBox("Enter a valid Integer")
        MsgBox "intValue has been set to " + CStr(intValue)
        GoTo LocalExit
        `
    LocalErr:
        If Err.Number = 6 Then ` was it an overflow error?
            strMsg = "You have entered an invalid integer value." & vbCrLf
            strMsg = strMsg & "The program will now set the value to 0 for you." & ÂvbCrLf
            strMsg = strMsg & "Select YES to set the value to 0 and continue." & ÂvbCrLf
            strMsg = strMsg & "Select NO to return to enter a new value."
            `
            lngReturn = MsgBox(strMsg, vbCritical + vbYesNo, "cmdResumeNextErr")
            If lngReturn = vbYes Then
                intValue = 0
                Resume Next
            Else
                Resume
            End If
        Else ` must have been some other error(!)
            strMsg = CStr(Err.Number) & " - " & Err.Description
            MsgBox strMsg, vbCritical, "cmdResumeNext"
            Resume
        End If
        `
    LocalExit:
        `
    End Sub

In Listing 14.6, you added a section of code to the error handler that tests for the anticipated overflow error. You explain the options to the user and then give the user a choice of how to proceed. This is a good general model for error handling that involves user interaction. Tell the user the problem, explain the options, and let the user decide how to go forward.

Notice, also, that this routine includes a general error trap for those cases when the error is not caused by an integer overflow. Even when you think you have covered all the possible error conditions, you should always include a general error trap.

Save and run this project. When you press the Next command button and enter an invalid value (that is, any number greater than 32,767), you see the error message that explains your options (see Figure 14.5).

#### Using Resume label to Exit an Error Handler

There are times when you need your program to return to another spot within the routine in order to fix an error that occurs. For example, if you ask the user to enter two numbers that you use to perform a division operation, and it results in a divide-by-zero error, you want to ask the user to enter both numbers again. You might not be able to just use the `Resume` statement after you handle the error.
Figure 14.5. An error message that asks for user input.
When you need to force the program to return to a specific point in the routine, you can use the `Resume label` exit method. The `Resume label` method enables you to return to any place within the current procedure. You can't use `Resume label` to jump to another `Sub` or `Function` within the project.

Now let's modify the BASICERR project to include an example of `Resume label`. Add a new command button to the project. Set its Name property to cmdResumeLabelErr and its Caption property to Resume Label. Now, place the code in Listing 14.7 behind the `Click` event.

### Listing 14.7. Using the Resume label keywords.

    Private Sub cmdResumeLabelErr_Click()
        `
        ` show resume label version
        `
        On Error GoTo LocalErr
        `
        Dim intX As Integer
        Dim intY As Integer
        Dim intZ As Integer
        `
    cmdLabelInput:
        intX = InputBox("Enter a Divisor:", "Input Box #1")
        intY = InputBox("Enter a Dividend:", "Input Box #2")
        intZ = intX / intY
        MsgBox "The Quotient is: " + Str(intZ), vbInformation, "Results"
        GoTo LocalExit
        `
    LocalErr:
        If Err = 11 Then     ` divide by zero error
            MsgBox CStr(Err.Number) & " - " & Err.Description, vbCritical, Â"cmdResumeLabelErr"
            Resume cmdLabelInput ` back for more
        Else
            MsgBox CStr(Err) & " -" & Error$, vbCritical, "cmdLabel"
            Resume Next
        End If
        `
    LocalExit:
        `
    End Sub

Save and run the project. Enter `13` at the first input box and `0` at the second input box. This causes a divide-by-zero error, and the error handler takes over from there. You see the error message shown in Figure 14.6 and then return to the line that starts the input process.
Figure 14.6. Displaying the `Divide by Zero` error message.

####
Using the Exit or End Method to Exit an Error Handler

There are times when an error occurs and there is no good way to return to the program. A good example of this type of error can occur when the program attempts to open files on a network file server and the user has forgotten to log onto the server. In this case, you need to either exit the routine and return to the calling procedure, or exit the program completely. Exiting to a calling routine can work if you have written your program to anticipate these critical errors. Usually it's difficult to do that. Most of the time, critical errors of this type mean you should end the program and let the user fix the problem before restarting the program.

Let's add one more button to the BASICERR project. Set its Caption property to End and its Name property to cmdEndErr. Enter the code in Listing 14.8 to support the `cmdEnd_Click` event.

### Listing 14.8. Using the End keyword.

    Private Sub cmdEndErr_Click()
        `
        ` use End to exit handler
        `
        On Error GoTo LocalErr
        `
        Dim strMsg As String
        Open "junk.txt" For Input As 1
        GoTo cmdEndExit
        `
    LocalErr:
       If Err.Number = 53 Then
          strMsg = "Unable to open JUNK.TXT" & vbCrLf
          strMsg = strMsg & "Exit the program and check your INI file" & vbCrLf
          strMsg = strMsg & "to make sure the JUNKFILE setting is correct."
          MsgBox strMsg, vbCritical, "cmdEnd"
          Unload Me
       Else
          MsgBox Str(Err) + " - " + Error$, vbCritical, "cmdEnd"
          Resume Next
       End If
       `
    LocalExit:
       `
    End Sub

In Listing 14.8, you add a check in the error handler for the anticipated `File not found` error. You give the user some helpful information and then tell him you are closing down the program. It's always a good idea to tell the user when you are about to exit the program. Notice that you did not use the Visual Basic `End` keyword; you used `Unload Me`. Remember that `End` stops all program execution immediately. Using `Unload Me` causes Visual Basic to execute any code placed in the `Unload` event of the form. This event should contain any file-closing routine needed to safely exit the program.

Save and run the project. When you click the End button, you see a message box explaining the problem and suggesting a solution (see Figure 14.7). When you click the OK button, Visual Basic ends the program.
Figure 14.7. Showing the error message before exiting the program.

####
Using the Err.Raise Method to Create Your Own Error Conditions

Many times it is not practical, or desirable, to display an error message when an error occurs. Other times, you may want to use the error-handling capabilities of Visual Basic 5 to your advantage by creating your own error codes and messages. You can do this using the `Raise` method of the Err object. Using the `Raise` method allows you to alert users (or other calling applications) that an error has occurred, but gives both you and the user additional flexibility on how the error is handled.

The `Raise` method takes up to five parameters:

- `ErrorNumber`: This is a unique number identifying the error that just occurred.

- `ErrorSource`: This is the code module that generated the error.

- `ErrorDescription`: This is the text message associated with the error.

- `HelpFile`: This is the help file that contains support information on the error.

- `HelpcontextID`: This is the ID number of the help topic associated with this error.

When you raise your own errors, you are required to report an error number. This number can be any unique value. If you use a number already defined as a Visual Basic error, you automatically get the `ErrorDescription` and any associated `HelpFile` and `HelpContextID` information as well. If you generate your own unique number, you can fill in the other parameters yourself. It is recommended that you use the vbObjectError constant as a base number for your own error codes. This guarantees that your error number does not conflict with any Visual Basic errors.

Here is a typical call to use the `Err.Raise` method:

    `
    LocalErr:
        ` trouble with file stuff!
        Err.Raise vbObjectError + 1, "errHandler.LogError", "Can't write log file [" Â& errLogFileName & "]"
        `
    End Sub

It is especially important to use this method for marking errors when you are coding ActiveX DLL servers. Since servers may run at a remote location on the network, you cannot be sure that users ever see any error dialog you display. Also, remember that, even if the DLL is running on the local PC, error dialog boxes are application-modal. No other processing occurs until the dialog box is dismissed. You learn to use the `Err.Raise` method when you create your errHandler object library later in this chapter.

So far, you have seen how to build a simple error handler and the different ways to exit error handlers. Now you need to learn about the different types of errors that you may encounter in your Visual Basic programs and how to plan for them in advance.

### Types of Errors

In order to make writing error handlers easier and more efficient, you can group errors into typical types. These error types can usually be handled in a similar manner. When you get an idea of the types of errors you may encounter, you can begin to write error handlers that take care of more than one error. You can write handlers that take care of error types.

There are four types of Visual Basic errors:

- General file errors: These are errors you encounter when you are attempting to open, read, or write simple files. This type of error does not include errors related to internal database operations (read/write table records).

- Physical media errors: These are errors caused by problems with physical devices--errors such as unresponsive communications ports or printers and low-level disk errors (`Unable To Read Sector`, and so on).

- Program code errors: These are errors that appear in your programs due to problems with your code. Errors include `Divide by zero`, `Invalid Property`, and other errors that can only be corrected by changing the Visual Basic code in your programs.

- Database errors: These are errors that occur during database operations, usually during data read/write or data object create/delete operations.

Each of these types of errors needs to be handled differently within your Visual Basic programs. You learn general rules for handling these errors in the following sections.

#### General File Errors

General file errors occur because of invalid data file information such as a bad filename, data path, or device name. Usually the user can fix these errors, and the program can continue from the point of failure. The basic approach to handling general file errors is to create an error handler that reports the problem to the user and asks for additional information to complete or retry the operation.

In Listing 14.9, the error handler is called when the program attempts to open a control file called `CONTROL.TXT`. The error handler then prompts the user for the proper file location and continues processing. Start a new Standard EXE project (`ERRTYPES.VBP`) and add a command button to the form. Set its Caption property to Control and its Name property to cmdControl. Also, add a `CommonDialog` control to the project. Enter the code in Listing 14.9 into the `cmdControl_Click` event.

### Listing 14.9. Adding code to the cmdControl_Click event.

    Private Sub cmdControl_Click()
        `
        ` show general file errors
        `
        On Error GoTo LocalErr
       `
       Dim strFile As String
       Dim strMsg As String
       Dim lngReturn As Long
       `
       strFile = "\control.txt"
       `
       Open strFile For Input As 1
       MsgBox "Control File Opened"
       GoTo LocalExit
       `
    LocalErr:
       If Err.Number = 53 Then ` file not found?
          strMsg = "Unable to Open CONTROL.TXT" & vbCrLf
          strMsg = strMsg & "Select OK to locate CONTROL.TXT" & vbCrLf
          strMsg = strMsg & "Select CANCEL to exit program."
          `
          lngReturn = MsgBox(strMsg, vbCritical + vbOKCancel, "cmdControl")
          `
          If lngReturn = vbOK Then
             CommonDialog1.filename = strFile
             CommonDialog1.DefaultExt = ".txt"
             CommonDialog1.ShowOpen
             Resume
          Else
             Unload Me
          End If
       Else
          MsgBox CStr(Err.Number) & " - " + Err.Description
          Resume Next
       End If
       `
    LocalExit:
       `
    End Sub

Save the form as `FRMERRTYPES.FRM` and the project as `PRJERRTYPES.VBP`. Now run this project. When you click on the Control button, the program tries to open the `CONTROL.TXT` file. If it can't be found, you see the error message (see Figure 14.8).

######

Figure 14.8. Displaying the `File not Found` error.
If the user selects OK, the program calls the `CommonDialog` control and prompts the user to locate the `CONTROL.TXT` file. It can be found in the `\TYSDBVB5\SOURCE\CHAP14` directory (see Figure 14.9).

######

Figure 14.9. Attempting to locate the `CONTROL.TXT` file.

> TIP: Notice the use of the `CommonDialog` control to open the file. Whenever you need to prompt users for file-related action (open, create, save), you should use the `CommonDialog` control. This is a familiar dialog for your users, and it handles all of the dirty work of scrolling, searching, and so on.

Table 14.2 lists errors that are similar to the `File not found` error illustrated in Listing 14.9. Errors of this type usually involve giving the user a chance to re-enter the filename or reset some value. Most of the time, you can write an error trap that anticipates these errors, prompts the user to supply the corrected information, and then retries the operation that caused the error.
Table 14.2. Common general file errors.

Error Code | Error Message
52 | Bad filename or number
53 | File not found
54 | Bad file mode
55 | File already open
58 | File already exists
59 | Bad record length
61 | Disk full
62 | Input past end of file
63 | Bad record number
64 | Bad filename
67 | Too many files
74 | Can't rename with different drive
75 | Path/File access error
76 | Path not found

In cases when it is not practical to prompt a user for additional information (such as during initial startup of the program), it is usually best to report the error in a message box. Then give the user some ideas about how to fix the problem before you exit the program safely.

#### Physical Media Errors

Another group of common errors is caused by problems with physical media. Unresponsive printers, disk drives that do not contain diskettes, and downed communications ports are the most common examples of physical media errors. These errors might, or might not, be easily fixed by your user. Usually, you can report the error, wait for the user to fix the problem, and then continue with the process. For example, if the printer is jammed with paper, all you need to do is report the error to the user, and then wait for the OK to continue.

Let's add another button to the `PRJERRTYPES.VBP` project to display an example of physical media error handling. Add a new command button to the project. Set its Caption property to &Media and its Name property to cmdMedia. Enter the code in Listing 14.10 into the `cmdMedia_Click` event.

### Listing 14.10. Trapping media errors.

    Private Sub cmdMedia_Click()
        `
        ` show handling of media errors
        `
        On Error GoTo LocalErr
        Dim strMsg As String
        Dim lngReturn As Long
       `
       ` open a file on the a drive
       ` an error will occur if there
       ` is no diskette in the drive
       `
       Open "a:\junk.txt" For Input As 1
       Close #1
       GoTo LocalExit
       `
    LocalErr:
       If Err.Number = 71 Then
          strMsg = "The disk drive is not ready." & vbCrLf
          strMsg = strMsg + "Please make sure there is a diskette" & vbCrLf
          strMsg = strMsg + "in the drive and the drive door is closed."
          `
          lngReturn = MsgBox(strMsg, vbCritical + vbRetryCancel, "cmdMedia")
          `
          If lngReturn = vbRetry Then
             Resume
          Else
             Resume Next
          End If
       Else
          MsgBox Str(Err.Number) & " - " & Err.Description
          Resume Next
       End If
       `
    LocalExit:
       `
    End Sub

In Listing 14.10, you attempt to open a file on a disk drive that has no disk (or has an open drive door). The error handler prompts the user to correct the problem and allows the user to try the operation again. If all goes well the second time, the program continues. The user also has an option to cancel the operation.

Save and run the project. When you click on the Media button, you should get results that look like those in Figure 14.10.
Figure 14.10. The results of a physical media error.

####
Program Code Errors

Another common type of error is the program code error. These errors occur as part of the Visual Basic code. Errors of this type cannot be fixed by users and are usually due to unanticipated conditions within the code itself. Error messages such as `Variable Not Found`, `Invalid Object`, and so on, create a mystery for most of your users. The best way to handle errors of this type is to tell the user to report the message to the programmer and close the program safely.

#### Database Errors with the Data Control

A very common type of error that occurs in database applications is the data-related error. These errors include those that deal with data type or field size problems, table access restrictions including read-only access, locked tables due to other users, and so on. Database errors fall into two groups. Those caused by attempting to read or write invalid data to or from tables, including data integrity errors, make up the most common group. The second group includes those errors caused by locked tables, restricted access, or multiuser conflicts.

> NOTE: Errors concerning table locks, restricted access, and multiuser issues are covered in depth in the last week of the book. In this chapter, you focus on the more common group of errors.

In most cases, all you need to do is trap for the error, report it to the user, and allow the user to return to the data entry screen to fix the problem. If you use the Visual Basic data control in your data forms, you can take advantage of the automatic database error reporting built into the data control. As an example, let's put together a simple data entry form to illustrate some of the common data entry-oriented database errors.

Start a new Visual Basic Standard EXE project to illustrate common database errors. Add a data control, two bound input controls, and two label controls. Use Table 14.3 as a reference for adding the controls to the form. Refer to Figure 14.11 as a guide for placing the controls.
Figure 14.11. Laying out the DataErr form.
Table 14.3. Controls for the frmDataErr form.

Control | Property | Setting
VB.Form | Name | frmDataErr
| Caption | "Data Error Demo"
| ClientHeight | 1335
| ClientLeft | 60
| ClientTop | 345
| ClientWidth | 4665
| StartUpPosition | 3 `Windows Default
VB.CommandButton | Name | cmdAdd
| Caption | "&Add"
| Height | 375
| Left | 3300
| Top | 60
| Width | 1215
VB.TextBox | Name | txtName
| DataField | "Name"
| DataSource | "Data1"
| Height | 315
| Left | 1500
| Top | 540
| Width | 3015
VB.TextBox | Name | txtKeyField
| DataField | "KeyField"
| DataSource | "Data1"
| Height | 375
| Left | 1500
| Top | 60
| Width | 1515
VB.Data | Name | Data1
| Align | 2 `Align Bottom
| Caption | "Data1"
| Connect | "Access"
| DatabaseName | `C:\TYSDBVB5\SOURCE\DATA\ERRORS\`
|  | `ERRORDB.MDB`
| Height | 360
| RecordSource | "Table1"
| Top | 975
| Width | 4665
VB.Label | Name | lblName
| Caption | "Name"
| Height | 255
| Left | 120
| Top | 540
| Width | 1215
VB.Label | Name | lblKeyField
| Caption | "Key Field"
| Height | 255
| Left | 120
| Top | 120
| Width | 1215

The only code you need to add to this form is a single line to support the Add button. Place the following code behind the `cmdAdd_Click` event.

    Private Sub cmdAdd_Click()
       Data1.Recordset.AddNew
    End Sub

Now save the new form as `DATAERR.FRM` and the project as `DATAERR.VBP`. When you run the project, you can test the built-in error trapping for Microsoft data controls by adding a new, duplicate record to the table. Press the Add button, then enter `KF109` in the KeyField input box and press one of the arrows on the data control to force it to save the record. You should see a database error message that looks like the one in Figure 14.12.

Are you surprised? You didn't add an error trap to the data entry form, but you still got a complete database error message! The Visual Basic data control is kind enough to provide complete database error reporting even if you have no error handlers in your Visual Basic program. Along with the automatic errors, the data control also has the `Error` event. Each time a data-related error occurs, this event occurs. You can add code in the `Data1_Error` event to automatically fix errors, display better error messages, and so on.
Figure 14.12. A sample Microsoft data control error message.
Let's modify the program a bit to show you how you can use the `Data1_Error` event. First, add a CommonDialog control to your form. Then edit the DatabaseName property of the data control to read `C:\ERRORDB.MDB`. Next, add the code from Listing 14.11 to the `Data1_Error` event.

### Listing 14.11. Coding the Data1_Error event.

    Private Sub Data1_Error(DataErr As Integer, Response As Integer)
        `
        ` add error-trapping for data errors
        `
        Dim strFileName As String
        `
        Select Case DataErr
            Case 3044 ` database not found
                MsgBox "Unable to locate data file", vbExclamation, "Database ÂMissing"
                `
                CommonDialog1.DialogTitle = "Locate ERRORDB.MDB"
                CommonDialog1.filename = "ERRORDB.MDB"
                CommonDialog1.Filter = "*.mdb"
                CommonDialog1.ShowOpen
                Data1.DatabaseName = CommonDialog1.filename
                `
                Response = vbCancel ` cancel auto-message
        End Select
        `
    End Sub

Notice that the code in Listing 14.11 checks to see whether the error code is `3044`. This is the error number that corresponds to the "database missing" message. If the `3044` code is reported, the user sees a short message and then the file open dialog, ready to locate and load the database. Finally, notice the line that sets the `Response` parameter to `vbCancel`. This step tells Visual Basic not to display the default message.

> TIP: Usually, it is not a good idea to attempt to override this facility with your own database errors. As long as you use the Visual Basic data control, you do not need to add database error-trapping routines to your data entry forms. The only time you need to add error-related code is when you want to perform special actions in the `Error` event of the data control.

You need to add one more bit of code to complete this error trap. Add the following line of code to the `Form_Activate` event.

    Private Sub Form_Activate()
        Data1.Refresh
    End Sub

This code makes sure the data entry fields on the form are updated with the most recent data from the database.

Now save and run the project. You first see a message telling you that the database is missing (Figure 14.13).
Figure 14.13. Custom error message in the `Data1_Error` event.
Next, the open file dialog waits for you to locate and load the requested database (Figure 14.14).
Figure 14.14. Locating the requested database.
Finally, once you load the database, the data entry screen comes up ready for your input.

#### Database Errors with Microsoft Data Access Objects

If you use Microsoft data access objects instead of the Visual Basic data control, you need to add error-handling routines to your project. For example, if you want to create a Dynaset using Visual Basic code, you need to trap for any error that might occur along the way.

Add the code in Listing 14.12 to the `Form_Load` event of `frmData`. This code opens the database and creates a Dynaset to stuff into the data control that already exists on the form.

### Listing 14.12. Adding code to the Form_Load event.

    Private Sub Form_Load()
        `
        ` create recordset using DAO
        `
        On Error GoTo LocalErr
        `
        Dim ws As Workspace
        Dim db As Database
        Dim rs As Recordset
        Dim strSQL As String
        `
        strSQL = "SELECT * FROM Table2"
        Set ws = DBEngine.Workspaces(0)
        Set db = ws.OpenDatabase(App.Path & "\..\..\Data\Errors\ErrorDB.mdb")
        Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
        Exit Sub
        `
    LocalErr:
        MsgBox "<" & CStr(Errors(0).Number) & "> " & Errors(0).Description, ÂvbCritical, "Form_Load Error"
        Unload Me
        `
    End Sub

The code in Listing 14.12 establishes some variables and then opens the database and creates a new Dynaset from a data table called Table2.

> NOTE: Notice that instead of the Visual Basic Err object, the DAO Errors collection is used to retrieve the most recent database error. The Errors collection is only available if you loaded the Microsoft DAO library using the Project | References option from the main Visual Basic 5 menu.

Because there is no Table2 in ERRORDB.MDB, you see a database error when the program runs. The error message is displayed, and then the form is unloaded completely (see Figure 14.15).
Figure 14.15. Displaying an error message from the `Form_Load` event.
It is a good idea to open any data tables or files that you need for a data entry form during the `Form_Load` event. That way, if there are problems, you can catch them before data entry begins.

### Creating Your Error Handler OLE Server

In the previous sections, you created several error handlers, each tuned to handle a special set of problems. Although this approach works for small projects, it can be tedious and burdensome if you have to put together a large application. Also, after you've written an error handler that works well for one type of error, you can use that error handler in every other program that might have the same error. Why write it more than once?

Even though Visual Basic requires error traps to be set for each `Sub` or `Function`, you can still create a generic approach to error handling that takes advantage of code you have already written. In this section, you write a set of routines that you can install in all your Visual Basic programs--the error-handling OLE Server. This OLE Server offers some generic error-handling capabilities along with the ability to log these errors to a disk file and to keep track of the procedure call stack. These last two services can be very valuable when you encounter a vexing bug in your program and need to get additional information on the exact subroutines and functions that were executed before the error occurred.

To build the new error handling OLE Server, you need to start a new Visual Basic ActiveX DLL project. Name the default Class module errHandler and set the project Name to prjErrHandler. You also need to add a BAS module and a form to the project. The form acts as the new customized error dialog box. The BAS module holds a new user-defined type and some API definitions for use with the customized error dialog box.

#### Building the errHandler Class

Building the errHandler class involves several steps. First, add some code to the general declaration section of the Class object (see Listing 14.13).

### Listing 14.13. Adding code to the Declaration section of the errHandler class.

    Option Explicit

    `
    ` error types
    Enum errType
        erritem = 0
        errcoll = 1
    End Enum

    `
    ` return/option values
    Enum errReturn
        errExit = 0
        errresume = 1
        errNext = 2
        errselect = 3
    End Enum

    `
    ` handler storage
    Private errDefRtn As errReturn
    Private errDefType As errType
    `

The first two items in the declaration section define enumerated types. These are a special type of user-defined type that are a mix between a standard user-defined type and a Public constant. Enumerated types make it easy to write well-documented code. Along with the enumerated types, you see two Private variables declared for local use.

Next, you need to add some declaration code to the BAS module in your DLL project. This code defines a special custom data type that you can use to control the display of your custom error dialog box. The errHandler DLL allows you to access any possible help topics associated with the error messages, too. For this reason, the BAS module contains the WinHelp API declaration. This is used on the custom dialog box. Open the BAS module, set its Name property to modErrHandler and enter the code in Listing 14.14 into the general declaration section of the module.

### Listing 14.14. Adding code to the general declaration section of the modErrHandler BAS module.

    Option Explicit

    `
    ` define dialog data type
    Public Type errDialog
        Message As String
        Buttons As Variant
        Title As String
        HelpFile As String
        HelpID As Long
        Return As Long
    End Type
    `
    Public udtErrDialog As errDialog

    `
    ` declare winHelp API
    Declare Function WinHelp Lib "user32" Alias "WinHelpA" (ByVal hwnd As Long, _
      ByVal lpHelpFile As String, _
      ByVal wCommand As Long, _
      ByVal dwData As Long) As Long
    `
    Public Const HELP_CONTEXT = &H1
    Public Const HELP_QUIT = &H2

That's all that you need to add to the `modErrHandler`. Next, you need to add code to the `Initialize` event of the errHandler Class module. Add the code from Listing 14.15 to the `Class_Initialize` event.

### Listing 14.15. Adding code to the Class_Initialize event
of the errHandler class.

    Private Sub Class_Initialize()
        `
        ` set starting values
        errDefRtn = errExit
        errDefType = errItem
        `
        udtErrDialog.Buttons = ""
        udtErrDialog.HelpFile = ""
        udtErrDialog.HelpID = -1
        udtErrDialog.Message = ""
        udtErrDialog.Return = -1
        udtErrDialog.Title = ""
        `
    End Sub

This errHandler class has two Public properties--the DefaultAction and DefaultType properties. These defaults were set in the `Initialize` event and can be overridden by setting the properties at runtime. Create the DefaultAction property (using the Tools | Add Procedure menu option) and add the code from Listing 14.16 into the `Property Let` and `Property Get` routines.

### Listing 14.16. Defining the Property Let and Property Get routines for the DefaultAction property.

    Public Property Get DefaultAction() As errReturn
        `
        ` return default
        `
        DefaultAction = errDefRtn
        `
    End Property

    Public Property Let DefaultAction(ByVal vNewValue As errReturn)
        `
        ` verify parm and store
        `
        If vNewValue >= errExit Or vNewValue <= errselect Then
            errDefRtn = vNewValue
        End If
        `
    End Property

Note that the data type for the property is errReturn. This is one of the enumerated types defined in the declaration section of the class module. Next, create the `Property Let` and `Property Get` routines for the DefaultType property, and enter the code from Listing 14.17 into the project.

### Listing 14.17. Coding the Property Let and Property Get routines for the DefaultType property.

    Public Property Get DefaultType() As errType
        `
        DefaultType = errDefType
        `
    End Property

    Public Property Let DefaultType(ByVal vNewValue As errType)
        `
        If vNewValue >= errcoll Or vNewValue <= erritem Then
            errDefType = vNewValue
        End If
        `
    End Property

Finally, you're ready to write the main error handler method. This method can be called from any VBA-compliant program. You pass the Visual Basic Err object (or database Errors collection) along with a few optional parameters that can control the behavior of the message dialog box. After the method has been completed, a value is returned. This value can be used to control program flow and error recovery.

Create a new function method in your class module called `errHandler` and enter the code from Listing 14.18.

### Listing 14.18. Coding the errHandler function.

    Public Function errHandler(objErrColl As Variant, Optional intType As errType, ÂOptional errOption As errReturn, Optional errRefName As String) As errReturn

        ` --------------------------------------------------------------------------
        ` produce msg and prompt for response
        `
        ` inputs:
        `   objErrColl  - DAO collection -OR- VBA Err object
        `   intType     - errType enum that describes objErrColl (coll or item)
        `   errOption   - errReturn enum sets dialog behavior (exit, resume, next, 	                 Âselect)
        `   errRefName  - string to reference caller
        `
        ` returns:
        `   errExit     - end program
        `   errResume   - try again
        `   errNext     - skip to next line
        ` --------------------------------------------------------------------------
        `
        Dim strMsg As String
        Dim strTitle As String
        Dim rtnValue As errReturn
        `
        ` retrieve action option
        If IsMissing(errOption) Then
            errOption = errDefRtn
        End If
        `
        ` retrieve reference name
        If IsMissing(errRefName) Then
            errRefName = ""
        Else
            errRefName = " from " & errRefName
        End If
        `
        ` build full message
        strMsg = errMsg(objErrColl, intType)
        `
        ` write it out, if allowed
        If errLogFlag = True Then
            LogError strMsg
        End If
        `
        ` evaluate things
        Select Case errOption
            Case errExit
                udtErrDialog.Title = "Exiting Program"
                udtErrDialog.Message = strMsg
                udtErrDialog.Buttons = Array("&Exit")
                frmErrDialog.Show vbModal
                rtnValue = errExit
                `
            Case errresume, errNext
                udtErrDialog.Title = "Error Message" & errRefName
                udtErrDialog.Message = strMsg
                udtErrDialog.Buttons = Array("&OK")
                frmErrDialog.Show vbModal
                rtnValue = errOption
                `
            Case Else
                udtErrDialog.Title = "Error Message" & errRefName
                udtErrDialog.Message = strMsg
                udtErrDialog.Buttons = Array("&Cancel", "&Retry", "&Ignore")
                frmErrDialog.Show vbModal
                rtnValue = udtErrDialog.Return
        End Select
        `
        ` give it back
        errHandler = rtnValue
        `
    End Function

The code in Listing 14.18 calls a support function (`errMsg`) and a dialog box (frmErrDialog). You need to code these two remaining objects before you can test your error handler. Create a new function (`errMsg`) in the class module and enter the code from Listing 14.19 into the project.

### Listing 14.19. Adding the errMsg support function.

    Public Function errMsg(objErrColl As Variant, intType As errType) As String
        `
        ` build and return complete error msg
        `
        Dim strMsg As String
        Dim objItem As Error
        `
        strMsg = ""
        `
        If intType = errcoll Then
            For Each objItem In objErrColl
                strMsg = strMsg & "<" & CStr(objItem.Number) & "> "
                strMsg = strMsg & objItem.Description
                strMsg = strMsg & " (in " & objItem.Source & ")." & vbCrLf
            Next
        Else ` intType= errItem
            strMsg = "<" & objErrColl.Number & "> "
            strMsg = strMsg & objErrColl.Description
            strMsg = strMsg & " (in " & objErrColl.Source & ")"
            `
            udtErrDialog.HelpFile = objErrColl.HelpFile
            udtErrDialog.HelpID = objErrColl.HelpContext
        End If
        `
        errMsg = strMsg
        `
    End Function

The main job of the `errMsg` routine is to build a complete error message for display to the user. In order to do this, `errMsg` needs to know if the object that was passed was a single VBA Err object or the DAO Errors collection. That is why the `errType` parameter is included in the call. Also note that the `errMsg` method has been declared as a `Public` method. You can call this method from your Visual Basic 5 programs, too. That way, even if you don't want to perform all of the error-handing operations, you can use `errMsg` to get an improved error message for your use.

#### Coding the frmErrDialog Form

The last main piece of the errHandler class is a custom dialog box to display the error message and get input from the user. Often, you want to do more than just display the message and let the user press "OK." You may ask them to retry the same process, or ask if they want to ignore the error and continue. This dialog box not only displays the message and gives you an opportunity to get a response from the user, it also allows you to provide an optional "Help" button to give the user greater support in discovering how to resolve the error.

> NOTE: Creating Help files for your Visual Basic project is covered in Appendix B of this book.

With the prjErrHandler project still open, add a form to the project. Use Table 14.4 and Figure 14.16 as guides in laying out the form.
Table 14.4. Control table for the frmErrDialog form.

Control | Property | Setting
VB.Form | Name | frmErrDialog
| BorderStyle | 3 `Fixed Dialog
| Caption | "Error Report"
| ClientHeight | 1755
| ClientLeft | 45
| ClientTop | 330
| ClientWidth | 5460
| ControlBox | 0 `False
| MaxButton | 0 `False
| MinButton | 0 `False
| ShowInTaskbar | 0 `False
| StartUpPosition | 2 `CenterScreen
VB.TextBox | Name | txtErrMsg
| BackColor | &H80000000&
| Height | 1035
| Left | 900
| Locked | -1 `True
| MultiLine | -1 `True
| ScrollBars | 2 `Vertical
| Top | 120
| Width | 4395
VB.CommandButton | Name | cmdBtn
| Caption | "&Help"
| Height | 315
| Index | 3
| Left | 4080
| Top | 1320
| Visible | 0 `False
| Width | 1215
VB.CommandButton | Name | cmdBtn
| Caption | "&Ignore"
| Height | 315
| Index | 2
| Left | 2760
| Top | 1320
| Visible | 0 `False
| Width | 1215
VB.CommandButton | Name | cmdBtn
| Caption | "&Retry"
| Height | 315
| Index | 1
| Left | 1440
| Top | 1320
| Visible | 0 `False
| Width | 1215
VB.CommandButton | Name | cmdBtn
| Caption | "&OK"
| Height | 315
| Index | 0
| Left | 120
| Top | 1320
| Visible | 0 `False
| Width | 1215
VB.Image | Name | Image1
| Height | 600
| Left | 120
| Picture | `C:\TYSDBVB5\SOURCE\CHAP14\`
|  | `ERRHANDLER\INTL_NO.BMP`
| Stretch | -1 `True
| Top | 120
| Width | 600

Be sure you build the command buttons as a control array and that you set their Visible property to False. You write code to arrange and enable these buttons as needed at runtime.
Figure 14.16. Laying out the frmErrDialog form.
Only two events need to be coded for this form. The `Form_Load` event handles most of the dirty work. Listing 14.20 shows the code that you should add to the `Form_Load` event.

### Listing 14.20. Coding the Form_Load event of the frmErrDialog form.

    Private Sub Form_Load()
        `
        Dim intBtns As Integer
        Dim intLoop As Integer
        `
        txtErrMsg = udtErrDialog.Message
        Me.Caption = udtErrDialog.Title
        `
        intBtns = UBound(udtErrDialog.Buttons)
        For intLoop = 0 To intBtns
            cmdBtn(intLoop).Caption = udtErrDialog.Buttons(intLoop)
            cmdBtn(intLoop).Visible = True
            cmdBtn(intLoop).Top = Me.ScaleHeight - 420
            cmdBtn(intLoop).Left = 120 + (1300 * intLoop)
        Next
        `
        ` check for help file
        If udtErrDialog.HelpFile <> "" Then
            cmdBtn(3).Visible = True
            cmdBtn(3).Top = Me.ScaleHeight - 420
            cmdBtn(3).Left = 120 + (1300 * 3)
        End If
        `
    End Sub

The code in Listing 14.20 first sets the dialog caption and message box. Then, based on the properties of the udtErrDialog type, the buttons are arranged on the form. The only other code that needs to be added to this form is the code that goes behind the command button array. Place the code from Listing 14.21 in the `cmdBtn_Click` event of the form.

### Listing 14.21. Coding the cmdBtn_Click event.

    Private Sub cmdBtn_Click(Index As Integer)
        `
        ` return user selection
        `
        Dim lngReturn As Long
        `
        Select Case Index
            Case 0
                udtErrDialog.Return = errExit
                lngReturn = WinHelp(Me.hwnd, udtErrDialog.HelpFile, HELP_QUIT, &H0)
                Unload Me
            Case 1
                udtErrDialog.Return = errresume
                lngReturn = WinHelp(Me.hwnd, udtErrDialog.HelpFile, HELP_QUIT, &H0)
                Unload Me
            Case 2
                udtErrDialog.Return = errNext
                lngReturn = WinHelp(Me.hwnd, udtErrDialog.HelpFile, HELP_QUIT, &H0)
                Unload Me
            Case 3
                lngReturn = WinHelp(Me.hwnd, udtErrDialog.HelpFile, HELP_CONTEXT, udtErrDialog.HelpID)
        End Select
        `
    End Sub

The code in Listing 14.21 allows the user to select the command button appropriate for the moment. If the user presses the Help button, the properties from the udtErrDialog type are used to fill in the parameters of the WinHelp API call.

That's all the code you need to create your errHandler ActiveX DLL. Save the project and then compile it (File | Make prjErrHandler.dll). If it compiles without error, you're all set for a quick test!

#### Testing the ErrHandler Object Library

Start a new Visual Basic 5 Standard EXE project. Set the form name to frmTest and the project name to prjTest. Add a data control and a command button to the form. Refer to Figure 14.17 while laying out the form.
Figure 14.17. Laying out the frmTest form.
Next you need to add a reference to the error handler object library to your project. Select Project | References and locate and add the prjErrHandler DLL (see Figure 14.18).
Figure 14.18. Adding the error handler object library to the project.
Now you can add a bit of code to the form to set up the error handler, and then cause an error to be handled. First, add the following line to the general declaration section of the form. This declares the object that contains the error handler.

    Option Explicit
    `
    Public objErr As Object

Next, add the code from Listing 14.22 to the `Form_Load` event of the project.

### Listing 14.22. Coding the Form_Load event.

    Private Sub Form_Load()
        `
        Data1.DatabaseName = "junk"
        Set objErr = New errHandler
        `
    End Sub

This code creates the new error handler object and then sets up the data control with a bogus database name. Now add the code from Listing 14.23 to the `Data1_Error` event. This code intercepts the database error and displays the new custom dialog box.

### Listing 14.23. Trapping the data control error.

    Private Sub Data1_Error(DataErr As Integer, Response As Integer)
        `
       Dim rtn As Long

        Response = 0
        rtn = objErr.errHandler(Errors, errcoll)
        `
    End Sub

Now save the form (`FRMTEST.FRM`) and the project (`PRJTEST.VBP`) and run the code. You should see your new object library error dialog telling you about the database error (see Figure 14.19).

Now add some code behind the `Command1_click` event to create a divide-by-zero error. The code in Listing 14.24 does just that.
Figure 14.19. The new error object library in action.

###
Listing 14.24. Creating a divide-by-zero error in code.

    Private Sub Command1_Click()
        `
        On Error GoTo Localerr
        Dim rtn as Long
        `
       Print 6 / 0
        `
        Exit Sub
        `
    Localerr:
       rtn = objErr.errHandler(Err, erritem, errresume, "prjTest. ÂForm1.Command1_Click")
       Resume Next
        `
    End Sub

When you save and run this code, press the command button to see the new error report. You should see that the Help button is active. Press the Help button to display Visual Basic 5 help for dealing with the divide-by-zero error (see Figure 14.20).

> NOTE: Check out Appendix B for information on how you can create your own help files for your Visual Basic 5 projects.

Figure 14.20. Viewing help on the divide-by-zero error.
Now let's add an option that creates an error report file whenever the error handler is activated.

#### Adding Error Logs to the Error Handler

When errors occur, users often do not remember details that appear in the error messages. It's much more useful to create an error log on disk whenever errors occur. This enables programmers or system administrators to review the logs and see the error messages without having to be right next to the user when the error occurs.

To build error-logging features into the existing errhandler class, you need to declare two new properties (LogFileName and WriteLogFlag), create a `LogError` method to write the errors to a disk file, add some code to the general declarations area, the `Class_Initialize` and `Class_Terminate` events, and add a few lines to the `errHandler` method to call the `LogError` method.

First, restart the errhandler DLL project and add the code below to the general declaration section of the class module.

    `
    ` logging storage
    Private errLogFileName As String
    Private errLogFlag As Boolean

These two lines of code appear at the end of the section. They declare local storage space for the new properties. Now use the Tools | Add Procedure menu option to add two new Public properties to the class: LogFileName and WriteLogFlag. Listing 14.25 shows the code you need to add to the `Property Let` and `Property Get` statements for these two new properties.

### Listing 14.25. Coding the Property Let and Property Get statements for the LogFileName and WriteLogFlag properties.

    Public Property Get LogFileName() As String
        `
        LogFileName = errLogFileName
        `
    End Property

    Public Property Let LogFileName(ByVal vNewValue As String)
        `
        errLogFileName = vNewValue
        `
    End Property

    Public Property Get WriteLogFlag() As Boolean
        `
        WriteLogFlag = errLogFlag
        `
    End Property

    Public Property Let WriteLogFlag(ByVal vNewValue As Boolean)
        `
        errLogFlag = vNewValue
        `

`End Property` The LogFileName property is used to hold the name of the disk file that holds the log records. The LogFlag property controls the status of the error logging. If the LogFlag property is set to `True`, log records are created. Now add the following code to the end of the `Class_Initialize` event. This sets the default values for the two new properties.

        errLogFileName = App.EXEName & ".err"
        errLogFlag = False
        `

Now create a new `Private Sub` method called `LogError` and enter the code from Listing 14.26 into the routine. This is the code that actually creates the log entries.

### Listing 14.26. Coding the LogError method.

    Private Sub LogError(strErrMsg As String)
        `
        ` write error to disk file
        On Error GoTo LocalErr
        `
        Dim intChFile As Integer
        `
        intChFile = FreeFile
        Open errLogFileName For Append As intChFile
            Print #intChFile, Format(Now, "general date")
            Print #intChFile, strErrMsg
            Print #intChFile, ""
        Close intChFile
        `
        Exit Sub
        `
    LocalErr:
        ` trouble with file stuff!
        Err.Raise vbObjectError + 1, "errHandler.LogError", "Can't write log file [" & errLogFileName & "]"
        `
    End Sub

Notice that you added an error handler in this routine. Because you are about to perform disk operations, you need to be ready for errors here, too! Notice also that the internal error is not displayed in a message box. Instead the `Raise` method of the Err object is used to generate a unique error number and description. This is sent back to the calling application for handling.

> TIP: The Visual Basic `FreeFile()` function is used to return a number that represents the first available file channel that Visual Basic uses to open the data file. Using `FreeFile()` guarantees that you do not select a file channel that Visual Basic is already using for another file.

Now all you need to do is add a call to the `LogError` method from within the Public `errHandler` method. Listing 14.27 shows the code you need to add in the routine. Make sure you add these lines of code right after the call to the `errMsg` function and just before the start of the `Select Case` statement.

### Listing 14.27. Updating the errHandler method.

    ` build full message
        strMsg = errMsg(objErrColl, intType)
        `
        ` write it out, if allowed           <<< new code
        If errLogFlag = True Then     <<< new code
            LogError strMsg                  <<< new code
        End If                                       <<< new code
        `
        ` evaluate things
    Select Case errOption

That's the end of the code to add logging to the error handler. Save the project and compile the ActiveX DLL. Once the DLL has been successfully compiled, close this project and open the test project you built earlier.

Open the `Form_Load` event of the frmTest form and add two lines to set the LogFileName and WriteLogFlag properties of the errHandler object. Listing 14.28 shows how to modify the code.

### Listing 14.28. Modifying the Form_Load event to include error logging.

    Private Sub Form_Load()
        `
        Data1.DatabaseName = "junk"
        Set objErr = New errHandler
        `
        objErr.WriteLogFlag = True
        objErr.LogFileName = App.Path & "\" & App.EXEName & ".log"
        `
    End Sub

Now, when you run the project, each error is logged to a file with the same name as the application in the same folder as the application. In the preceding example, a file called `errTest.log` was created in the `C:\TYSDBVB5\SOURCE\CHAP14\` folder. Listing 14.29 shows the contents of this error log file.

### Listing 14.29. Contents of the errTest.log file.

    05-Feb-97 5:27:01 AM
    <3024> Couldn't find file `junk'. (in DAO.Workspace).

    05-Feb-97 5:27:08 AM
    <11> Division by zero (in prjTest)

You can easily modify the layout and the contents of the log reports. You need only change a few lines of code in the `LogError` method.

#### Adding a Module Trace to the Error Handler

The final touch to add to your error handler library is the option to keep track of and print a module trace. A module trace keeps track of all the modules that have been called and the order in which they were invoked. This can be very valuable when you're debugging programs. Often, a routine works just fine when it is called from one module, but it reports errors if called from another module. When errors occur, it's handy to have a module trace to look through to help find the source of your problems.

We implement our module trace routines as a new objclass object in the prjErrHandler project. Reload the ActiveX DLL project and add a new class module to the project. Set its Name property to TraceObject and keep its Instancing property set to the default, `5 - MultiUse`.

You need two new properties for this object (TraceFileName and TraceFlag) and a handful of new Public methods:

- `Push` adds a sub or function name to the call list.

- `Pop` removes a sub or function name from the call list.

- `List` returns an array of all the names on the call list.

- `Dump` writes the complete call list to a disk file.

- `Show` displays the complete call list in a message box.

- `Clear` resets the call list.

First, add the code in Listing 14.30 to the general declarations area of the class module.

### Listing 14.30. Declaring the TraceObject variables.

    Option Explicit
    `
    ` local property storage
    Private trcFileName As String
    Private trcFlag As Boolean
    `
    ` internal variables
    Private trcStack() As String
    Private trcPointer As Long

Next, create the two new Public properties, TraceFile and TraceFlag, and enter the code from Listing 14.31 into the `Property Let` and `Property Get` statements for these two new properties.

### Listing 14.31. Coding the Property Let and Property Get statements for the TraceFile and TraceLog properties.

    Public Property Get TraceFileName() As String
        `
        TraceFileName = trcFileName
        `
    End Property

    Public Property Let TraceFileName(ByVal vNewValue As String)
        `
        trcFileName = vNewValue
        `
    End Property

    Public Property Get TraceFlag() As Boolean
        `
        TraceFlag = trcFlag
        `
    End Property

    Public Property Let TraceFlag(ByVal vNewValue As Boolean)
        `
        trcFlag = vNewValue
        `
    End Property

Now add the code from Listing 14.32 to the `Class_Initialize` event. This code sets the default values for the two Public properties.

### Listing 14.32. Coding the Class_Initialize event.

    Private Sub Class_Initialize()
        `
        ` startup stuff
        trcFileName = App.EXEName & ".trc"
        trcFlag = False
        `
    End Sub

Now it's time to code the various methods you need to manage call tracing in Visual Basic 5. First, create the `Public Sub` methods `Push` and `Pop`. These two routines handle the details of keeping track of each function or sub as it is executed. Listing 14.33 shows the code for these two Public methods.

### Listing 14.33. Coding the Push and Pop methods of the TraceObject.

    Public Sub Push(ProcName As String)
        `
        ` push a proc onto the stack
        trcPointer = trcPointer + 1
        ReDim Preserve trcStack(trcPointer)
        trcStack(trcPointer) = ProcName
        `
    End Sub

    Public Sub Pop()
        `
        ` pop a proc off the stack
        If trcPointer <> 0 Then
            trcPointer = trcPointer - 1
            ReDim Preserve trcStack(trcPointer)
        End If
        `
    End Sub

Now create another `Public Sub` method (`Clear`) and a `Public Function` method (`List`). Add the code from Listing 14.34 to the class.

### Listing 14.34. Coding the List and Clear methods of the TraceObject.

    Public Function List() As Variant
        `
        ` return an array of the trace log
        List = trcStack
        `
    End Function

    Public Sub Clear()
        `
        ` clear off the stack
        trcPointer = 0
        ReDim Preserve trcStack(0)
        `
    End Sub

> TIP: Notice the use of the Variant data type to return an array of items. This is a very efficient way to pass array data among Visual Basic methods.

Now create a new `Public Sub` called `Dump`. This writes the trace list to a disk file. Fill in the method with the code from Listing 14.35.

### Listing 14.35. Coding the Dump method of the TraceObject.

    Public Sub Dump()
        `
        ` write trace log to file
        Dim intFile As Integer
        Dim intLoop As Integer
        `
        intFile = FreeFile
        Open trcFileName For Append As intFile
            Print #intFile, "***TRACE STACK DUMP***"
            Print #intFile, "***DATE: " & Format(Now(), "general date")
            Print #intFile, ""
            `
            For intLoop = trcPointer To 1 Step -1
                Print #intFile, vbTab & Format(intLoop, "000") & ": " & ÂtrcStack(intLoop)
            Next
            `
            Print #intFile, ""
            Print #intFile, "***EOF"
        Close #intFile
        `
        Exit Sub
        `
    LocalErr:
        Err.Raise vbObjectError + 3, "Trace.Dump", "Can't write trace file [" & ÂtrcFileName & "]"
        `
    End Sub

Finally, create the `Public Sub` method called `Show` and enter the code from Listing 14.36.

### Listing 14.36. Coding the Show method of the TraceObject.

    Public Sub Show()
        `
        ` show trace log in dialog
        `
        Dim intLoop As Integer
        Dim strMsg As String
        `
        strMsg = ""
        For intLoop = trcPointer To 1 Step -1
            strMsg = strMsg & Format(intLoop, "000")
            strMsg = strMsg & ": "
            strMsg = strMsg & Trim(trcStack(intLoop))
            strMsg = strMsg & vbCrLf
        Next
        `
        MsgBox strMsg, vbInformation, "Trace Stack"
        `
    End Sub

Notice that the code in Listing 14.36 prints the call array in reverse order. This is the conventional way to print trace lists. The top-most entry shows the most recently executed routine, and the bottom-most entry shows the first routine in this trace.

After adding this last code, save and compile the ActiveX DLL and then load your errTest project. After you load the frmTest form, add the following code to the general declarations area of the form.

    Public objTrace As Object

Next, update the `Form_Load` event as shown in Listing 14.37. This adds the use of the trace module to the project.

### Listing 14.37. Updating the Form_Load event to include module tracing.

    Private Sub Form_Load()
        `
        Data1.DatabaseName = "junk"
        `
        Set objErr = New errHandler
        Set objTrace = New TraceObject
        `
        objTrace.Push "Form_Load"
        `
        objErr.WriteLogFlag = True
        objErr.LogFileName = App.Path & "\" & App.EXEName & ".log"
        `
        objTrace.Pop
        `
    End Sub

Note the use of `objTrace.Push` to add the name of the method onto the trace stack. This should happen as early as possible in the method code. Note, also the `objTrace.Pop` line at the very end of the method. This removes the name of the method from the stack just as the method is complete.

Let's also add trace coding to the `Command1_click` event. Update your form's `command1_Click` event to match the one in Listing 14.38.

### Listing 14.38. Updating the Command1_click event to use module tracing.

    Private Sub Command1_Click()
        `
        On Error GoTo Localerr
        Dim varList As Variant
        Dim rtn As Long
        `
        objTrace.Push "Command1_Click"
        `
        Print 6 / 0
        `
        Exit Sub
        `
    Localerr:
        `
        rtn = objErr.errHandler(Err, erritem, errresume, "prjTest.Form1.Command1_Click")
        `
        objTrace.Show
        objTrace.Pop
        Resume Next
        `
    End Sub

Save this code and run the project. When you press the command button, you get a trace report on the screen (see Figure 14.21).
Figure 14.21. Viewing the trace message.
Notice that, in order to add module tracing to a project, you only need to add a `.Push` line at the start of the routine and a `.Pop` line at the end of the routine. This is all you need to do in order to update the procedure stack for the program. But, for this to be really valuable, you have to do this for each routine that you want to track.

In a real application environment, you wouldn't want to show the procedure stack each time an error is reported. The best place for a stack dump is at exit time due to a fatal error. You should probably use the TraceFile option to write the stack to disk rather than displaying it to the user.

#### Other Error Handler Options

Now that you have the basics of error handling under your belt, you can continue to add features to the generic error handler. As you add these features, your programs take on a more professional look and feel. Also, using options such as error report logs and procedure stack logs makes it easier to debug and maintain your applications.

Additional features that you can add to your error handler include:

- Add the name of the user or workstation address to the reports.

- If you have created an error trap for common errors, such as Error 53--File not found, add that recovery code to your generic handler. Now you can count on consistent handling of common errors without adding code to every project.

### Summary

Today's lesson covered all the basics of creating your own error-handling routines for Visual Basic applications. You learned that an error handler has three basic parts:

- The `On Error Goto` statement

- The body of the error handler code

- The error handler exit

You learned that an error handler has four possible exits:

- `Resume`: Re-executes the code that caused the error.

- `Resume Next`: Continues processing at the line immediately following the code line that caused the error.

- `Resume label`: Continues processing at the location identified by the label.

- `EXIT` or `END`: `EXIT` ends processing for the current routine, and `END` exits the program completely.

You learned how to use the `Err.Raise` method to flag errors without resorting to modal dialog boxes.

You learned about the major types of errors that you are likely to encounter in your program:

- General file errors: These include errors such as `File not found` and `Invalid path`. Errors of this type can usually be fixed by the user and then the operation retried. Use `Resume` as an exit for these types of errors.

- Database errors: These include errors related to data entry mistakes, integrity violations, and multiuser-related errors, such as locked records. Errors of this type are best handled by allowing the user to correct the data and attempt the operation again. If you use the Visual Basic data control, you do not have to write error handlers--the data control handles them for you. For operations that do not use the data control, you need to write your own error-handling routines.

- Physical media errors: These errors relate to device problems, such as unresponsive printers, downed communications ports, and so on. Sometimes users can fix the problems and continue (such as refilling the paper tray of the printer). Other times, users cannot fix the problem without first exiting the program. It is a good idea to give users the option of exiting the program safely when errors of these types are reported.

- Program code errors: These errors occur because of problems within the Visual Basic code itself. Examples of program code errors include `Object variable not set` and `For loop not initialized`. Usually the user cannot do anything to fix errors of this type. It is best to encourage the user to report the error to the system administrator and exit the program safely.

You also learned that you can declare a global error handler or a local error handler. The advantage of the global error handler is that it allows you to create a single module that handles all expected errors. The disadvantage is that, due to the way Visual Basic keeps track of running routines, you are not able to resume processing at the point the error occurs once you arrive at the global error handler. The advantage of the local error handler is that you are always able to use `Resume`, `Resume Next`, or `Resume label` to continue processing at the point the error occurs. The disadvantage of the local error handler is that you need to add error-handling code to every routine in your program.

Finally, you learned how to create an error handler object library that combines local error trapping with global error messages and responses. The error handler object library also contains modules to keep track of the procedures currently running at the time of the error, a process for printing procedure stack dumps to the screen and to a file, and a process that creates an error log on file for later review.

### Quiz

  1. What are the three main parts of error handlers in Visual Basic?
  2. What are the four ways to exit an error handler routine?
  3. When would you use `Resume` to exit an error handler?
  4. When would you use `Resume Next` to exit an error handler?
  5. When would you use `Resume label` to exit an error handler?
  6. When would you use the `EXIT` or `END` command to exit an error handler?
  7. List the four types of Visual Basic errors.
  8. Should you use error trapping for the Visual Basic data control?
  9. In what Visual Basic event should you open data tables or files in which the user enters data?
  10. What are the advantages and disadvantages of global error handlers?
  11. What is the `Err.Raise` method and why is it useful?

### Exercises

  1. Create a new project and add code to a command button that opens the file `C:\ABC.TXT`. Include an error handler that notifies the user that the file cannot be opened, and then terminates the program.
  2. Modify the project started in Exercise 1 by adding a new command button. Attach code to this button that attempts to load a file named `C:\ABC.TXT`. Notify the user that this file cannot be opened, and give the user the option and the dialog box to search for the file. Exit the program when a selection has been made or if the user chooses not to proceed.
  Run this program and elect to find the file. Cancel out of any common dialogs that appear. After this, create the file using Notepad and run the process again. Finally, move the file to a location other than the C drive and run the program. Use the common dialog to search for and select the file.

| Use of this site is subject certain Terms & Conditions. Copyright (c) 1996-1999 EarthWeb, Inc.. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Please read our privacy policy for details.