[英]Calling a Managed API from COM
我是學習 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# 能夠修改內容嗎?
請注意以下示例:如果我刪除了Request的接口原型和函數調用中的“ref”,則程序僅接受該結構作為輸入並成功繼續。 通過使用“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;
假設我理解正確,您需要傳遞結構的地址,而不是按值傳遞結構。
所以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
和相關函數來實現的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.