VB Error Handling

Visual Basic Error Handling
The various functions, statements, properties and methods that are available in Visual Basic and the available components used in Visual Basic expect (to deal with) certain types of data and behavior in your application. For example, the CDate function can convert a value to a Date variable. This function is very flexible interms of the type of information it can accept, but it also expects to receive data that it can use to derive a date. If you provide input that the function can't convert, then it generates error number 13 - "Type mismatch" - in this way, the function is essentially saying "I can't handle this type of data."

This type of error may be a program logic error (where, the wrong data was passed) or it may be a data entry error on the part of the user (you asked for a date but the user typed a word). In the first case, the program needs to be debugged in order to fix the mistake. However, there is no way for the programmer to anticipate the behavior of the end users of the application. If the user enters data you can't handle, you need to deal with the situation (by validation and error handling).

Dealing with errors at run-time is a two step process:
 * 1) Trap the Error - Before you can deal with an error, you need to know about it. Use VB's On Error statement to setup an error trap.
 * 2) Handle the Error - Code in your error handler may correct an error, ignore it, inform the user of the problem, or deal with it in some other way. You can examine the properties of the Err object to determine the nature of the error. Once the error has been dealt with, you use the Resume statement to return control (execution) to the regular flow of the code within the application.

In addition to dealing with run-time errors, at times you may want to generate them. This is often done in class modules built as components of ActiveX server, DLLs (Dynamic Link Library) or EXEs (Executables). It is considered as a good programming practice to separate the graphical user interface from the program flow logic as much as possible, so if a server component cannot deal with an error, it should raise the error in its client application rather than simply to display an error message for the user.

Trapping Errors at Run-Time
Before dealing with a run-time error, it (the error) first needs to be captured. The On Error statement is used to enable an error trap. On Error will redirect the execution to another statement in the event of a run-time error. There are several forms of the On Error statement:

On Error Goto label
This type of the On Error statement redirects program execution to the specified line label (which contains a block of error handling code). The block of error handling code is constructed following (after) the label.

Example: On Error Goto ProcError ' Error trapping and redirecting MsgBox FuncA ProcError: MsgBox Err. Description 'Label followed by colon then labels code which is an error description as trapped Sample code: Public SubAra On Error Goto ProcError ' other code MsgBox FuncADate ProcExit: Exit Sub ProcError: MsgBox Err. Description Resume ProcExit End Sub Private Function FuncADate As Date FuncADate = CDate ("hi there") End Function In the above example, procedure SubAra enables an error handler using the statement On Error Goto ProcError. When function FuncADate is called in the MsgBox statement, the On Error Goto ProcError handler is still enabled. The CDate function in FuncADate will generate error 13 (type mismatch) because CDate can't make a date from the input data. VB first looks in FuncADate for an error handler. None was enabled, so the error is propogated back up the call tree to SubAra. Since there is an error handler in SubAra, program execution is redirected to the ProcError label in SubAra. The MsgBox statement displays a description of the error and the Resume statement directs VB to continue execution at the ProcExit label.

On Error Resume Next
This form of the On Error statement directs Visual Basic to continue with the line of code succeeding the line where the error occurred. With this type of error trap, it is important to test for an error at selected points in the program code where you anticipate that an error may occur.

Example: Public SubV SubA On Error Resume Next ' other code MsgBox FuncDate ProcExit: Exit Sub End Sub Private Function FuncDate As Date FuncDate = CDate ("hi there") End Function

On Error Goto 0
On Error Goto 0 disables any error handler within the current procedure (the procedure within which it occurs). If a run-time error occurs when no error handler is enabled or after an On Error Goto 0 is encountered then it will be handled using Visual Basic's default error handling logic.

NB: It is not necessary to code an error handling routine in every procedure that you write in Visual Basic. If an error is raised in a procedure, Visual Basic will work its way back up through the call tree looking for an error handler. There are some situations where VB cannot pass an error back up the call tree. This applies to Sub Main, most event procedures, and the Class_Terminate event procedure. Sub Main (if defined in the project property sheet) is the first code executed, so there is no procedure higher in the tree at application startup time. Most event procedures are also fired by Visual Basic when no other code is running so these are also at the top of the tree. The Class_Terminate event of class modules cannot raise an error because this event can also occur when there is no other code executing in the program. When an error is generated in one of these type of procedures and no error handler is specified or enabled, then by default, VB invokes its own default error handler and displays a message, and then terminates the application. Because of this behavior, it is vital that an error handler is always to be coded in; Sub Main, all event procedures, and the Class_Terminate event for class modules.

''Unlike the Class_Terminate event, the Class_Initialize event of a class module can raise an error or allow the error to go untrapped. However, it is considered as a good programming practice to have the classes trap their own errors making it simpler for maintenance and the user to deal with them if possible. If necessary raise errors explicitly, providing an error number and description defined within the class.

Classes can be coded to map any error the class encounters to the class-defined error numbers, but sometimes given the large number of potential errors that may occur in an application, it may not always be practical. The alternative is to have the class to assign specific numbers and descriptions for errors that are specific to problems with the code or to data in the class e.g for a value that violates a rule for the data, but to output / generate the standard Visual Basic errors (such as error 13 - for type mismatch) as it is.

This practice allows applications using the class to explicitly handle the errors exclusive to the class with a customized code, but at the same time to handle the standard Visual Basic errors with more generic code. Regardless of the approach you take, you must always ensure that private data within the VB class is valid and that the code that is within the class cleans up any local or module level object variables that it creates. This may require you to setup an error handler that traps errors, cleans up local object variables, and then raises the same error again.''

Building the Error Handlers
Trapping an error using the On Error statement is only but the first step in dealing with runtime errors in the VB code. You must also deal with the error in some way, even if the error handling code is as simple as ignoring the error or just displaying a message for the user. The 1st in handling an error is by first determining the nature of the error. This can be accomplished by examining the properties of Visual Basic's Err object. The Err object includes the following properties:

• Number - This is the number of the error that was raised.

• Description - It is a descriptive message about the error. Depending on the type of error, the description may or may not be useful.

• Source - The Source property tells you the object that generated the error.

• HelpContext - If there is a help file defined for the component that raised the error, this property will give you the help context ID. This property along with the HelpFile property can be used to display context sensitive help for errors in your application or as a debugging aid.

• HelpFile - Is the name of the help file that contains additional information about the error.

It is important that you rely only on the error number to determine the nature of the error. While the Description and other properties may contain useful information, only the Number property is a reliable indicator of the exact error that occurred. In coding an error handler the common approach is to build a Select Case block based on the Number property of the Err object: Public Sub SubEra On Error Goto placeError ' code that raises an error placeExit: Exit Sub placeError: Select Case Err.Number Case X ' handle X Case Y ' handle Y Case Z ' handle Z Case Else ' default MsgBox Err.Description Resume placeExit End Select End Sub X, Y, and Z may be literal numbers (Case 13 ' type mismatch) or, if they are available, they may be symbolic constants representing the numbers.

When you have trapped and handled the error, you need to tell Visual Basic where to continue with program execution from. There are several options available, when an error handling block is entered using On Error Goto label: statement, you can use: • Resume - The Resume statement tells VB to continue execution from the line that generated the error.

• Resume Next - Resume Next instructs Visual Basic to continue execution from the line succeeding (following) the line that generated the error. This allows the program to skip the offending code.

• Resume label - This allows the redirection of the program execution to any label within the current procedure. The label may be a location that contains special code to handle the error, an exit point designed to perform clean up operations, or any other point that you may choose.

• Exit - Exit Sub, Exit Function, or Exit Property can be used to break out of the current procedure and continue execution at whatever point you were at when the procedure was called.

• End - This statement is not recommended, but it can be used to immediately terminate the application. If End is used, the application is forcibly terminated, in this case, no Unload, QueryUnload, or Terminate event procedures will be fired.

In addition to these statements, the Clear method of the Err object can be called to clear the current error. This method is most often used with inline error handling, as shown below: Public Sub flnFileCreater(flnNamedFile As String) On Error Resume Next ' the succeeding line will raise an error if the file doesn't exist, ' the point is to kill it if it does anyway. Kill flnNamedFile Err.Clear 'insert the code to create a file End Sub NBThere are many other things besides a file that doesn't exist that could cause the Kill statement to fail. The file may be read-only, there may be a network permissions error, or some other problem.

Handling of Errors that You Can't Handle
The error handling code might be as simple as a message to the user such as "This field requires a data earlier than today." However, in some instances, you may encounter errors that you didn't anticipate but you must find a way of dealing with them.

There are two general approaches can be taken in the handling of unprecedented errors:

1. You assume that the error is not fatal to the application. This is because most errors are not fatal to an application. The error may be as a result of wrong data being provided by the user, a file not being found, etc. These types of errors can easily be corrected by the user making the application to continue. A default case in an error handler can simply display a message and exit the current procedure or continue.

2. You assume that the error is really fatal and that the application must be terminated. Any error may be dangerous to an application (an application killer). This is rare because this kind of error should be explicitly handled, if necessary by providing the tools or information necessary in correcting the situation to the user. But, if a situation occurs where an non-anticipated error is fatal, then clean it up before you shut down the application by unloading all forms and releasing any object variables you have created. You should try to avoid the latter situation by all means. If it is a must for an application to be terminated due to some disastrous situation, provide as much information to the user as you can so that the situation can be resolved.

Code your error handlers so that they are able to call the code that corrects these severe problems when they occur. When designing a database application create the code in such a way that when a corrupted database file encountered, the error handling code is able to give the user the option of attempting to repair the damaged file or if a file cannot be found in the location where it should be, then write code to either search for it or provide the user with a file open dialog box to find it.

Raising Your Own Errors
At times you may need to generate errors in your code, this often happens in class modules, though you can also raise an error anywhere in a Visual Basic application. An error can be raised by calling the Raise method of the Err object. The parameters of the Raise method are the same as the properties of the Err object: Number, Description, Source, HelpContext, and HelpFile. The values that one provides for these parameters are available to the error handling code that deals with the error that you generate. When specifying that an error is raised, then make the information you provide via the Err object as informative as possible so that the error handling code that deals with the error has the best possible opportunity of handling it.

• Number - You can raise any of the standard VB error numbers or provide a number. When raising application-defined errors, then add the intrinsic constant vbObjectError to the number you raise so that the given number does not conflict with inbuilt error numbers.

• Description - Make the error description as informative as possible and if invalid data is provided by the user, it may be helpful to make that data as part of the error message.

• Source - The Source provides the name of the object that has generated the error. e.g if a Jet Database object raises an error, the Source property is "DAO.Database".

• HelpContext - When providing a help file with the component or application, then use the HelpContext parameter to provide the context ID. This can in turn be passed on to the MsgBox statement so that context sensitive help about the error is available.

• HelpFile - This is the name of the help file and is used in conjunction with the HelpContext parameter.

Example: ' in the general declarations section Private myDate As Date Public Enum myCLASSErrors errInvalidDate ' other errors End Enum ' a property procedure Public Property Let Date (vrnDate As Variant) ' a variant is used to allow the property ' procedure to attempt to convert the value ' provided to a date If IsDate (vrnDate) Then myDate = CDate (vrnDate) Else ' invalid data Err .Raise _ Number:=errInvalidDate, _ Description:= CStr (vrnDate) & " is an invalid date.", _ Source:="Foo.MyClass" ' help context and file go here if a help file is available End If    End Property In this example, the property procedure tests for a valid date using the IsDate function. If the data provided is not a date, an error is raised using the constant from the error enumeration in the declarations section of the class module and a description that includes the bad data and the nature of the error.

Reminder
All applications must be able to handle run-time errors if they are to be robust and reliable. The key points for error handling are: • There are two steps to handling run-time errors: 1. Use the On Error statement to trap the error and enable an error handler. 2. Handle the error first by examining the properties of the Err object and writing the code to deal with the problem. • Error handlers can be dedicated blocks of code enabled by using On Error Goto label or can be inline handlers enabled by using On Error Resume Next statement. • You can raise errors by calling the Raise method of the Err object. • Handle run-time errors rather than just informing the user of the problem, if nothing can be done to handle the error then display a message to the user and make it as informative as possible. • If possible avoid terminating the application.