Q194418: PRB: CallByName Fails to Return the Correct Error Information
Article: Q194418
Product(s): Microsoft Visual Basic for Windows
Version(s): WINDOWS:6.0
Operating System(s):
Keyword(s): kbGrpDSVB
Last Modified: 11-JAN-2001
-------------------------------------------------------------------------------
The information in this article applies to:
- Microsoft Visual Basic Learning Edition for Windows, version 6.0
- Microsoft Visual Basic Professional Edition for Windows, version 6.0
- Microsoft Visual Basic Enterprise Edition for Windows, version 6.0
-------------------------------------------------------------------------------
SYMPTOMS
========
When an error is raised in a procedure of an ActiveX server and the procedure is
called with the CallByName() function from a client, the client always gets
error 440 regardless of the original error number being raised.
RESOLUTION
==========
See the MORE INFORMATION section of this article for a workaround.
STATUS
======
This behavior is by design.
MORE INFORMATION
================
The following sample projects demonstrate the problem and a workaround:
Create a Test Server
--------------------
1. Start Visual Basic and create a new ActiveX DLL project. Class1 is created by
default.
2. Rename the project TestSvr.
3. Add the following code to Class1:
Option Explicit
Private strObjName As String
Private Sub Class_Initialize()
strObjName = "Default Name"
End Sub
Public Property Get ObjectName() As String
ObjectName = strObjName
End Property
Public Property Let ObjectName(s As String)
strObjName = s
End Property
Public Property Get ObjectNameWithErr() As String
ObjectNameWithErr = strObjName
' User-defined error numbers should be between 513 and 65535
' plus vbObjectError Numbers below 513 are reserved.
' The vbObjectError is equivalent to FACILITY_ITF (&H80040000).
' You need to add this constant because everything made public
' in VB is on an interface.
' We are arbitrarily choosing 1000 below.
Err.Raise vbObjectError + 1000, _
"Error in " & App.EXEName & ".ObjectNameWithErr", _
"User defined error"
End Property
Public Property Let ObjectNameWithErr(s As String)
strObjName = s
Err.Raise vbObjectError + 1000, _
"Error in " & App.EXEName & ".ObjectNameWithErr", _
"User defined error"
End Property
Public Function ConcatString _
(s1 As String, s2 As String) As String
ConcatString = s1 & s2
End Function
Public Function ConcatStringWithErr _
(s1 As String, s2 As String) As String
ConcatStringWithErr = s1 & s2
Err.Raise vbObjectError + 1000, _
"Error in " & App.EXEName & ".ConcatStringWithErr", _
"User defined error"
End Function
4. Compile the project. If you want to run the server in the IDE, select Options
from the Tools menu, and select "Break on Unhandled Errors" under "Error
Trapping" on the General Tab.
Create a Test Client
--------------------
1. Create a new Standard EXE project in Microsoft Visual Basic. Form1 is created
by default.
2. Select References from the Project menu, and add "TypeLib Information"
(TLBINF32.DLL) to the reference list.
3. Add a ComboBox, two CommandButtons, a Frame, and two OptionButtons inside the
frame to Form1.
4. Add the following code to Form1's code window:
Option Explicit
Private objServer As Object
Private IFaceInfo As TLI.InterfaceInfo
Private Sub Command1_Click()
' This subroutine demonstrates the problem with returned errors
' when CallByName() is used.
On Error GoTo Command1Handler
Select Case Combo1.ListIndex
Case 0, 2
If Option1.Value Then
Call CallByName(objServer, _
Combo1.Text, _
VbLet, _
App.EXEName)
Else
MsgBox CallByName(objServer, _
Combo1.Text, _
VbGet)
End If
Case 1, 3
MsgBox CallByName(objServer, _
Combo1.Text, _
VbMethod, _
"Parameter1", _
"Parameter2")
End Select
Exit Sub
Command1Handler:
MsgBox Err.Number & vbCrLf & Err.Source & vbCrLf & Err.Description
End Sub
Private Sub Command2_Click()
' This subroutine demonstrates a workaround for the problem
' shown in the Command1_Click subroutine.
Dim sResults As TLI.SearchResults
Dim sItem As TLI.SearchItem
On Error GoTo Command2Handler
Set IFaceInfo = TLI.InterfaceInfoFromObject(objServer)
Set sResults = IFaceInfo.Members.GetFilteredMembers
With sResults
For Each sItem In sResults
If sItem.Name = Combo1.Text Then
Select Case Combo1.ListIndex
Case 0, 2
If Option1.Value = True Then
Call TLI.InvokeHook(objServer, _
sItem.MemberId, _
INVOKE_PROPERTYPUT, _
App.EXEName)
Else
MsgBox TLI.InvokeHook(objServer, _
sItem.MemberId, _
INVOKE_PROPERTYGET)
End If
Case 1, 3
Dim vArg(1) As Variant
vArg(0) = "Parameter2": vArg(1) = "Parameter1"
' The variant array elements are passed in reverse
' order, that is the 0th element first
MsgBox TLI.InvokeHookArray(objServer, _
sItem.MemberId, _
INVOKE_FUNC, _
vArg)
End Select
End If
Next
End With
Exit Sub
Command2Handler:
' Please note that the error number will be the raw error number
' raised in the server.
' To get the equivalent error number of VB, subtract vbObjectError
' from Err.Number.
MsgBox (Err.Number - vbObjectError) & vbCrLf & Err.Source & _
vbCrLf & Err.Description
End Sub
Private Sub Form_Load()
Command1.Caption = "&CallByName"
Command2.Caption = "&InvokeHook"
Option1.Caption = "Property &Let"
Option2.Value = True
Option2.Caption = "Property &Get"
With Combo1
.AddItem "ObjectName"
.AddItem "ConcatString"
.AddItem "ObjectNameWithErr"
.AddItem "ConcatStringWithErr"
.ListIndex = 0
End With
Set objServer = CreateObject("TestSvr.Class1")
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set objServer = Nothing
End Sub
5. Run the project.
When you access the ObjectNameWithErr property or call the ConcatStringWithErr
function using CallByName(), the error object you received has the following
error information regardless of the error raised in the server:
Number: 440
Source: Name of the application
Description: Automation error
To get the correct error object, use the InvokeHook set of functions as shown
in the Click event of Command2.
REFERENCES
==========
Additional information on TLBINF32.DLL can be found in the article "Inspect
Dynamic Objects" by Matthew Curland in the November 1998 issue of Visual Basic
Programmer's Journal, pages 120-124
For additional information, please see the following article in the Microsoft
Knowledge Base:
Q172988 : FILE: Programmatically Retrieve the Members of a DLL Class
Additional query words: kbDSupport kbVBp kbdss kbNoKeyWord
======================================================================
Keywords : kbGrpDSVB
Technology : kbVBSearch kbAudDeveloper kbZNotKeyword6 kbZNotKeyword2 kbVB600Search kbVBA600 kbVB600
Version : WINDOWS:6.0
Issue type : kbprb
=============================================================================
THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.
Copyright Microsoft Corporation 1986-2002.