我是学习COM互操作的新手,从我发现他们从C#到C / C ++的所有例子。我正在尝试C到C#并返回。
我在C中有一个程序,它通过使用COM互操作来调用C#DLL。我有一个IDL文件,它公开C#函数以允许C程序调用它。通过创建一个具有相同变量的类并指定[StructLayout(LayoutKind.Sequential)],我只能使用blittable类型将结构传递给C#DLL。
问题:如何在C结构中传递变量并允许C#DLL修改结构中的变量并将其传递回调用C程序? 我可以传入一个指向我的C结构的指针,C#能够修改内容吗?
请注意以下示例: 如果我删除' ref'在接口原型和函数调用 Request 中,程序只接受结构作为输入并继续成功。通过' ref'程序在调用C#函数 Request()时崩溃。
更新完整示例
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文件(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文件(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;
答案 0 :(得分:0)
假设我理解正确,你需要传递结构的地址,而不是按值传递结构。
所以C代码变成:
MYINFO Info;
my_interface->lpVtbl->Request(interface_ptr,
&Info,
&rc);
IDL成为:
[id(4)] HRESULT Request([in, out] MYINFO* Info,
[out, retval] long* pRetVal);
C#功能变为:
public int Request(ref My_info Info)
更新1
C代码对我来说很奇怪。你为什么要通过interface_ptr
而不是my_interface
?
更新2
您的最新编辑删除了原始答案所引用的所有代码。这是令人失望的。
您最新更新的明显错误是您没有制作真正的BSTR
个实例。它们是通过调用SysAllocString
和相关函数来完成的。