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;