none
Out-of-proc C++ COM server returns a IUnkown** as by ref argument in function. Need help getting the argument from VB.NET RRS feed

  • Question

  • I am using InvokeMember to call methods of an out-of-proc COM server. It works for a methods that return a "long*" as a by ref argument. However, several methods return a COM object and the argument is declared as "IUnknown**":

    .h
    afx_msg long closeAllDocument(LPUNKNOWN FAR* _ppitfEreur);
    .cpp
    long CNAVDocViewer::closeAllDocument(IUnknown* _ppitfEreur)
    {
    ...
    }

    I think I am doing the right thing in VB.NET, but I keep getting an DISP_E_TYPEMISMATCH exception:

       Public Sub closeAllDocument()
            Dim viewerType As Type = Type.GetTypeFromProgID("NAVDocViewer.NAVDocViewer")
            Dim viewerInstance As Object = Activator.CreateInstance(viewerType)
            Dim p As IntPtr = Nothing
            Dim args As Object() = New Object() {p}
            Dim paramModif As Reflection.ParameterModifier = New Reflection.ParameterModifier(1)
            Dim codeRetour As Long = 0
            paramModif.Item(0) = True
            Dim paramModifArray As Reflection.ParameterModifier() = {paramModif}
            Try
                viewerType.InvokeMember( _
                    "closeAllDocument", _
                    Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    viewerInstance, _
                    args, paramModifArray, Nothing, Nothing)
            Catch ex As Exception
            End Try
        End Sub

    Wednesday, April 18, 2012 7:43 PM

Answers

  • Hello Kéboo,

    1.  In situations like the one described in the OP, you need to use the UnknownWrapper class. The following is a sample based on code supplied in the OP :

            Dim viewerType As Type = Type.GetTypeFromProgID("NAVDocViewer.NAVDocViewer")
            Dim viewerInstance As Object = Activator.CreateInstance(viewerType)
            '' Instead of using an IntPtr type,
            '' use an UnknownWrapper type instantiated
            '' with a null Object.
            ''Dim p As IntPtr = Nothing
            Dim obj As Object = Nothing
            Dim instance As New UnknownWrapper(obj)
            Dim args As Object() = New Object() {instance}
            Dim paramModif As Reflection.ParameterModifier = New Reflection.ParameterModifier(1)
            Dim codeRetour As Long = 0
            paramModif.Item(0) = True
            Dim paramModifArray As Reflection.ParameterModifier() = {paramModif}
            Dim objRet As Object
            Try
                objRet = viewerType.InvokeMember( _
                    "closeAllDocument", _
                    Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    viewerInstance, _
                    args, paramModifArray, Nothing, Nothing)
    
                '' The IUnknown pointer will be contained in args(0).
                If Not (args(0) Is Nothing) Then
    		...
    		...
    		...
                End If
            Catch ex As Exception
            End Try

    2. At low level, what happened (when you used "p" the IntPtr) was that the COM server (I assume that it was written in MFC) expected the DISPPARAM to contain a VARIANT of type (VT_BYREF | VT_UNKNOWN).

    3. However, when you used "p", the CLR created a VARIANT (to be passed into the DISPPARAM) of type (VT_BYREF | VT_INT).

    4. This causes the MFC class (that implements the COM server) to reject the parameter as invalid.

    5. The UnknownWrapper will ensure that the CLR created a VARIANT (to be passed into the DISPPARAM) of type (VT_BYREF | VT_UNKNOWN).

    6. Lookup MSDN for more information on the UnknownWrapper class :

    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unknownwrapper.aspx

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Thursday, April 19, 2012 5:50 PM

All replies

  • Hi Keboo,

    Nice to see you.

    How about the suggestion which ghost posted in your another thread: http://social.msdn.microsoft.com/Forums/en-US/vbinterop/thread/98c27c3a-a1db-49b3-8a65-b6f0134cb852/#98c27c3a-a1db-49b3-8a65-b6f0134cb852

    Dim erreurDQS As aDQSErrorServr.DQSEror = New aDQSErrorServr.DQSEror
    
     Dim args As Object() = New Object() {erreurDQS }

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, April 19, 2012 8:43 AM
    Moderator
  • Hello Mike

    Does not work, keep getting DISP_E_TYPEMISMATCH. The DQSError object should normaly be "Nothing", because if its not Nothing after the method call then it means an exception occured in C++ and a DQSError COM object has been created pas passed back to describe the exception. I've also tried with "Object", and "IntPtr", nothing works. Is there a way to find out which type COM is expecting ? According to the many articles I've read on the net IntPtr should do the job, followed by Marshal.QueryInterface()

    Public Sub closeAllDocument()
            Dim viewerType As Type = Type.GetTypeFromProgID("NAVDocViewer.NAVDocViewer")
            Dim viewerInstance As Object = Activator.CreateInstance(viewerType)
            Dim erreurDQS As aDQSErrorServr.DQSEror = New aDQSErrorServr.DQSEror
            'Dim p As IntPtr = Nothing
            Dim args As Object() = New Object() {erreurDQS}
            Dim paramModif As Reflection.ParameterModifier = New Reflection.ParameterModifier(1)
            Dim codeRetour As Long = 0
            paramModif.Item(0) = True
            Dim paramModifArray As Reflection.ParameterModifier() = {paramModif}
            Try
                viewerType.InvokeMember( _
                    "closeAllDocument", _
                    Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    viewerInstance, _
                    args, paramModifArray, Nothing, Nothing)
            Catch ex As Exception
            End Try
        End Sub

    Thursday, April 19, 2012 12:20 PM
  • More tests, all no go:

            ' DISP_E_TYPEMISMATCH
            Dim err As Object = Nothing ' No
            Dim errDQS As aDQSErrorServr.DQSEror = New aDQSErrorServr.DQSEror ' No
            Dim errDQSI As aDQSErrorServr.IDQSEror = Nothing ' No
            Dim i As Int32 = 0 ' No
            Dim s As String = "" ' No...
            Dim p As IntPtr = Nothing ' No
            Dim p1 As IntPtr = New IntPtr(0) ' No
            Dim p2 As IntPtr = Marshal.GetIUnknownForObject(errDQS)  ' No
            o.closeAllDocument(p2)

    Thursday, April 19, 2012 1:24 PM
  • Hello Kéboo,

    1.  In situations like the one described in the OP, you need to use the UnknownWrapper class. The following is a sample based on code supplied in the OP :

            Dim viewerType As Type = Type.GetTypeFromProgID("NAVDocViewer.NAVDocViewer")
            Dim viewerInstance As Object = Activator.CreateInstance(viewerType)
            '' Instead of using an IntPtr type,
            '' use an UnknownWrapper type instantiated
            '' with a null Object.
            ''Dim p As IntPtr = Nothing
            Dim obj As Object = Nothing
            Dim instance As New UnknownWrapper(obj)
            Dim args As Object() = New Object() {instance}
            Dim paramModif As Reflection.ParameterModifier = New Reflection.ParameterModifier(1)
            Dim codeRetour As Long = 0
            paramModif.Item(0) = True
            Dim paramModifArray As Reflection.ParameterModifier() = {paramModif}
            Dim objRet As Object
            Try
                objRet = viewerType.InvokeMember( _
                    "closeAllDocument", _
                    Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    viewerInstance, _
                    args, paramModifArray, Nothing, Nothing)
    
                '' The IUnknown pointer will be contained in args(0).
                If Not (args(0) Is Nothing) Then
    		...
    		...
    		...
                End If
            Catch ex As Exception
            End Try

    2. At low level, what happened (when you used "p" the IntPtr) was that the COM server (I assume that it was written in MFC) expected the DISPPARAM to contain a VARIANT of type (VT_BYREF | VT_UNKNOWN).

    3. However, when you used "p", the CLR created a VARIANT (to be passed into the DISPPARAM) of type (VT_BYREF | VT_INT).

    4. This causes the MFC class (that implements the COM server) to reject the parameter as invalid.

    5. The UnknownWrapper will ensure that the CLR created a VARIANT (to be passed into the DISPPARAM) of type (VT_BYREF | VT_UNKNOWN).

    6. Lookup MSDN for more information on the UnknownWrapper class :

    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unknownwrapper.aspx

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Thursday, April 19, 2012 5:50 PM
  • Hello

    Regarding this code:

                '' The IUnknown pointer will be contained in args(0).
                If Not (args(0) Is Nothing) Then
    		...
    		...
    		...
                End If

    args(0) is never Nothing, its always "{Object}". So I need code to find if the wrapped object passed as argument has NOT been changed by MFC. I found two ways to do it, but I am not sure which one is the best:

    Solution 1:

      If Marshal.IsComObject(args(0)) Then

    Solution 2:

    IsDQSError(args(0))

        Public Function IsDQSError(ByVal o As Object) As Boolean
            Dim result As Boolean = False
            Dim co As Object = Nothing
            Try
                co = DirectCast(o, aDqsErrorPia.DQSEror)
                If co IsNot Nothing Then
                    result = True
                End If
            Catch ex1 As InvalidCastException
                result = False
            End Try
            Return result
        End Function
    Since the MSDN documentation indicates not to use QueryInterface, but DirectCast() instead.
    Monday, April 23, 2012 5:42 PM