none
Tlbimp converts 'BYTE*' to 'ref Byte' for a callback (Event delegate) RRS feed

  • Question

  • Hi,

    I have been facing a problem while trying to communicate between a C# .NET dll and a COM dll.

    The COM dll has the following callback method defined in an interface:

    _IConnectEvents : public IDispatch
        {
        public:
            virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE DispatchReq(
                /* [in] */ BSTR bstrSource,
                /* [in][in] */ BYTE __RPC_FAR *pMsg,
                /* [in] */ long lLen,
                /* [in] */ long lID) = 0;
        };

    I have run tlbimp.exe on the COM Connect.dll, and obtained the .NET ConnectLib.dll.
    And in my C# .NET project, I have added a reference to ConnectLib.dll.

    Now, when I subscribe to this event, I get the following event handler signature:
    objConnect.DispatchReq += new _IConnectEvents_DispatchReqEventHandler(m_objConnect_DispatchReq);

    static void m_objConnect_DispatchReq(string bstrRequester, ref byte pData, int lDataLength, int lMsgID)
    {
    }

    My problem is that the 'BYTE*' is being converted to 'ref byte' and I cannot get the contents of the byte array.

    I am guessing the correct conversion should be to 'byte[]'.
    I have googled a bit and found out that I need to use ildasm.exe, edit the IL and then reassemble the dll, but all examples are for direct functions and not callback events.

    I have tried editing the IL, but I get errors like 'method not implemented' or the event does not fire at all - I think I am editing the IL incorrectly, as there are many references to the callback in the IL (Invoke, SinkHelper, etc...) and I am not sure which ones to edit.

    Can you please help me out with this?

    Thanks,

    Santosh

    Thursday, June 2, 2011 5:50 AM

Answers

  • Hello Santosh,

     

    1. BYTE* pMsg is a Pointer to One Single Byte.

    >> _IConnectEvents : public IDispatch
    >>    {
    >>    public:
    >>        virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE DispatchReq(
    >>            /* [in] */ BSTR bstrSource,
    >>            /* [in][in] */ BYTE __RPC_FAR *pMsg,
    >>            /* [in] */ long lLen,
    >>            /* [in] */ long lID) = 0;
    >>    };
    >> My problem is that the 'BYTE*' is being converted to 'ref byte' and I cannot get the contents of the byte array...

    1.1 I believe that the DispatchReq() event method is probably declared as follows :

    dispinterface _IConnectEvents
    {
      properties:
      methods:
       [id(1), helpstring("method DispatchReq")] HRESULT DispatchReq([in] BSTR bstrSource, [in] BYTE* pMsg, [in] LONG lLen, [in] LONG lID);
     };

    1.2 If "pMsg" is intended to be an array of BYTEs, then it should be declared as a SAFEARRAY of BYTES :

    [id(1), helpstring("method DispatchReq")] HRESULT DispatchReq([in] BSTR bstrSource, [in] SAFEARRAY(BYTE) pMsg, [in] LONG lLen, [in] LONG lID);

    1.3 This is because a declaration like the following :

    BYTE* pMsg

    indicates that "pMsg" is a pointer to one single BYTE.

     

    2. Proposed Solution.

    2.1 Do not modify the IL of the interop assembly generated by TLBIMP.EXE. The required modifications for the IL goes beyond changing the signature of the DispatchReq() method. The interop assembly contains actual IL that will perform the delegate invokation. It's best to leave these alone.

    2.2 Modify the IDL declaration for DispatchReq() as indicated in point 1.2 above.

    2.3 Are you using ATL ? If so note that you need to perform the following :

    2.3.1 Repeat the process for making the class that fires the event (e.g. the Connect class) implement the _IConnectEvents event.

    2.3.2 Look up the code for the wizard-generated IConnectionPointImpl<>-derived event firing helper, probably named as "CProxy_IConnectEvents". Its declararion will likely be something like :

    HRESULT Fire_DispatchReq( BSTR bstrSource, SAFEARRAY ( BYTE ) pMsg,  LONG lLen,  LONG lID)...

    Change it to :

    HRESULT Fire_DispatchReq( BSTR bstrSource, SAFEARRAY* pMsg,  LONG lLen,  LONG lID)...

    2.4 You would also need to change the method that fires the DispatchReq() event to create and use a SAFEARRAY of BYTEs. Here is a sample implementation in C++ :

    STDMETHODIMP CConnect::TestMethod(void)
    {
     // TODO: Add your implementation code here
     BSTR bstSource = ::SysAllocString(L"MyConnector");
     BYTE byte_array[10] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
     
     SAFEARRAY* pSafeArrayOfBytes = NULL;
     
     CreateSafeArrayFromBytes
     (
      byte_array,
      10,
      &pSafeArrayOfBytes
     );
      
     Fire_DispatchReq(bstSource,  pSafeArrayOfBytes,  10,  100);
     
     ::SysFreeString(bstSource);
     bstSource = NULL;
     
     return S_OK;
    }

    2.5 Here are the codes for the CreateSafeArrayFromBytes() helper function :

    void CreateSafeArrayFromBytes
    (
      LPBYTE lpbyBytes,
      ULONG ulSize,
      SAFEARRAY** ppSafeArrayReceiver
    )
    {
      HRESULT   hrRetTemp = S_OK;
      SAFEARRAY*  pSAFEARRAYRet = NULL;
      SAFEARRAYBOUND rgsabound[1];
      ULONG    ulIndex = 0;

      // Initialise receiver.
      if (ppSafeArrayReceiver)
      {
        *ppSafeArrayReceiver = NULL;
      }

      if (lpbyBytes)
      {
     rgsabound[0].lLbound = 0;
        rgsabound[0].cElements = ulSize;

        pSAFEARRAYRet = (SAFEARRAY*)SafeArrayCreate
     (
          (VARTYPE)VT_UI1,
          (unsigned int)1,            
          (SAFEARRAYBOUND*)rgsabound
        );
      }

      for (ulIndex = 0; ulIndex < ulSize; ulIndex++)
      {
     long lIndexVector[1];

     lIndexVector[0] = ulIndex;

        SafeArrayPutElement
     (
          (SAFEARRAY*)pSAFEARRAYRet, 
          (long*)lIndexVector,
          (void*)(&(lpbyBytes[ulIndex]))
        );
      }

      if (pSAFEARRAYRet)
      {
        *ppSafeArrayReceiver = pSAFEARRAYRet;
      }
    }

     

    Best of luck, Santosh,

    - Bio.

     

    • Proposed as answer by Paul Zhou Monday, June 6, 2011 6:41 AM
    • Marked as answer by Paul Zhou Friday, June 10, 2011 5:25 AM
    Thursday, June 2, 2011 9:13 AM

All replies

  • Hello Santosh,

     

    1. BYTE* pMsg is a Pointer to One Single Byte.

    >> _IConnectEvents : public IDispatch
    >>    {
    >>    public:
    >>        virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE DispatchReq(
    >>            /* [in] */ BSTR bstrSource,
    >>            /* [in][in] */ BYTE __RPC_FAR *pMsg,
    >>            /* [in] */ long lLen,
    >>            /* [in] */ long lID) = 0;
    >>    };
    >> My problem is that the 'BYTE*' is being converted to 'ref byte' and I cannot get the contents of the byte array...

    1.1 I believe that the DispatchReq() event method is probably declared as follows :

    dispinterface _IConnectEvents
    {
      properties:
      methods:
       [id(1), helpstring("method DispatchReq")] HRESULT DispatchReq([in] BSTR bstrSource, [in] BYTE* pMsg, [in] LONG lLen, [in] LONG lID);
     };

    1.2 If "pMsg" is intended to be an array of BYTEs, then it should be declared as a SAFEARRAY of BYTES :

    [id(1), helpstring("method DispatchReq")] HRESULT DispatchReq([in] BSTR bstrSource, [in] SAFEARRAY(BYTE) pMsg, [in] LONG lLen, [in] LONG lID);

    1.3 This is because a declaration like the following :

    BYTE* pMsg

    indicates that "pMsg" is a pointer to one single BYTE.

     

    2. Proposed Solution.

    2.1 Do not modify the IL of the interop assembly generated by TLBIMP.EXE. The required modifications for the IL goes beyond changing the signature of the DispatchReq() method. The interop assembly contains actual IL that will perform the delegate invokation. It's best to leave these alone.

    2.2 Modify the IDL declaration for DispatchReq() as indicated in point 1.2 above.

    2.3 Are you using ATL ? If so note that you need to perform the following :

    2.3.1 Repeat the process for making the class that fires the event (e.g. the Connect class) implement the _IConnectEvents event.

    2.3.2 Look up the code for the wizard-generated IConnectionPointImpl<>-derived event firing helper, probably named as "CProxy_IConnectEvents". Its declararion will likely be something like :

    HRESULT Fire_DispatchReq( BSTR bstrSource, SAFEARRAY ( BYTE ) pMsg,  LONG lLen,  LONG lID)...

    Change it to :

    HRESULT Fire_DispatchReq( BSTR bstrSource, SAFEARRAY* pMsg,  LONG lLen,  LONG lID)...

    2.4 You would also need to change the method that fires the DispatchReq() event to create and use a SAFEARRAY of BYTEs. Here is a sample implementation in C++ :

    STDMETHODIMP CConnect::TestMethod(void)
    {
     // TODO: Add your implementation code here
     BSTR bstSource = ::SysAllocString(L"MyConnector");
     BYTE byte_array[10] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
     
     SAFEARRAY* pSafeArrayOfBytes = NULL;
     
     CreateSafeArrayFromBytes
     (
      byte_array,
      10,
      &pSafeArrayOfBytes
     );
      
     Fire_DispatchReq(bstSource,  pSafeArrayOfBytes,  10,  100);
     
     ::SysFreeString(bstSource);
     bstSource = NULL;
     
     return S_OK;
    }

    2.5 Here are the codes for the CreateSafeArrayFromBytes() helper function :

    void CreateSafeArrayFromBytes
    (
      LPBYTE lpbyBytes,
      ULONG ulSize,
      SAFEARRAY** ppSafeArrayReceiver
    )
    {
      HRESULT   hrRetTemp = S_OK;
      SAFEARRAY*  pSAFEARRAYRet = NULL;
      SAFEARRAYBOUND rgsabound[1];
      ULONG    ulIndex = 0;

      // Initialise receiver.
      if (ppSafeArrayReceiver)
      {
        *ppSafeArrayReceiver = NULL;
      }

      if (lpbyBytes)
      {
     rgsabound[0].lLbound = 0;
        rgsabound[0].cElements = ulSize;

        pSAFEARRAYRet = (SAFEARRAY*)SafeArrayCreate
     (
          (VARTYPE)VT_UI1,
          (unsigned int)1,            
          (SAFEARRAYBOUND*)rgsabound
        );
      }

      for (ulIndex = 0; ulIndex < ulSize; ulIndex++)
      {
     long lIndexVector[1];

     lIndexVector[0] = ulIndex;

        SafeArrayPutElement
     (
          (SAFEARRAY*)pSAFEARRAYRet, 
          (long*)lIndexVector,
          (void*)(&(lpbyBytes[ulIndex]))
        );
      }

      if (pSAFEARRAYRet)
      {
        *ppSafeArrayReceiver = pSAFEARRAYRet;
      }
    }

     

    Best of luck, Santosh,

    - Bio.

     

    • Proposed as answer by Paul Zhou Monday, June 6, 2011 6:41 AM
    • Marked as answer by Paul Zhou Friday, June 10, 2011 5:25 AM
    Thursday, June 2, 2011 9:13 AM
  •  

    Hi Santosh,

     

    Has your issue been resolved? Would you mind letting us know the result of the suggestions?

     

    Now I will mark an answer, you can mark others that you think to be so useful to your issue.

    If you still have any questions about this issue, please feel free to let me know. We will continue to work with you on this issue.

     

    Have a nice day!


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, June 10, 2011 5:25 AM
  • Thank You Lim & Paul!! Your suggestions work!!! Thanks once again! :)
    Friday, June 17, 2011 6:47 AM