Error Handling with ActiveX Components

See Also

In applications that use one or more objects, it becomes more difficult to determine where an error occurs, particularly if it occurs in another application's object. For example, Figure 13.4 shows an application that consists of a form module, that references a class module, that in turn references a Microsoft Excel Worksheet object.

Figure 13.4   Regenerating errors between forms, classes, and ActiveX components

If the Worksheet object does not handle a particular error arising in the Worksheet, but regenerates it instead, Visual Basic will pass the error to the referencing object, MyClassA. When an error is raised in an external object and it is untrapped, it will be raised in the procedure that called the external object.

The MyClassA object can either handle the error (which is preferable), or regenerate it. The interface specifies that any object regenerating an error that arises in a referenced object should not simply propagate the error (pass the error code), but should instead remap the error number to something meaningful. When you remap the error, the number can either be a number defined by Visual Basic that indicates the error condition, if your handler can determine that the error is similar to a defined Visual Basic error (for instance, overflow or division by zero), or an undefined error number. Add the new number to the intrinsic Visual Basic constant vbObjectError to notify other handlers that this error was raised by your object.

Whenever possible, a class module should try to handle every error that arises within the module itself, and should also try to handle errors that arise in an object it references that are not handled by that object. However, there are some errors that it cannot handle because it cannot anticipate them. There are also cases where it is more appropriate for the referencing object to handle the error, rather than the referenced object.

When an error occurs in the form module, Visual Basic raises one of the predefined Visual Basic error numbers.

Note   If you are creating a public class, be sure to clearly document the meaning of each non-Visual Basic error-handler you define. (Public classes cannot be created in the Learning Edition.) Other programmers who reference your public classes will need to know how to handle errors raised by your objects.

When you regenerate an error, leave the Err object's other properties unchanged. If the raised error is not trapped, the Source and Description properties can be displayed to help the user take corrective action.

Handling Errors in Objects

A class module could include the following error handler to accommodate any error it might trap, regenerating those it is unable to resolve:

MyServerHandler:
   Select Case ErrNum
      Case 7      ' Handle out-of-memory error.
         .
         .
         .
      Case 440      ' Handle external object error.
         Err.Raise Number:=vbObjectError + 9999
      ' Error from another Visual Basic object.
      Case Is > vbObjectError and Is < vbObjectError _
      + 65536
         ObjectError = ErrNum
      Select Case ObjectError
         ' This object handles the error, based on
         ' error code documentation for the object.
         Case vbObjectError + 10
         .
         .
         .
         Case Else
            ' Remap error as generic object error and
            ' regenerate.
            Err.Raise Number:=vbObjectError + 9999
         End Select
      Case Else
         ' Remap error as generic object error and
         ' regenerate.
         Err.Raise Number:=vbObjectError + 9999
   End Select
   Err.Clear
   Resume Next

The Case 440 statement traps errors that arise in a referenced object outside the Visual Basic application. In this example, the error is simply propagated using the value 9999, because it is difficult for this type of centralized handler to determine the cause of the error. When this error is raised, it is generally the result of a fatal automation error (one that would cause the component to end execution), or because an object didn't correctly handle a trapped error. Error 440 shouldn't be propagated unless it is a fatal error. If this trap were written for an inline handler as discussed previously in the topic, "Inline Error Handling," it might be possible to determine the cause of the error and correct it.

The statement

Case Is > vbObjectError and Is < vbObjectError + 65536

traps errors that originate in an object within the Visual Basic application, or within the same object that contains this handler. Only errors defined by objects will be in the range of the vbObjectError offset.

The error code documentation provided for the object should define the possible error codes and their meaning, so that this portion of the handler can be written to intelligently resolve anticipated errors. The actual error codes may be documented without the vbObjectError offset, or they may be documented after being added to the offset, in which case the Case Else statement should subtract vbObjectError, rather than add it. On the other hand, object errors may be constants, shown in the type library for the object, as shown in the Object Browser. In that case, use the error constant in the Case Else statement, instead of the error code.

Any error not handled should be regenerated with a new number, as shown in the Case Else statement. Within your application, you can design a handler to anticipate this new number you've defined. If this were a public class (not available in the Learning Edition), you would also want to include an explanation of the new error-handling code in your application's documentation.

The last Case Else statement traps and regenerates any other errors that are not trapped elsewhere in the handler. Because this part of the trap will catch errors that may or may not have the vbObjectError constant added, you should simply remap these errors to a generic "unresolved error" code. That code should be added to vbObjectError, indicating to any handler that this error originated in the referenced object.

Debugging Error Handlers in ActiveX Components

When you are debugging an application that has a reference to an object created in Visual Basic or a class defined in a class module, you may find it confusing to determine which object generates an error. To make this easier, you can select the Break in Class Module option on the General tab of the Options dialog box (available from the Tools menu). With this option selected, an error in a class module or an object in another application or project that is running in Visual Basic will cause that class to enter the debugger's break mode, allowing you to analyze the error. An error arising in a compiled object will not display the Immediate window in break mode; rather, such errors will be handled by the object's error handler, or trapped by the referencing module.

For More Information   For a thorough discussion of the Break in Class Module option, see "Debugging Class Modules" in "Programming with Objects."