Visual Basic Concepts

Handling Errors in a Component

See Also

When authoring a component, you should be prepared to handle three types of errors:

Handling Errors Internally

Handling the first of these three error types is no different for a component than for any other application developed using Visual Basic. This type of error handling is covered in "Debugging Your Code and Handling Errors," in the Visual Basic Programmer’s Guide.

Passing Errors Back to the Client

As outlined in "Raising Errors from Your Component," you can use the Raise method of the Err object to raise errors in a client application that calls methods provided by your component. For the error to be raised in the client, you must call Raise from your method’s error-handling routine, or with error handling disabled, as in the following fragment of code from the Run method of a hypothetical Widget object.

Public Sub Run()
   ' Use in-line error handling.
   On Error Resume Next
   ' (Many lines of Run method code omitted.)
   ' If the following test fails, raise an error in
   ' the client that called the method.
   If intWidgetState <> STATE_RUNNING Then
      ' Disable in-line error handling.
      On Error Goto 0
      Err.Raise _
         Number:=(ERR_WDG_HALTED + vbObjectError), _
         Source:=CMP_SOURCENAME, _
         Description:=LoadResString(ERR_WDG_HALTED)
   End If
   ' (Run method code continues.)
End Sub

When you raise errors for invalid parameter values, you can simply place the Err.Raise statements at the beginning of the method, before the first On Error statement. The same is true of code to validate property values; place it at the beginning of the Property Let or Property Set.

Raising Errors from Error Handlers

Frequently your properties and methods will include code to handle internal errors. Such code may from time to time contain cases in which no sensible internal response is possible. In such cases, it’s reasonable for the property or method to fail and return an error to the client:

   On Error Goto ErrHandler
   ' (Code omitted.)
   Exit Sub

ErrHandler:
   Select Case Err.Number
      ' Errors that can be handled (not shown).
      Case 11
         ' Division by zero cannot be handled in any
         ' sensible fashion.
         Err.Raise ERR_INTERNAL + vbObjectError, _
            CMP_SOURCENAME, _
            LoadResString(ERR_INTERNAL)
      ' Other errors (not shown).
   End Select

In most cases it doesn’t make sense to return the original "Divide by zero" error to the client. The client has no way of knowing why such an error occurred within your property or method, so the message is of no use in finding a solution.

If the developer of the client was aware of the possibility of this internal error, because of the excellent documentation you provided, he may have included code to handle it, and shown the end user of the application a useful error message.

If the developer didn’t handle it, ERR_INTERNAL might at least serve a diagnostic purpose by including the method parameters in the error message text, as information to pass on to the author of the component.

Handling Errors from Another Component

If your component uses objects provided by another component, you must handle errors that may result when you call methods of those objects. You should be able to obtain a list of these errors from the second component’s documentation.

It’s important to handle such errors within your component, and not return them to the client application that called your component. The client was written to use your component, and to respond to your component’s error codes. The user or programmer who wrote it may have no knowledge of secondary components you’re using.

Encapsulation of Errors

The idea that a client application should receive errors only from the components it calls directly reflects the object-oriented concept of encapsulation. In the case of errors, encapsulation means that an object is self-contained. Although the object in your component may use other objects supplied by other components, the client application is ignorant of those objects.

For this reason, you should always specify the source argument of the Raise method when you handle an error from another component. Otherwise, the Source property of the client application’s Err object will contain the name of the component your component called.

Note   You should not use the value of the Source property of the Err object for program flow decisions. The Source property may provide useful context information when you’re debugging your component, but the text it contains may be version dependent.

The following code fragment shows the error handler for a method that uses objects in another component. When it encounters an error it cannot handle, or an unanticipated error, the error handler raises an error for the client, using its own error numbers and descriptions.

' Code from the Spin method of a hypothetical Widget
' object.
Public Sub Spin(ByVal Speed As Double)
   On Error Goto ErrHandler
   ' Code containing calls to objects in the Gears 
   ' component (omitted).
   Exit Sub

ErrHandler:
   Select Case Err.Number
      ' Other errors (not shown).
      ' --- Errors from the Gears component. ---
      Case vbObjectError + 2000
         ' Error 2000 is handled by shifting to
         ' another gear (code not shown).
      Case vbObjectError + 3000
         ' Error 3000 causes spin-down; error must be
         ' returned to caller.
         Err.Raise ERR_SPN_SPINDOWN + vbObjectError, _
            CMP_SOURCENAME, _
            LoadResString(ERR_SPN_SPINDOWN)
      ' --- Unanticipated errors from components. ---
      Case vbObjectError To (vbObjectError + 65536)
         Err.Raise ERR_SPN_FAILURE + vbObjectError, _
            CMP_SOURCENAME, _
            LoadResString(ERR_SPN_FAILURE)
      ' Other errors (not shown).
   End Select
End Sub

Note   Some components use older error return mechanisms. Such components may return error numbers in the range 0–65535.

When the Gears component raises Error 3000, the Spin method must return an error to the application that called it. The error returned is an error of the Spin method. The application using the Spin method doesn’t know that Spin is implemented using the Gears component, and could not be expected to deal with Error 3000. All it needs to know is that a spin-down condition has occurred.

In the preceding example a global constant, CMP_SOURCENAME, is used for the source argument of the Raise method. If you raise errors outside an error handler, and do not specify the source argument, Visual Basic uses the name you entered in the Project Name field of the General tab in the Project Properties dialog box, combined with the Name property of the class module.

While this may be useful for you to know while debugging your component, it’s better for the compiled component to specify a consistent value for the source argument in all errors it raises.

Note   It’s frequently easier to use in-line error handling when making calls to components, because the same error may require a different response depending on the method that was called, or the circumstances in which it was called. If you’re calling two different components, they may use the same number for different errors.

Bending Encapsulation Rules

There may be cases in which some of the error messages returned by a secondary component may contain information of use to the end user of the client application. For example, an error message might contain the name of a file.

In such cases, you may want to bend encapsulation rules to pass along this information. The safest way to do this is to include the text of the Description property of the Err object in your own error message text, as in the following fragment of error-handling code:

' Error 3033 is returned from a call to another
' component; the message text contains a file name that
' will be useful in resolving the problem.
Case vbObjectError + 3033
   ' Raise a Print Run Failure error to the client,
   ' and append the message text from Error 3033.
   Err.Raise _
      ERR_PRINTRUNFAILURE + vbObjectError, _
      CMP_SOURCENAME, _
      LoadResString(ERR_PRINTRUNFAILURE) _
         & Err.Description

In some cases, it may be useful to the end user to know what component originated the error, and the original error number and message text. You can include all of this information in the description of the error you raise.

For More Information   See "Debugging Your Code and Handling Errors," in the Visual Basic Programmer’s Guide, or see Error or Err in the Language Reference.