1. 用Tlbimp.exe,通过一个定义了该interface的typelib生成interface的managed definition. 然后你的类从它的interface继承就行了。2. 自己手工写这个interface的managed definition,加上GuidAttribute,表示正确的COM Interface的GUID,然后你的类从这个自己定义的interface继承,如果定义正确的话,一样可以用来进行COM Interop。3. 无论哪种方法,如果要在COM中调用该组件的话,类定义的时候一定要保证COMVisibleAttribute = true (Default)。4. IUnkown/IDispatch等少数几个接口不能这样实现。
大致的源程序如下:
C++的DLL:class ISomething : public IUnknown
{
STDMETHOD FuncX();
STDMETHOD FuncXX();
};extern "C"
{
__declspec(dllexport)int __stdcall GetSomething(ISomething * pSomething);
}原来用VC时我的客户端可以这么写:
class CSomething : public ISomething
{
// ...
};main()
{
CSomething s;
GetSomethingProc(&s);
}现在在C#中我就faint了我不想用Disp interface,一是因为速度慢,二是因为十有八九要和注册表打交道,我打算让我的程序跨平台,所以不能依赖它(尽管这个在C#中我知道怎么用,写一个TypeLib然后用那个tlbimp就行)
确切的说我连GUID都不想用(在上面那个特殊情况中,我根本不需要去QueryInterface,直接返回this就可以了,在Delphi中也一样)之所以会有这么多麻烦,是因为我原来的DLL写得并不完全遵循COM规范,比如传递的参数和返回值就不是COM兼容类型(有好几个自定义的结构体),在C#中想要用它说不定还需要自己写一个boxing/unboxing的处理程序。如果实在没办法,我只好重写我的VC DLL了,这可是个浩大的工程…………
在你的代码例子中,实际上是CSomething实现了ISomething接口,怎么能说他是Client呢?究竟需要C#扮演怎样的角色,实现什么功能?原来的DLL有实现了什么功能?
tlbimp生成的代理不一定会通过IDispatch的,一般仍然是普通的COM接口方式。
而且COM本身就不垮平台,而且标准COM一定会用到注册表...
假设我有一个COM Interface的定义如上述的ISomething,我如何才能在C#里面实现它并且让它对于其他COM程序可见。
1. 假设你在IDL里面有定义:
interface IHello;
[
odl,
uuid(4371C4F5-CD87-4CAB-8F99-C3336BF6FD46),
]
interface IHello : IUnknown {
HRESULT _stdcall SayHello();
};2. 必须先将接口转换成C#的形式(或者用tlbimp.exe也可以):
[ComVisible(true)]
[Guid("4371C4F5-CD87-4CAB-8F99-C3336BF6FD46")]
//没有IDispatch,duel interface
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHello {
void SayHello();
}3. 实现该接口:
[ComVisible(true)] //找你的传指针的做法,这里是false也没关系
//避免CLR为你生成default class duel interface, 使用IHello
[ClassInterface(ClassInterfaceType.None)]
public class MyHello {
public void SayHello() {
Console.WriteLine("hello, Universe!");
}
}4. 在unmanaged code中使用:
a. 如果unmanaged code接受一个IUnknown:
_declspec(dllexport) void DllSayHello(IUnknown* punk) {
IHello* phello;
punk ->QueryInterface(IID_IHello, (void**)&phello);
phello ->SayHello();
...
}
C#调用:
[DllImport("mydll.dll")]
public static extern void DllSayHello(
[MarshalAs(UnmanagedType.Interface)] object o);
...
MyHello hello = new MyHello();
DllSayHello(hello);b. Dll接受一个IHello*:
_declspec(dllexport) void DllSayHello(IHello* phello) {
phello ->SayHello();
...
}
C#调用:
[DllImport("mydll.dll")]
public static extern void DllSayHello(IntPtr pinterface);
...
MyHello hello = new MyHello();
IntPtr punk = Marshal.GetIUnknownForObject(hello);
IntPtr phello;
Marshal.QueryInterface(punk, ref guid, out phello);
DllSayHello(phello);
...对于复杂的数据类型,只要按照Marshal规则将它转换成对应的C#类型就可以了。
3. 实现该接口:
[ComVisible(true)] //找你的传指针的做法,这里是false也没关系
//避免CLR为你生成default class duel interface, 使用IHello
[ClassInterface(ClassInterfaceType.None)]
public class MyHello : IHello { <-----漏了!!!!!!!。
public void SayHello() {
Console.WriteLine("hello, Universe!");
}
}