The Central Error HandlerThe central error handler is the heart of any complex error handling system. It consists of a procedure designed to log errors to an error log file or other persistent location and display error messages to the user, as well as provide facilities that allow the programmer to debug errors during development. (We cover debugging in detail in Chapter 16 VBA Debugging.) The module containing the central error handler also contains all error handling-related constants, making the error handling system fully encapsulated. Listing 12-9 shows an example of a complete central error handler. Listing 12-9. A Central Error HandlerThis is a lot to digest, so let's dissect it piece by piece. First the constant declarations:gbDEBUG_MODE This public Boolean constant is used by the developer to set the debug mode status of the application. When you are testing your application or attempting to locate errors in your code, you want your error handlers to behave differently than they do when your application is deployed to end users. Setting the gbDEBUG_MODE constant to True causes the central error handler function to display an error message immediately after an error occurs and then return True.Listing 12-6 and Listing 12-7, when the central error handler function returns True, the procedure error handler drops into a VBA Stop statement followed by a Resume statement. The Stop statement puts the project into Break mode and the Resume statement enables you to single step back to the line of code in the procedure where the error occurred. You can then debug the error. Again, we discuss debugging in detail in Chapter 16 VBA Debugging.glHANDLED_ERROR This public Long constant is an error number you can use to raise custom errors. As we discussed in the section on raising custom errors, when you raise a custom error you must supply an error number not already used by Excel or VBA. The glHANDLED_ERROR constant has a value of 9999, which is not within the range of error number values used by VBA. It also has the advantage of being easily recognizable as a custom error number for debugging purposes. In all but the most complex error handling scenarios, a single custom error number can be used for all of your custom errors.glUSER_CANCEL This public Long constant is set to the VBA error value 18. This error value occurs when the user cancels program execution by pressing the Esc or Ctrl+Break keys. Unless it is absolutely critical that your program not be interrupted, such as during startup and shutdown, you should always allow the user to halt program execution. The best way to do this is to add the following statement at the beginning of each entry point procedure: This will cause VBA to treat a user cancel as a runtime error with an Err.Number = 18 that is routed through your error handler. When the central error handler sees this error number, it converts it into a special error message string, which we cover next, that causes the error to be ignored.msSILENT_ERROR This private String constant is assigned to the static error message variable in the central error handling function whenever a user cancel error (Err.Number = glUSER_ CANCEL) is detected. Because this error message variable is static, it holds its value between calls to the central error handler. This means no matter how deep in the call stack the program was when the user cancelled execution, the error handler will pass the error up the stack and out the entry point procedure without displaying an error message to the user. Silent errors also will not trigger the procedure debugging mechanism, even if the application is in debug mode.msFILE_ERROR_LOG This private String constant specifies the name of the text file to which all error messages will be written. The error log file will always be located in the same directory as the workbook containing the central error handler. The information stored in the error log file is designed to help you debug errors that have occurred on a user's computer that you may not necessarily have access to. The error log file will show you the exact error message, the procedure where the error originated and the call stack that led to the error. Combined with a brief verbal report from the user about exactly what they were doing when the error occurred, this information is usually sufficient to enable you to debug the problem. Now we examine the code in the central error handler function line by line to see how an error is treated under various conditions. First let's look at the arguments to the function. The first three arguments to the bCentralErrorHandler function identify the code module, procedure and filename from which the function was called. This information is written to the error log file for use in debugging runtime errors. The fourth argument to the bCentralErrorHandler function indicates whether or not it was called from an entry point procedure. If the application is not in debug mode, an error message is displayed to the user only when the error reaches the originating entry point procedure. If the application is in debug mode, the fourth argument is ignored, an error message is displayed immediately and the central error handler returns False so that you can begin debugging the error.Notice that within the bCentralErrorHandler function we have declared a static String variable. This variable is used to store the original error message so we can display it to the user when we reach the entry point, regardless of how many procedures deep in the stack we are when the error occurs. This static variable will hold its value until we explicitly change it.As soon as code execution has entered the bCentralErrorHandler function, we must read and store any information we need from the VBA Err object. The reason for this will become apparent very shortly. First we read and store the error number. Then, if the error number indicates that the user has cancelled program execution, we store the gsSILENT_ERROR flag message in our static error message variable. If the error number does not indicate a user cancel and the static error message variable does not already contain a value, we store the error description in the static error message variable. In this way, we store only the original error message and persist it through any additional calls to the central error handler function. The static variable is cleared only after the entry point procedure has been reached and the error message displayed to the user.The reason we must persist any necessary Err object values immediately upon entering the central error handler function is because we cannot allow any errors to occur in this function. Therefore the entire function is wrapped in On Error Resume Next: As soon as code execution passes the On Error Resume Next statement, all properties of the Err object are automatically cleared. If you have not stored the original values from the Err object's properties at this point, they are lost forever. In the next section of code we construct several String values that the error handler requires. You will notice from Listing 12-9 that the sFile argument is optional. If no value for this argument is passed, the central error handler assumes it is being called from within the current workbook and it loads this argument's value with ThisWorkbook.Name. The next task is to get the path to the current workbook. This is where the error log file will be created (or updated if it has already been created). In the next line of code we construct a fully qualified location that identifies where the call to the bCentralErrorHandler function originated. This location identifier has the following format: The last string we construct in this section is the complete error log file entry. This consists of the fully qualified location string created above, prefixed with the date and time the error occurred and suffixed with the error number and the error message. We will see examples of error log file entries later in this chapter.Our next task is to write the entry in the application error log file. As shown below, we use standard VBA file I/O techniques to create or append to the error log file: We first acquire an available file number and use it to create or open the error log file specified by the msFILE_ERROR_LOG constant and located in the path created in the previous section. We then write the log file entry string created above to the log file. If the bCentralErrorHandler function has been called by an entry point procedure, we write an additional blank line to the error log file to provide visual separation between this and subsequent errors. After that we close the error log file.The last section of the central error handler determines whether and when an error message is displayed and whether the central error handler triggers debug mode behavior in the procedure that called it. The primary deciding factor on when to display an error message and when to ignore it is the value of the static sErrMsg variable. Remember that this variable holds the value of the original error message that triggered the central error handler. If the value of this variable indicates that the original error was the result of the user canceling program execution, then no error message is displayed, the static error message variable is cleared to prepare the central error handler for the next error, and the return value of the central error handler is False, so as not to trigger any debug actions.If the static error message variable indicates any error other than a user cancel error, then an error message is displayed. If the application is in debug mode (gbDEBUG_MODE = True), an error message displays as soon as the error occurs and the central error handler returns True so the calling procedure can begin executing debug code. If the application is not in debug mode, an error message displays only when the error handling code reaches the entry point procedure. In this case, the central error handler function returns False throughout so as not to trigger any procedure-level debug code. |