KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

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.