天天看点

在COM应用中使用.NET组件

转载:原文地址:http://blogs.msdn.com/silverlightshanghai/archive/2008/07/23/com-net.aspx

上一博中,我们用Visual Studio把.NET组件(托管组件)变成了COM服务器。本博继续这个话题,看看如何使用.NET工具包来完成相应的功能。

首先我们来看看今天的主题和CLR Interop的关系,在我们组里,interop这个术语包含了四个范畴,即P/Invoke, Reverse P/Invoke, COM Interop和Reverse COM Interop。前二个概念通过动态连接库(DLL)在native世界和.NET世界中实现互通性;后两个概念顾名思义,就是和COM打交道了。其中,COM interop是在.NET应用中使用COM组件;Reverse COM Interop指的是在COM应用中使用.NET组件。概念有些绕口令,看官先别急着抛转,看看下图。

在COM应用中使用.NET组件

可能有人会问, COM技术已经历史悠久了,.NET程序员为什么需要和它打交道呢?问题的答案就在于”组件”一词。举个例子,若干年前一个牛人写了个程序,扩展性极佳,他用了COM把插件的接口定义的明明白白,而我们想用.NET来做这个插件。。。

为COM写一个.NET组件,可以参照以下三部曲

1.       定义.NET接口,撰写.NET class

2.       部署.NET组件

3.       撰写COM客户端

第一步骤对经常从事.NET的开发朋友来说非常熟悉,这里给出例子,不再赘述。

csc /target:library a.cs

第二,要部署.NET组件,这里包括两个方面:

regasm a.dll /tlb

1.       把类型库(type library)导出。对于COM应用来说,它只懂得类型库,是为COM组件遵循的二进制”标准”。/tlb选项告诉regasm,导出类型库。我们可以用oleview察看tlb的内容,如下图所示,此前定义的.NET接口和类都在其中。

在COM应用中使用.NET组件

2.       把步骤2中生成的dll放到注册表中。COM并不懂得诸如GAC的概念,而是通过注册表来查询HKEY_CLASSES_ROOT/CLS_ID/00000000-0000-0000-FFFF-000000000004,观察这个注册表项,InprocServer32中把COM的动态链接库指向了mscoree.dll,这就是传说中的垫片(shim),它会负责加载公共语言运行时,并找到真正的.NET组件----a.dll。

在COM应用中使用.NET组件

有了以上两步,我们就可以在COM应用中使用.NET组件了。如何撰写COM组件超出了本文的范畴,有兴趣的读者可以参考代码中的注释。

cl client.cs

  1. // client.cs
  2. #define _WIN32_COM
  3. #include <stdio.h>
  4. #include <wtypes.h>
  5. #import "a.tlb" no_namespace named_guids raw_interfaces_only
  6. // a.tlb是第二步中导出的
  7. int main()
  8. {
  9.       IUnknown *pUnk = NULL;
  10.       IA *pIA = NULL;
  11.       HRESULT hresult;
  12.       // 0. 初始化COM组件
  13.       hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  14.       if (FAILED(hresult))
  15.       {
  16.             printf("ERROR: cannot initialze COM: 0x%x/n", hresult);
  17.             return -1;
  18.       }
  19.       // 1. 获取IUnknown接口
  20.       hresult = CoCreateInstance(CLSID_A, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnk);
  21.       if (FAILED(hresult))
  22.       {
  23.             printf("ERROR: cannot Get the IUnkown interface: 0x%x/n", hresult);
  24.             return -1;
  25.       }
  26.       // 2. 获得IA接口
  27.       hresult = pUnk->QueryInterface(IID_IA, (void **)&pIA);
  28.       if (FAILED(hresult))
  29.       {
  30.             printf("ERROR: cannot Convert to IDispatch interface: 0x%x/n", hresult);
  31.             pUnk->Release();
  32.             return -1;
  33.       }
  34.       pUnk->Release();
  35.       // 3. 调用.NET组件
  36.       hresult = pIA->Hello();
  37.       if (FAILED(hresult))
  38.       {
  39.             printf("ERROR: Invoke failed: 0x%x/n", hresult);
  40.       }
  41.       // 4. 清理
  42.       pIA->Release();
  43.       CoUninitialize();
  44.       return 0;
  45. }