亚洲【COM服务器的伪异步技术及其实现】

COM服务器的伪异步技术及其实现

COM服务器的伪异步技术及其实现 摘要:COM组件技术在Windows操作系统中发挥了极其重要的作用,但标 准COM组件技术在实时操作性上不尽完善。作为补充可以用COM连接点技术和 多线程技术来构造一种伪异步技术, 完成某些前者不能完成的任务。本文详细 描述了其实现原理,并根据技术细节提供了作者的解决方案。

关键词:
COM;异步调用;连接点;接口;STA;
MTA 1. COM异步技术 与普通同步执行的COM服务器不同,一个提供异步方法调用的COM服务器 允许客户程序以一种非阻塞的方式来访问它,客户程序可以在等待调用返回的过 程中继续进行别的工作,从而提高工作效率。

尤其随着Windows2000和COM+ 的发布,COM现在已经完全支持异步方法的调用了,通过IDL的定义,由MIDL 编译器为接口的每个方法实现同步和异步两种独立的调用定义。但是COM提供 的这种异步方法调用并非尽善尽美: 1) 这种技术发布较晚,只在WIN2000下被支持,所以用它开发的软件不能 在Windows 95和Windows NT平台上运行。

2) 客户端使用不便,尽管COM通过MIDL生成的接口代理(proxy)与存根 (stub)完成了大部分为支持异步方法调用所做的工作,客户端仍需要完成系列 繁琐的细节工作。

3) 无论客户端还是服务器端都需要聚合由COM提供的对象,方法调用方式 也有别于标准同步调用,因而不适用于标准ACTIVEX控件的编写,而ACTIVEX 控件实质上是一种特殊的COM服务器。

由于上述一系列缺陷,往往需要一种替代解决办法,通常可以用一些基本的 COM技术来构造一种伪异步技术。

2. COM应用 这种伪异步技术是通过接口回调(interface callback)的方法来实现的,它基 于两项关键的COM技术: 连接点(Connection point)技术和COM多线程 (Multithread)技术。

连接点技术的基础是COM的出接口(outgoing interface)。出接口与普通的 接口不一样,它由COM服务器端进行定义,但却由用户程序来实现(此实现对 象被称为接收器sink),并把接口指针告诉给COM服务器。由于出接口是在服务 器的类型库中定义, 所以COM服务器可以很方便的利用此接口指针与客户进行通讯。但直接使用出接口,客户程序和COM服务器都需要出接口有一定的了解, 为此COM又提供了连接点技术(可连接对象),如图: COM提供的可连接对象,通过IConnectionPointContainer接口管理所有的 出接口。对应于每个出接口,可连接对象管理一个连接点对象,每一个连接点对 象实现了IConnectionPoint接口,客户通过连接点对象建立接收器与可连接对象的 连接,且通过两个枚举器的引入,使得可连接对象支持多个出接口,而每个出接 口支持多个与接收器的连接。明显,连接点技术的实质是对出接口的一种封装。

通过使用连接点技术,COM服务器可以用一种非常常规的方法描述它的引出接 口,并为客户程序提供了一种标准的方法来实现这些接口,对于实现服务器组件 的标准化非常方便,也有利于客户程序的独立开发,符合中间件开发的原则。

COM多线程管理相当复杂,要实现多线程,应对COM的线程模型有深入的 了解。COM支持两种线程模型:单线程单元(STA,Single-Threaded Apartment) 和多线程单元(MTA,MultiThreaded Apartment)。如图所示:
单元是一种抽象的概念,定义了一组对象的逻辑集合,这些对象共享同一组 并发性和重入限制,每个COM对象都只能属于某一个单元。STA只能包含一个 线程(单元线程),与32位Windows平台上的用户界面(UI)线程相似,单元线 程拥有一个隐藏的窗口,所有对此线程中服务器组件方法的调用都被COM转化 为消息(message),通过消息循环进行消息分发,并调用窗口过程保证调用到 COM对象的成员函数中;而每个MTA可以包含多个线程(自由线程),自由线程 相似于Windows平台的工作(Work)线程,不带消息分发,可以直接使用。每个 进程只能有一个MTA,但是可以于有任意多个STA,每一个使用COM的线程必 须通过调用CoInitialize(0)来新建一个STA,或者通过调用CoInitializeEx(0, COINT_MULTITHREAD)来创建或加入一个MTA,否则这个线程将出错。服务 器组件实体存在于线程中,要么处于MTA中,要么处在STA中,别无选择。遵守 如下规则:跨单元的接口指针传递需要调度,否则指针无效。

3. 构造伪异步COM服务器 3.1伪异步COM服务器总体框架 伪异步调用的实质是客户程序将一个自己所实现的接收器指针传递到COM 服务器,当服务器的接口受到客户程序调用的时候将产生新的线程来实现功能任 务,而客户程序的调用结束返回,新线程将在任务完成后通过传入的接口指针回 调到客户程序,以通知客户程序当前调用服务的消息和结果,从而在客户程序和 服务器之间产生一种对等关系。服务器和客户程序异步协商过程如下:
1) COM实现一个服务器组件对象,它要么处在STA中,要么处在MTA中。2) 客户程序通过COM服务器的公开接口查询IConnectionPointContainer接口, 客户查询成功就表明服务器支持连接点。

3) 查询成功,客户程序使用上面接口的FindConnectionPoint()方法来定位 特定的出接口;或者用EnumConnectionPoints()方法枚举所有连接点,再进行查 询定位。这样客户程序可以获得一个IConnectionPoint接口,并通过它建立与服务 器的连接。

4) 定位成功,则由客户端实现这个出接口,这里的接口实现也即接收器 (Sink)。

5) 客户程序通过IConnectionPoint::Advise()方法将一个指向接收器的 一个出接口指针传递给服务器。连接被建立。

6) COM服务器收到调用请求,创建新线程,并由新线程通过这个收到的出 接口指针进行回调(callback)操作,将通知或消息反馈到接收器,从而完成一 次异步调用。

这是一种非常常规的方法,为客户程序提供了一个标准的方法来实现这些接 口,而且MFC(微软基本类库)和ATL(活动模板类库)都对连接点技术提供了 很好的支持,使用方便。当然也能在服务器组件中直接使用出接口,从而省去出 于标准化目的而对出接口进行的封装。这样的好处是服务器组件直观、高效,可 以减少额外的开销,但是它不能为客户程序提供一个标准的方法来实现出接口, 所以建议只在服务器和客户端配合开发,双方均了解出接口,且接口较为简单的 情况下使用。

4. 结论 要实现一个伪异步工作的COM服务器,必须对连接点技术和COM线程模型 进行合理的组合使用,尤其线程模型的选择更是我们实现高效COM服务器的关 键。此外,作者个人推荐使用ATL来开发这样一个服务器组件,因为它灵活,高 效,对COM的各项技术均提供了极好的支持,尤其在对COM线程模型的支持方 面,是MFC所不能比拟的。

[参考文献] [1]Tom Armstrong 等 (著),董梁 等(译), ATL开发指南[M] ,北京: 电子工业出版社 [2]潘爱民,COM原理与应用[M] , 北京:清华大学出版社 [3]Martin Gudgin(著)宋亚男(译),IDL精髓[M] , 北京:中国电力出版 社 [4]MSDN(2001),Eight Lessons from the COM School of Hard Knocks[C][6]MSDN(2001)/Q15077,Descriptions and Workings of OLE Threading Models[C]