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. (The following section, "Reporting Errors," 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

Dim oTest As TestErrors.DoStuff Set oTest = New TestErrors.DoStuff oTest.SomeStuff Set oTest = Nothing

Command3_Err:

MsgBox Err.Description & vbCrLf & Err.Number & _

drr 1 3

vbCrLf & Err.Source

End Sub

«a

Server code:

Public Function SomeStuff() As Boolean

Public Function SomeStuff() As Boolean

On Error GoTo SomeStuff_Err

Exit Function

SomeStuff_Err:

App.LogEvent Err.Description & " in " & "TestErrors::SomeStuff", _ vbLogEventTypeError

Error Handling in ActiveX Servers 101

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"

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, _

102 Chapter 6- Error Handling

"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

Error Handling in ActiveX Servers 103

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.

Was this article helpful?

0 0

Post a comment