• AllInOne Code Framework(AIO): 如何使用C#编写进程外的COM组件 (How to write an outofproc COM server in C#)


    本文介绍如何使用C#编写进程外的COM组件。文章中所使用的example为All-In-One Code Framework 的CSExeCOMServer示例:

    http://cfx.codeplex.com/

    为了帮助读者更快体会到什么是进程外的COM组件,我建议您从上诉链接中下载最新的release,并按照如下步骤搭一个简单的测试环境:

    Step1. 使用Visual Studio 2008打开All-In-One Code Framework(AIO)的solution文件(Visual Studio需要run as admin)。

    Step2. 直接build其中的CSExeCOMServer项目。该COM组件的注册将自动完成(我稍后再解释它是如何被注册的)。

    Step3. 编写一个简单的客户端以测试该进程外COM组件:打开notepad,并输入如下vbscript代码。将文件保存为client.vbs,并双击运行。此时,您将看到一个HelloWorld的message box被弹出。“HelloWorld”其实是由我们的COM组件的HelloWorld方法返回的一个字符串。

      Set obj = CreateObject("CSExeCOMServer.CSSimpleObject")
      MsgBox obj.HelloWorld

    Step4. 在点击确定按钮关闭message box之前,打开Windows的任务管理器,并找到CSExeCOMServer.exe进程。我们CreateObject("CSExeCOMServer.CSSimpleObject")时创建的COM组件对象就是运行在这个进程里的,而非客户端进程。这也就是所谓的“进程外”COM server。

    Step5. 点击确定按钮以关闭message box。观察Windows任务管理器中的CSExeCOMServer进程。您会发现几秒钟之后,该进程自动退出了。这是因为我们的client端释放了那个COM对象。当COM server中不再含有活着的COM对象时,出于性能考虑,COM server会自动shut down。

    简单的测试到此结束。下面我们来看看编写一个C#进程外COM组件的一般方法:

    方法一:先用C#编写一个进程内的COM组件(例见AIO/CSDllCOMServer),然后使用DCOMCNFG将这个DLL包装在一个COM+ Application里。

    方法二:使用C#编写一个Service Component,并使用regsvcs.exe将其直接注册成一个COM+的组件。(例见AIO/CSServicedComponent)

    方法三:仿造ATL out-of-process的template,使用C#编写一个真正意义上的out-of-process COM local server。

    方法一和方法二的缺点是,部署人员不得不struggle with 复杂的COM+属性,尤其是其中和安全性相关的设置。如果设置不妥,客户端将抛出类似于permission denied的错误。但好处也是很明显的:你可以享受到COM+ transaction,isolation等特点。

    方法三的缺点是,它需要P/Invoke CoRegisterClassObject API,而微软在这篇MSDN文档中已明确指出P/Invoke CoRegisterClassObject是不受support的。

    AIO/CSExeCOMServer的ReadMe.txt中,对方法三的具体操作步骤已有详细的说明。其中值得注意的几点是:

    1. Out-of-proc .NET COM 组件的注册

    Regasm.exe只适用于进程内.NET COM组件的注册。为了注册进程外.NET COM组件,我们需要customize一下Regasm的注册逻辑,以改变其中的部分注册表键值。.NET通过ComRegisterFunctionAttribute和ComUnregisterFunctionAttribute提供了修改Regasm注册逻辑的机会。被这两个属性修饰的方法,将在Regasm完成对ComVisible type默认的注册行为之后被call到。在CSExeCOMServer中,我将HKCR"CLSID"<CLSID of CSSimpleObject>"InprocServer32 key替换成了LocalServer32 key。同时这个key的default value被设置成了CSExeCOMServer.exe的文件路径。

    2. CSExeCOMServer.exe进程的shutdown

    CSExeCOMServer.exe进程的shutdown取决于该COM Server中活着的COM object数量(lock count)。借助于ReferenceCountedObject 这个class,当有一个新的COM object被创建时, lock count自增1。当该COM object被release时(GC/Finalize), lock count自减1。当lock count变为0时(意味着此时没有活的COM 对象了)我们就post WM_QUIT message到COM Server的message loop上以quit这个message loop, 并shutdown COM server。

    考虑到GC触发的时机,我使用了一个timer去定时地触发GC,从而及时地Finalize那些已经可以被释放的.NET COM objects。

    讨论

    .NET中,如果我们要做RPC,.NET Remoting和WCF都是很不错的选择,另外进程内的COM组件也比进程外的好些很多。为什么还需要用.NET写“进程外”的“COM组件”?

    COM有机会优于.NET Remoting和WCF很重要的一点就是,客户端可以使用简单灵活的解释型语言vbscript去编写。另外,使用.NET来写COM,开发人员可以收益与.NET强大的基础类库,轻松写出VC++可能很困难做到的一些事情。

    之所以要写进程外而非进程内COM组件,我想到的原因一是进程外的COM直接支持RPC,二是一些COM应用可能需要进程范围的全局变量,比如session。如果写成进程内的COM组件,这些全局变量将常驻客户端进程。而如果写成进程外COM组件,我们只要适时地shut down那个COM进程,就能灵活地控制这些全局变量了。

  • 相关阅读:
    【52】目标检测之检测算法
    【51】目标检测之特征点检测
    6-----Scrapy框架中Item Pipeline用法
    5-----Scrapy框架中Spiders用法
    4-----Scrapy框架中选择器的用法
    3-----Scrapy框架的命令行详解
    1-----Scrapy框架整体的一个了解
    Python入妖5-----正则的基本使用
    win安装wordcloud报错解决方案
    在新项目下使用rbc权限
  • 原文地址:https://www.cnblogs.com/Jialiang/p/CSExeCOMServer.html
Copyright © 2020-2023  润新知