6.3. Error Handling in ActiveX Servers

While the basic error-handling concepts are the same, errors within an ActiveX server class have to be handled slightly differently than errors in a client application. Some of the important differences include:


Don't display the error

Unlike a client application, in which you might display a message box to the user detailing the problem, you should remember that with a server-side application, there might be no one there to click OK! Instead, you should write an entry into an event log. (Section 6.4 details how to write an event log.) If you have a large application that already has many MsgBox calls, and you don't want to spend ages rewriting this code, simply go to the project properties dialog and select the Unattended Execution option. This forces all MsgBox calls to be written to an event log.


Use Err.Raise

Once you've logged the error in your server class, you need some way of informing the user that an error occurred. The simplest method of doing this is to raise an error using the Err.Raise method. This error will be picked up by the client's error handler, and the relevant message displayed. This simple client and server code demonstrates the Err.Raise method:

Client code:

Private Sub Command3_Click()

   On Error GoTo Command3_Err

   Dim oTest As TestErrors.DoStuff
   Set oTest = New TestErrors.DoStuff
      oTest.SomeStuff
   Set oTest = Nothing
Command3_Err:
   MsgBox Err.Description & vbCrLf & Err.Number & _
          vbCrLf & Err.Source

End Sub

Server code:

Public Function SomeStuff() As Boolean

   On Error GoTo SomeStuff_Err
    
   Dim i As Integer
   i = 100
   i = i / 0
    
   Exit Function

SomeStuff_Err:
   App.LogEvent Err.Description & " in " & _
               "TestErrors::SomeStuff", _
               vbLogEventTypeError
   Err.Raise vbObjectError + Err.Number, _
             "TestErrors::SomeStuff", Err.Description
End Function

As you can see from the server code, an illegal operation takes place—you can't divide by zero. The error handler logs the event and then uses the Err.Raise method to pass this error on to the client.

You can also use the Err.Raise method to trap incorrect user input. Look at MoreStuff, the following modified version of the server SomeStuff function:

Public Function MoreStuff(iVal As Integer) As Integer

   On Error GoTo MoreStuff_Err
    
   If iVal > 50 Then
      Err.Raise 23456, "TestErrors::MoreStuff", _
                "Can't have a figure greater than 50"
   End If
        
   iVal = iVal / 0
    
   Exit Function

MoreStuff_Err:
   App.LogEvent Err.Description & " in " & _
                "TestErrors::MoreStuff", _
                vbLogEventTypeError
   Err.Raise vbObjectError + Err.Number, _
             "TestErrors::MoreStuff", Err.Description

End Function

This time, the Err.Raise method passes back a custom error code and description alerting the user of the invalid input.


Use the vbObjectError constant

Errors generated in OLE objects start at –2147221504. (Actually, OLE automation errors aren't negative numbers; they are unsigned long integers. But because VB doesn't support unsigned longs, they appear as a negative.) VB provides vbObjectError, an intrinsic constant for this value that you should add to both custom error codes and system error codes. However, if you add vbObjectError to an error code that is already of greater negative magnitude than –262144, you generate an "Overflow" error that masks the real error that occurred in the procedure. Because of this, it's best if your error handler adds vbObjectError only to positive numbers, as this snippet demonstrates:

SomeStuff_Err:
   App.LogEvent Err.Description & " in " & _
                "TestErrors::SomeStuff", _
                vbLogEventTypeError
   If Err.Number < 0 Then
      Err.Raise Err.Number, "TestErrors::SomeStuff", _
                Err.Description
   Else
      Err.Raise vbObjectError +  Err.Number, _
                "TestErrors::SomeStuff", Err.Description
End If


Where did the error occur?

Passing back the source of the error to the client application is very important. However, unless you take care writing your error handler, you can report the source as the last procedure to handle the error, rather than the one in which the error occurred. This example shows how this can happen:

Public Function SomeStuff() As Boolean

   On Error GoTo SomeStuff_Err
    
   Dim i As Integer
   i = 100
   i = MoreStuff(i)
    
   Exit Function

SomeStuff_Err:
   App.LogEvent Err.Description & " in " & _
                "TestErrors::SomeStuff", _
                vbLogEventTypeError
   If Err.Number < 0 Then
      Err.Raise Err.Number, _
                "TestErrors::SomeStuff", Err.Description
   Else
      Err.Raise vbObjectError + Err.Number, _
                "TestErrors::SomeStuff", Err.Description
   End If
End Function

Private Function MoreStuff(iVal As Integer) As Integer

   On Error GoTo MoreStuff_Err
    
   iVal = iVal / 0
    
   Exit Function

MoreStuff_Err:
   Err.Raise vbObjectError + Err.Number, _
             "TestErrors::MoreStuff", Err.Description

End Function

In this example, SomeStuff has called MoreStuff, in which an error is raised. The error is first handled by the error handler in MoreStuff, which reports the source of the error correctly. The error is then handled by SomeStuff 's error handler, which overwrites the original Source argument and reports it as being SomeStuff. You would wrongly assume that your problem was with the SomeStuff function.

If a system-generated error occurs, the source property of the Err object is set to the same value as the App.Title property. So you know that if the Err.Source property is the same as App.Title, the error has been generated in the current procedure, and you can safely use your own custom Source string. However, if the Err.Source property differs from the application title, the current Err.Source property should be passed on, as this snippet shows:

Dim ErrSrc As String
If Err.Source <> App.Title Then
   ErrSrc = Err.Source
Else
   Err.Src = App.Title & "::SomeStuff"
End If


Don't forget to clean up before you leave

If your procedure exits unexpectedly, you run the risk of leaving objects that have been used in the procedure live. To prevent this, you should get into the habit of adding code to the beginning of your error handler to set all object variables that are declared in the procedure to Nothing. This way, whenever an error occurs, you will be sure that all objects are safely destroyed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset