用C#编写Com是如此的简单,比C++的ATL还要简单。但是当C++的非托管代码调用C#编写的托管Com时,接口函数变成什么样子了呢?
例如:C#中的接口如下定义:
[Guid("FA1FD727-74C7-4434-95D4-B25BC82F709C")]
public interface ICSharp
{
[DispId(1)]
int ModifyInt(out int value);
[DispId(2)]
string name();
[DispId(3)]
int Count { get; set; }
}
在C++中调用的ModifyInt为HRESULT ModifyInt(long* value, long* valRet);
也就是说在C#中定义的接口的参数和返回值都成了C++中的参数,而且由out 定义的参数,在C++中成了指针,C#中的int对应成了c++中的long,那么c++中的HRESULT 又是C#中的什么呢?
哦了,C#中抛出的异常便是C++中ModifyInt的返回值了!ok
再看第二个接口函数
C# : string name();
C++: HRESULT name(BSTR* valRet);
再看第三个接口属性,这个有些不同
C# : int Count { get; set; }
C++: HRESULT get_Count(long* value);
HRESULT put_Count(long pRetVal);
常用封送类型对应表:
C++类型 |
C#封送处理类型 |
备注 |
BSTR |
[MarshalAs(UnmanagedType.BStr)] string或直接使用 string |
string的默认封送类型就是BSTR,但是在结构体中直接使用string,会有乱码。 |
const BSTR |
[MarshalAs(UnmanagedType. LPWStr)] string |
|
ULONG |
uint |
|
UINT |
uint |
|
GUID |
Guid |
|
HWND |
intptr |
在调用COM接口时,需要将句柄强制转换成long型。 |
HIMAGELIST |
intptr |
在调用COM接口时,需要将句柄强制转换成long型。 |
VARIANT |
object |
|
IUnknown * |
[MarshalAs(UnmanagedType.IUnknown)] object |
|
IUnknown ** |
out IntPtr |
当需要在COM中生成一个对象时,必须用intptr类型获得其指针,如果用object的话,非托管对象无法访问托管对象的堆栈。 |
结构体指针 |
用unsafe直接定义。 |
在COM中应定义相同的结构体,填充数据后可调用Marshal.StructureToPtr将非托管结构体对应内存填充上。 |
对用与dll中的dllregisterserver,在C# 中需标识[ComRegisterFunctionAttribute]
如:
[ComRegisterFunctionAttribute]
public static void ComRegisterServer(Type t)
可在注册com时,顺便调用此函数注册所需内容。