none
How to pass a C++ Structure to C#

    Question

  • Hi,

       I want to pass a C++ structure to C# code, and i am using Invoke Helper functions as a COM Interface , how can i do this. Here is the sample scenario

    C++ Code:

    Struct

    {

    char name[30];

    char depart[30];

    }


    Working as a software engineer
    Friday, May 07, 2010 6:46 AM

Answers

  • Hello rsodimba,

     

    1. Because your structure is to be passed via COleDispatchDriver::InvokeHelper() to the C# COM Server, its fields has to be OLE-automation compatible types. This means that the fields "name" and "depart" has to be declared as of type "BSTR" instead of "char".

     

    2. Also because the structure is to be passed via COleDispatchDriver::InvokeHelper() to the C# COM Server, it has to be passed via a VT_RECORD VARIANT. Hence it must be declared in the IDL with a GUID.

     

    3. The following is a sample declaration for your structure in the IDL :

     [
       uuid(6A4E71F8-F211-4464-AF67-E965A9B4B1B2),
       version(1.0),
       helpstring("RSODimba Structure")
     ]
     typedef struct RSODimba
        {
          BSTR name;
          BSTR depart;
        } RSODimba;

     

    4. The RSODimba structure, when passed as a parameter to a method, has to be contained inside a VARIANT. The following is a sample method declaration :

    [id(1), helpstring("method Method")] HRESULT Method([in] VARIANT rsodimba_struct);

    The RSODimba structure has to be declared as a VARIANT.

     

    5. Over at the C# server side, the method is declared to take in an "object" parameter :

            public void Method(object rsodimba_struct)
            {
                RSODimba rsodimba = (RSODimba)rsodimba_struct;
                MessageBox.Show(rsodimba.name, "CSharp Server");
                MessageBox.Show(rsodimba.depart, "CSharp Server");
            }

     

    6. For a C++ client, the RSODimba structure has to be processed specially, using an IRecordInfo interface pointer that is uniquely matched with the RSODimba structure.

    6.1 This IRecordInfo interface pointer must be obtained from the Type Library which is compiled from the IDL in which RSODimba is defined.

    6.2 The following is a sample function I wrote for this purpose :

    HRESULT GetIRecordType
    (
      LPCTSTR lpszTypeLibraryPath,  // Path to type library that contains definition of a User-Defined Type (UDT).
      REFGUID refguid,  // GUID of UDT.
      IRecordInfo** ppIRecordInfoReceiver  // Receiver of IRecordInfo that encapsulates information of the UDT.
    )
    {
      _bstr_t  bstTypeLibraryPath = lpszTypeLibraryPath;
      ITypeLib*  pTypeLib = NULL;
      ITypeInfo* pTypeInfo = NULL;
      HRESULT  hrRet = S_OK;

      *ppIRecordInfoReceiver = NULL;  // Initialize receiver.
     
      hrRet = LoadTypeLib((const OLECHAR FAR*)bstTypeLibraryPath, &pTypeLib);

      if (SUCCEEDED(hrRet))
      {
        if (pTypeLib)
        {
           hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid, &pTypeInfo);
           pTypeLib->Release();
           pTypeLib = NULL;
        }

        if (pTypeInfo)
        {
          hrRet = GetRecordInfoFromTypeInfo(pTypeInfo, ppIRecordInfoReceiver);
          pTypeInfo->Release();
          pTypeInfo = NULL;
        }
      }

      return hrRet;
    }

    6.3 You can use the function as follows :

       IRecordInfoPtr spIRecordInfo = NULL;
      
       GetIRecordType
       (
         "RSODimbaTypeLibrary.tlb",
         __uuidof(RSODimba),
         (IRecordInfo**)&spIRecordInfo
       );

    6.4 After obtaining the appropriate IRecordInfo interface pointer for the RSODimba structure, you need to set up a VARIANT that contains both an RSODimba structure as well as the IRecordInfo interface pointer :

       VARIANT       varRSODimba;
       RSODimba*   prsodimba = (RSODimba*)(spIRecordInfo -> RecordCreate());
      
       memset (prsodimba, 0, sizeof(RSODimba));
       prsodimba -> name = _bstr_t("RSO Dimba").copy();
       prsodimba -> depart = _bstr_t("RSO Dimba Department").copy();
       
       VariantInit(&varRSODimba);
       VariantClear(&varRSODimba); 
      
       V_VT(&varRSODimba) = VT_RECORD;
       V_RECORD(&varRSODimba) = prsodimba;
       V_RECORDINFO(&varRSODimba) = spIRecordInfo;
       spIRecordInfo -> AddRef();

    6.4.1 You would first need to allocate memory for an RSODimba struct. This is shown above in the code :

    RSODimba*   prsodimba = (RSODimba*)(spIRecordInfo -> RecordCreate());

    Yes, the IRecordInfo interface (for RSODimba) knowns the size required for the RSODimba struct. You can either call its RecordCreate() method or use the ::CoTaskMemAlloc() API passing the sizeof(RSODimba).

    Note that it is safest to always specially allocate memory for a structure that you intend to pass into a VARIANT via VT_RECORD. This memory will be deallocated later via VariantClear().

    6.4.2 Next, fill in the field values of the RSODimba structure. Note that, just like the RSODimba structure itself, memory has to be allocated specially for any pointer fields inside the RSODimba, e.g. BSTRs. Hence :

       prsodimba -> name = _bstr_t("RSO Dimba").copy();
       prsodimba -> depart = _bstr_t("RSO Dimba Department").copy();

    6.4.3 Finally we setup a VARIANT to hold the structure :

       VariantInit(&varRSODimba);
       VariantClear(&varRSODimba); 
      
       V_VT(&varRSODimba) = VT_RECORD;
       V_RECORD(&varRSODimba) = prsodimba;
       V_RECORDINFO(&varRSODimba) = spIRecordInfo;
       spIRecordInfo -> AddRef();

    Note that because we will be passing the IRecordInfo interface pointer inside the VARIANT, its reference count must also be incremented.

    6.5. Finally, obtain the IDispatch interface pointer from the C# COM Server, attach it to a COleDispatchDriver object and then call the InvokeHelper() function, e.g. :

     

       COleDispatchDriver disp_driver;
       IDispatchPtr            spIDispatch = NULL;
       
       pRSODimbaInterface -> QueryInterface(IID_IDispatch, (void**)&spIDispatch);
      
       if (spIDispatch)
       {
          OLECHAR FAR* rgszNames = L"Method";
          DISPID dispid = 0;

          spIDispatch -> GetIDsOfNames
          (
           IID_NULL,                 
           &rgszNames, 
           1,         
           LOCALE_SYSTEM_DEFAULT,                  
           &dispid         
          );

         disp_driver.AttachDispatch(spIDispatch, FALSE);
      
         disp_driver.InvokeHelper
         (
           dispid,
           DISPATCH_METHOD,
           VT_EMPTY,
           (void*)NULL,
           (const BYTE*)VTS_VARIANT,
           &varRSODimba 
         );
       }
       
       VariantClear(&varRSODimba);

     

    6.5.1 Note that with C# COM Servers, it is best to obtain the DISPID of the method to invoke via the IDispatch::GetIDsOfNames() method. Do not assume that the DISPID is as what has been declared in the IDL. Hence :

          OLECHAR FAR* rgszNames = L"Method";
          DISPID dispid = 0;

          spIDispatch -> GetIDsOfNames
          (
           IID_NULL,                 
           &rgszNames, 
           1,         
           LOCALE_SYSTEM_DEFAULT,                  
           &dispid         
          );

    and do not assume that the DISP ID for Method() is 1. You will be surprised that the DISPID obtained from GetIDsOfNames() can be totally different from what is declared in the IDL.

    6.5.1 The VariantClear() call at the end :

    VariantClear(&varRSODimba);

    will clear the memory allocated for the RSODimba structure as well as the BSTR fields inside the structure.

     

     

    I hope this rather long post will be useful to you. Best of luck, rsodimba.

    - Bio.

    Monday, May 10, 2010 7:36 AM

All replies

  • Hello rsodimba,

     

    1. A few things to clarify :

    1.1 Is the C# code running as a COM server (via COM-Interop) for the C++ client ?

    1.2 Am I correct to say that you are intending to use COleDispatchDriver::InvokeHelper() to invoke a method of the C# object via a COM-callable wrapper ?

    1.3 One of the parameters to this method is the struct mentioned in your OP.

     

    Thanks, rsodimba,

    - Bio.

    Friday, May 07, 2010 10:25 AM
  • Yes, i am exactly doing the same thing... Pls provide us the information...

     

    Thanks in Advance

    Ravi Kiran


    Working as a software engineer
    Monday, May 10, 2010 4:43 AM
  • Hello rsodimba,

     

    1. Because your structure is to be passed via COleDispatchDriver::InvokeHelper() to the C# COM Server, its fields has to be OLE-automation compatible types. This means that the fields "name" and "depart" has to be declared as of type "BSTR" instead of "char".

     

    2. Also because the structure is to be passed via COleDispatchDriver::InvokeHelper() to the C# COM Server, it has to be passed via a VT_RECORD VARIANT. Hence it must be declared in the IDL with a GUID.

     

    3. The following is a sample declaration for your structure in the IDL :

     [
       uuid(6A4E71F8-F211-4464-AF67-E965A9B4B1B2),
       version(1.0),
       helpstring("RSODimba Structure")
     ]
     typedef struct RSODimba
        {
          BSTR name;
          BSTR depart;
        } RSODimba;

     

    4. The RSODimba structure, when passed as a parameter to a method, has to be contained inside a VARIANT. The following is a sample method declaration :

    [id(1), helpstring("method Method")] HRESULT Method([in] VARIANT rsodimba_struct);

    The RSODimba structure has to be declared as a VARIANT.

     

    5. Over at the C# server side, the method is declared to take in an "object" parameter :

            public void Method(object rsodimba_struct)
            {
                RSODimba rsodimba = (RSODimba)rsodimba_struct;
                MessageBox.Show(rsodimba.name, "CSharp Server");
                MessageBox.Show(rsodimba.depart, "CSharp Server");
            }

     

    6. For a C++ client, the RSODimba structure has to be processed specially, using an IRecordInfo interface pointer that is uniquely matched with the RSODimba structure.

    6.1 This IRecordInfo interface pointer must be obtained from the Type Library which is compiled from the IDL in which RSODimba is defined.

    6.2 The following is a sample function I wrote for this purpose :

    HRESULT GetIRecordType
    (
      LPCTSTR lpszTypeLibraryPath,  // Path to type library that contains definition of a User-Defined Type (UDT).
      REFGUID refguid,  // GUID of UDT.
      IRecordInfo** ppIRecordInfoReceiver  // Receiver of IRecordInfo that encapsulates information of the UDT.
    )
    {
      _bstr_t  bstTypeLibraryPath = lpszTypeLibraryPath;
      ITypeLib*  pTypeLib = NULL;
      ITypeInfo* pTypeInfo = NULL;
      HRESULT  hrRet = S_OK;

      *ppIRecordInfoReceiver = NULL;  // Initialize receiver.
     
      hrRet = LoadTypeLib((const OLECHAR FAR*)bstTypeLibraryPath, &pTypeLib);

      if (SUCCEEDED(hrRet))
      {
        if (pTypeLib)
        {
           hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid, &pTypeInfo);
           pTypeLib->Release();
           pTypeLib = NULL;
        }

        if (pTypeInfo)
        {
          hrRet = GetRecordInfoFromTypeInfo(pTypeInfo, ppIRecordInfoReceiver);
          pTypeInfo->Release();
          pTypeInfo = NULL;
        }
      }

      return hrRet;
    }

    6.3 You can use the function as follows :

       IRecordInfoPtr spIRecordInfo = NULL;
      
       GetIRecordType
       (
         "RSODimbaTypeLibrary.tlb",
         __uuidof(RSODimba),
         (IRecordInfo**)&spIRecordInfo
       );

    6.4 After obtaining the appropriate IRecordInfo interface pointer for the RSODimba structure, you need to set up a VARIANT that contains both an RSODimba structure as well as the IRecordInfo interface pointer :

       VARIANT       varRSODimba;
       RSODimba*   prsodimba = (RSODimba*)(spIRecordInfo -> RecordCreate());
      
       memset (prsodimba, 0, sizeof(RSODimba));
       prsodimba -> name = _bstr_t("RSO Dimba").copy();
       prsodimba -> depart = _bstr_t("RSO Dimba Department").copy();
       
       VariantInit(&varRSODimba);
       VariantClear(&varRSODimba); 
      
       V_VT(&varRSODimba) = VT_RECORD;
       V_RECORD(&varRSODimba) = prsodimba;
       V_RECORDINFO(&varRSODimba) = spIRecordInfo;
       spIRecordInfo -> AddRef();

    6.4.1 You would first need to allocate memory for an RSODimba struct. This is shown above in the code :

    RSODimba*   prsodimba = (RSODimba*)(spIRecordInfo -> RecordCreate());

    Yes, the IRecordInfo interface (for RSODimba) knowns the size required for the RSODimba struct. You can either call its RecordCreate() method or use the ::CoTaskMemAlloc() API passing the sizeof(RSODimba).

    Note that it is safest to always specially allocate memory for a structure that you intend to pass into a VARIANT via VT_RECORD. This memory will be deallocated later via VariantClear().

    6.4.2 Next, fill in the field values of the RSODimba structure. Note that, just like the RSODimba structure itself, memory has to be allocated specially for any pointer fields inside the RSODimba, e.g. BSTRs. Hence :

       prsodimba -> name = _bstr_t("RSO Dimba").copy();
       prsodimba -> depart = _bstr_t("RSO Dimba Department").copy();

    6.4.3 Finally we setup a VARIANT to hold the structure :

       VariantInit(&varRSODimba);
       VariantClear(&varRSODimba); 
      
       V_VT(&varRSODimba) = VT_RECORD;
       V_RECORD(&varRSODimba) = prsodimba;
       V_RECORDINFO(&varRSODimba) = spIRecordInfo;
       spIRecordInfo -> AddRef();

    Note that because we will be passing the IRecordInfo interface pointer inside the VARIANT, its reference count must also be incremented.

    6.5. Finally, obtain the IDispatch interface pointer from the C# COM Server, attach it to a COleDispatchDriver object and then call the InvokeHelper() function, e.g. :

     

       COleDispatchDriver disp_driver;
       IDispatchPtr            spIDispatch = NULL;
       
       pRSODimbaInterface -> QueryInterface(IID_IDispatch, (void**)&spIDispatch);
      
       if (spIDispatch)
       {
          OLECHAR FAR* rgszNames = L"Method";
          DISPID dispid = 0;

          spIDispatch -> GetIDsOfNames
          (
           IID_NULL,                 
           &rgszNames, 
           1,         
           LOCALE_SYSTEM_DEFAULT,                  
           &dispid         
          );

         disp_driver.AttachDispatch(spIDispatch, FALSE);
      
         disp_driver.InvokeHelper
         (
           dispid,
           DISPATCH_METHOD,
           VT_EMPTY,
           (void*)NULL,
           (const BYTE*)VTS_VARIANT,
           &varRSODimba 
         );
       }
       
       VariantClear(&varRSODimba);

     

    6.5.1 Note that with C# COM Servers, it is best to obtain the DISPID of the method to invoke via the IDispatch::GetIDsOfNames() method. Do not assume that the DISPID is as what has been declared in the IDL. Hence :

          OLECHAR FAR* rgszNames = L"Method";
          DISPID dispid = 0;

          spIDispatch -> GetIDsOfNames
          (
           IID_NULL,                 
           &rgszNames, 
           1,         
           LOCALE_SYSTEM_DEFAULT,                  
           &dispid         
          );

    and do not assume that the DISP ID for Method() is 1. You will be surprised that the DISPID obtained from GetIDsOfNames() can be totally different from what is declared in the IDL.

    6.5.1 The VariantClear() call at the end :

    VariantClear(&varRSODimba);

    will clear the memory allocated for the RSODimba structure as well as the BSTR fields inside the structure.

     

     

    I hope this rather long post will be useful to you. Best of luck, rsodimba.

    - Bio.

    Monday, May 10, 2010 7:36 AM
  • Hi Bio, Thanks for the quick reply, i will try out all the steps now, and will get back to you incase if i face any problems.

    Thanks

    RaviKiran


    Working as a software engineer
    Monday, May 10, 2010 7:57 AM
  • Hi Bio,

         I have implemented the same, i am able to compile it sucessfully, but i am failing at the function

     hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid, &pTypeInfo)

     here is the code i used in both VC++ & C#, pls correct me if i am wrong at some place

    My C# code

    /** COM Interface ****/ 

     

    [Guid("6A4E71F8-F211-4464-AF67-E965A9B4B1B2"),  

    ComVisible(true)]  

    public struct RSODimba

    { 

    public String name;  

    //public String depart;

    };

     VC++ Code

    [

    uuid(6A4E71F8-F211-4464-AF67-E965A9B4B1B2), 

    //version(1.0),

    helpstring(

    "RSODimba Structure" 

    typedef struct RSODimba  

     

    {

    BSTR name;

    BSTR depart;

    } RSODimba;

    GetIRecordType(

    "plqbplg.tlb",__uuidof(RSODimba),(IRecordInfo**)&spIRecordInfo);

     

     

    (

    LPCTSTR lpszTypeLibraryPath,

     HRESULT CPlSchedBaseDlg::GetIRecordType

    // Path to type library that contains definition of a User-Defined Type (UDT).

    REFGUID refguid,

    // GUID of UDT.

    IRecordInfo** ppIRecordInfoReceiver

    // Receiver of IRecordInfo that encapsulates information of the UDT.

    )

    {

    _bstr_t bstTypeLibraryPath = lpszTypeLibraryPath;

    ITypeLib* pTypeLib = NULL;

    ITypeInfo* pTypeInfo = NULL;

    HRESULT hrRet = S_OK;

    *ppIRecordInfoReceiver = NULL;

    // Initialize receiver.

     hrRet = LoadTypeLib((

    const OLECHAR FAR*)bstTypeLibraryPath, &pTypeLib);

     if (SUCCEEDED(hrRet))

     if (pTypeLib)

     if (pTypeInfo)

     return hrRet;

     }

     {

    hrRet = GetRecordInfoFromTypeInfo(pTypeInfo, ppIRecordInfoReceiver);

    pTypeInfo->Release();

    pTypeInfo = NULL;

    }

    } 

    {

    hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid, &pTypeInfo);

    pTypeLib->Release();

    pTypeLib = NULL;

    { 

    (IRecordInfo**)&spIRecordInfo

    );

     

    ]

     


    Working as a software engineer

     

     

     

     

     

     

    Monday, May 10, 2010 10:54 AM
  • Hello rsodimba,

     

    1. To guide you through every step would be very time-consuming and impractical.

    2. I suggest that I email to you the sample code that I wrote for the RSODimba struct example.

    3. Email me : bio_lim_2004@yahoo.com

     

    - Bio.

    Tuesday, May 11, 2010 12:44 PM