Вопрос

I am new to learning COM interop, from all the examples I have found they are going from C# to C/C++. I am trying C to C# and back.

I have a program in C that calls a C# DLL by the use of COM interop. I have an IDL file that exposes the C# functions to allow the C program to call into it. I was able to pass a structure into the C# DLL by creating a class with the same variables and specifying [StructLayout(LayoutKind.Sequential)], only with blittable types.

Question: How do I pass a variable within the C structure and allow the C# DLL to modify the variable within the structure and pass it back to the calling C program? Can I pass in a pointer to my C structure and the C# would be able to modify the contents?

Note on the example below: If I remove the 'ref' in the interface prototype and function call for Request, the program only accepts the structure as input and continues successfully. By having the 'ref' the program crashes on the call to the C# function Request().

UPDATE WITH full example

C# 3 files (main.cs, ocStruct.cs, guid.cs)

//main.cs
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace OC
{
    //--------------------------------
    //  Interface protoype
    //--------------------------------    
   [Guid(OC.Guid.IID)]
   public interface IOC
       {
       int Request (ref ocInfo formatInfo);
       }

    [ClassInterface(ClassInterfaceType.None)]

    [Guid(OC.Guid.CLSID)]
    public class OCClass1: IOC
        {
        public OCClass1()
            {
            }

        //--------------------------------
        //  Request()
        //--------------------------------
        public int Request(ref ocInfo Info)
            {
            int rc = 1; //Return code
            try
                {
                Info.req_type = 2;
                }
            catch (Exception e)
                {
                Info.bstr_error = e.Message;
                Info.bstr_stacktrace = e.StackTrace;
                goto exit;
                }

            rc = 0;//Success

            exit:
               return (rc);
           }
        }
    }

//ocStruct.cs
using System;
using System.Runtime.InteropServices;

namespace OC
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ocInfo
        {
                                            public int    req_type;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_filepath;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_newfilepath;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_error;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_stacktrace;
        }
}

//ocGuid.cs
namespace OC
   {
   public sealed class Guid
       {
       //Class ID
       public const String CLSID  = "12345678-90aA-BCDE-1234-5607890ABCDE";

       //Interface ID
       public const String IID    = "12345678-90aB-BCDE-1234-5607890ABCDE";

       //Type Library
       private const String TLB    = "12345678-90aC-BCDE-1234-5607890ABCDE";
       }//End of class
   }//End of namespace

C 3 file (test.c, sscce.h, ocStruct.h)


//test.c
#include <windows.h>
#include "sscce.h"


static const IID OC_CLSID = {0x12345678,0x90aA,0xBCDE,{0x12,0x34,0x56,0x07,0x89,0x0A,0xBC,0xDE}};
static const IID OC_IID   = {0x12345678,0x90aB,0xBCDE,{0x12,0x34,0x56,0x07,0x89,0x0A,0xBC,0xDE}};

BSTR BSTR_ALLOC(wchar_t* wstr)
{
byte*          bptr = NULL;
unsigned long  wlen; 

   wlen = (unsigned long)wcslen(wstr) * sizeof(wchar_t);

   if  ((bptr = (byte *)malloc(wlen + sizeof(unsigned long) + sizeof(wchar_t))) == NULL)
       {
       goto exit;
       }

   memcpy(bptr,
          &wlen,
          sizeof(ULONG));

   bptr += sizeof(ULONG);

   memcpy(bptr,
          wstr,
          wlen);
exit:
   return ((BSTR)bptr);
}

void BSTR_FREE(BSTR bstr)
{
byte*   bptr = NULL;
int     rc = 1;

   bptr = (byte*)bstr;
   bptr -= sizeof(unsigned long);

   free(bptr);
   bptr = NULL;

   return;
}


int main(int argc, char* argv[])
{
HRESULT     hr;
IOC        *ocInterface;
OCINFO      Info;
int         rc; //return code

   hr = CoInitializeEx(NULL,
                       COINIT_MULTITHREADED);
   if  (FAILED(hr))
       {
       goto exit;
       }
   hr = CoCreateInstance(&OC_CLSID,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         &OC_IID,
                         &ocInterface);
   if  (FAILED(hr))
       {
       goto exit;
       }

   memset(&Info,
          0x00,
          sizeof(OCINFO));

   Info.req_type = 1;
   Info.bstr_filepath = BSTR_ALLOC(L"C:\\test\\readme.txt");

   ocInterface->lpVtbl->Request(ocInterface,
                                &Info,
                                &rc);

   BSTR_FREE(Info.bstr_filepath);
exit:
    return (0);
}

sscce.h

/* this ALWAYS GENERATED file contains the definitions for the interfaces */


 /* File created by MIDL compiler version 6.00.0366 */
/* at Wed Apr 16 15:38:43 2014
 */
/* Compiler settings for sscce.idl:
    Oicf, W1, Zp8, env=Win32 (32b run)
    protocol : dce , ms_ext, c_ext, robust
    error checks: allocation ref bounds_check enum stub_data 
    VC __declspec() decoration level: 
         __declspec(uuid()), __declspec(selectany), __declspec(novtable)
         DECLSPEC_UUID(), MIDL_INTERFACE()
*/
//@@MIDL_FILE_HEADING(  )

#pragma warning( disable: 4049 )  /* more than 64k source lines */


/* verify that the <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif

#include "rpc.h"
#include "rpcndr.h"

#ifndef __RPCNDR_H_VERSION__
#error this stub requires an updated version of <rpcndr.h>
#endif // __RPCNDR_H_VERSION__

#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __sscce_h__
#define __sscce_h__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

/* Forward Declarations */ 

#ifndef __IOC_FWD_DEFINED__
#define __IOC_FWD_DEFINED__
typedef interface IOC IOC;
#endif  /* __IOC_FWD_DEFINED__ */


#ifndef __OCClass1_FWD_DEFINED__
#define __OCClass1_FWD_DEFINED__

#ifdef __cplusplus
typedef class OCClass1 OCClass1;
#else
typedef struct OCClass1 OCClass1;
#endif /* __cplusplus */

#endif  /* __OCClass1_FWD_DEFINED__ */


/* header files for imported files */
#include "oaidl.h"
#include "ocidl.h"
#include "ocStruct.h"

#ifdef __cplusplus
extern "C"{
#endif 

void * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void * ); 

#ifndef __IOC_INTERFACE_DEFINED__
#define __IOC_INTERFACE_DEFINED__

/* interface IOC */
/* [helpstring][oleautomation][dual][version][uuid][object] */ 


EXTERN_C const IID IID_IOC;

#if defined(__cplusplus) && !defined(CINTERFACE)

    MIDL_INTERFACE("12345678-90aB-BCDE-1234-5607890ABCDE")
    IOC : public IDispatch
    {
    public:
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Request( 
            /* [out][in] */ POCINFO Info,
            /* [retval][out] */ long *pRetVal) = 0;

    };

#else   /* C style interface */

    typedef struct IOCVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            IOC * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void **ppvObject);

        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            IOC * This);

        ULONG ( STDMETHODCALLTYPE *Release )( 
            IOC * This);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( 
            IOC * This,
            /* [out] */ UINT *pctinfo);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( 
            IOC * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo **ppTInfo);

        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( 
            IOC * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( 
            IOC * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS *pDispParams,
            /* [out] */ VARIANT *pVarResult,
            /* [out] */ EXCEPINFO *pExcepInfo,
            /* [out] */ UINT *puArgErr);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE *Request )( 
            IOC * This,
            /* [out][in] */ POCINFO Info,
            /* [retval][out] */ long *pRetVal);

        END_INTERFACE
    } IOCVtbl;

    interface IOC
    {
        CONST_VTBL struct IOCVtbl *lpVtbl;
    };



#ifdef COBJMACROS


#define IOC_QueryInterface(This,riid,ppvObject) \
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IOC_AddRef(This)    \
    (This)->lpVtbl -> AddRef(This)

#define IOC_Release(This)   \
    (This)->lpVtbl -> Release(This)


#define IOC_GetTypeInfoCount(This,pctinfo)  \
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)

#define IOC_GetTypeInfo(This,iTInfo,lcid,ppTInfo)   \
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)

#define IOC_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)

#define IOC_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)   \
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)


#define IOC_Request(This,Info,pRetVal)  \
    (This)->lpVtbl -> Request(This,Info,pRetVal)

#endif /* COBJMACROS */


#endif  /* C style interface */



/* [id] */ HRESULT STDMETHODCALLTYPE IOC_Request_Proxy( 
    IOC * This,
    /* [out][in] */ POCINFO Info,
    /* [retval][out] */ long *pRetVal);


void __RPC_STUB IOC_Request_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);



#endif  /* __IOC_INTERFACE_DEFINED__ */



#ifndef __OC_LIBRARY_DEFINED__
#define __OC_LIBRARY_DEFINED__

/* library OC */
/* [helpstring][version][uuid] */ 


EXTERN_C const IID LIBID_OC;

EXTERN_C const CLSID CLSID_OCClass1;

#ifdef __cplusplus

class DECLSPEC_UUID("12345678-90aA-BCDE-1234-5607890ABCDE")
OCClass1;
#endif
#endif /* __OC_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

unsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * ); 
unsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * ); 
unsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * ); 
void                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * ); 

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif

IDL 1 file (sscce.idl)


import "oaidl.idl";
import "ocidl.idl";

import "ocStruct.h";

//Interface header
    [
    object,                                    
    uuid(12345678-90aB-BCDE-1234-5607890ABCDE),
    version(1.0),                              
    dual,                                      
    oleautomation,                             
    helpstring("OC interface")          
    ]

//Interface body
interface IOC : IDispatch
    {
    [id(1)]    HRESULT Request([in,out] POCINFO Info,
                               [out, retval] long* pRetVal);
    };


//Type Library information
    [
    uuid(12345678-90aC-BCDE-1234-5607890ABCDE),
    version(1.0),
    helpstring("oc type library")
    ]
library OC
    {
    importlib("stdole2.tlb");

        [
        uuid(12345678-90aA-BCDE-1234-5607890ABCDE),
        version(1.0),
        helpstring("oc class")
        ]

    coclass OCClass1
        {
        [default] interface IOC;
        };
    };

//ocStruct.h
typedef struct _ocInfo
{
    INT                     req_type;             
    BSTR                    bstr_filepath;   
    BSTR                    bstr_newfilepath;
    BSTR                    bstr_error;      
    BSTR                    bstr_stacktrace;   
}OCINFO, *POCINFO;
Это было полезно?

Решение

Assuming that I understand you correctly, you need to pass the address of the struct rather than passing the struct by value.

So the C code becomes:

MYINFO     Info;
my_interface->lpVtbl->Request(interface_ptr,
                              &Info,
                              &rc);

The IDL becomes:

[id(4)]    HRESULT Request([in, out] MYINFO* Info,
                           [out, retval] long* pRetVal);

And the C# function becomes:

public int Request(ref My_info Info)

Update 1

The C code looks odd to me. Why are you passing interface_ptr rather than my_interface?

Update 2

Your latest edit removed all the code to which the original answer referred to. Which is disappointing.

The glaring error with your latest update is that you are not making real BSTR instances. They are made by calling SysAllocString and related functions.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top