ATL和MFC,用哪种框架来创建ActiveX控件:第一部分

 本文假定你熟悉MFC, ATL, and COM 
摘要:目前MFC和ATL代表了两种框架,分别面向不同类型的基于Windows的开发。MFC代表了创建独立的Windows应用的一种简单、一致的方法;ATL提供了一种框架来实现创建COM客户机和服务器所必须的样板文件代码。这两种框架在用于开发ActiveX控件的道路上走到了一起。
        我们将看看这两种框架是如何适用于创建ActiveX控件的——突出其优缺点,亲自经历创建一个控件的过程——以便你能够决定何时使用一种框架,何时使用另一种。
    如果你希望用C++来写ActiveX®控件,有两个流行的框架,一个是Microsoft® Foundation Classes (MFC),另一个就是ATL。本文将将深入这两种框架,解释它们对开发ActiveX控件所提供的支持,帮助你更好地决定哪种模型最适合你的开发环境和需求。

ActiveX控件的完全形态

      ActiveX控件基于组件对象模型COM,使得ActiveX控件成为可能的COM的基本原则是:一个对象的接口和其实现能够而且应该分开对待。只要COM的对象和它的客户方代码就接口细节达成了一致,如何实现就不是问题。ActiveX控件展示了大量ActiveX控件包容器理解的接口。因为客户方代码和控件认可这些接口细节达成了一致,你可以编写一个ActiveX控件然后简单的将它放入包容器中。包容器将通过定义良好的接口来驱动控件,而这些控件将以自己的方式做出合适的响应。
    在更高的层次上,一个ActiveX控件是实现了几个主要ActiveX技术的一个COM对象,包括常规引入COM接口,OLE嵌入协议,连接点和属性页。在较低的编程层次上,ActiveX控件只是实现了某些类型接口的COM类。当某些客户方代码成功的查询到这些接口之一时,它就知道了它正在与一个ActiveX控件打交道。

    一个ActiveX控件暴露的接口主要分为三类。第一、ActiveX控件是可嵌入的对象;就是说,它们实现了大多数的OLE文档就地(in-place)激活和嵌入协议。ActiveX控件实现了如下的接口:

IOleObject, IPersistStorage, IDataObject, IOleInPlaceActiveObject, IOleInPlaceObject, IViewObject2和IRunnableObject (这一个很少用到). 第二、ActiveX控件通常都支持属性页,这样客户方就可以修改控件的属性了。最后,ActiveX控件通常都利用COM的连接点技术,实现了客户方能发现的外出接口。

    为了帮助比较ATL和MFC框架,我们来看一下在两种框架中写的相同的控件。此控件监视创建它的线程上传递的消息流。消息流控件是一个很不错的例子,因为它演示了一个ActiveX控件所有主要的方面,包括引入接口、外出接口,属性,永久性以及属性页。让我们从研究这两个框架提供的标准的COM支持开始吧。

MFC中基本的COM支持

    Microsoft建立MFC使得开发Windows®应用程序比使用SDK容易多了。有了MFC,Microsoft接着增加了对即存框架的COM支持。这意味着MFC的开发者在增加越来越多的功能时必须保持框架的完整。同时,Visual C++®编译器那时还不支持模板,因此,它们不得不借助非模板的其它手段来将COM功能掺入类中。Microsoft通过加入一些虚函数到CCmdTarget类和一些宏中解决了这个问题,使得在MFC中实现COM接口有了可能。

    MFC内部的COM支持是从CCmdTarget开始的,CCmdTarget类实现了IUnknown接口,还包括了一个用于引用计数的成员变量(m_dwRef)以及用于实现IUnknown 的6个函数:: InternalAddRef, InternalRelease, InternalQueryInterface, ExternalAddRef, ExternalRelease, 和 ExternalQueryInterface.。QueryInterface的两个版本——AddRef和Release支持COM聚合。InternalAddRef, InternalRelease和InternalQueryInterface完成引用计数和QueryInterface操作,而ExternalAddRef, ExternalRelease和 ExternalQueryInterface代理控制聚合的对象(如果此对象参与聚合的话)。

    MFC使用嵌套的类复合策略来实现COM接口。在MFC中,想实现COM 接口的类是从CCmdTarget中派生的。每个由CCmdTarget派生出的类实现的接口得到它自己的嵌套类。MFC使用宏BEGIN_INTERFACE_PART和END_INTERFACE_PART来产生嵌套类。

    最后,MFC实现了表驱动的QueryInterface。MFC的接口映射的工作机理同它的消息映射基本相同:MFC的消息映射把一个Windows消息和一个C++类中的函数相联系;MFC的接口映射把一个接口的GUID和一个表示此接口的特定的vptr的地址相联系。每个实现COM接口的基于CCmdTarget的类通过更多的宏:DECLARE_INTERFACE_MAP, BEGIN_INTERFACE_MAP, INTERFACE_PART,和 END_INTERFACE_MAP来获得增加的接口映射。

    为了理解这些宏在实际中是什么样子的,请看图一,它说明了实现ActiveX控件的MFC类 COleControl。当你细读代码时,注意COleControl带有夹在一对BEGIN_INTERFACE_PART 和 END_INTERFACE_PART宏之间的每个接口的签名,还要注意COleControl的接口映射表有22个条目。

    除了实现了IUnknown接口,MFC还包括IClassFactory的一个标准实现。MFC又一次通过若干宏提供了此支持。MFC有两个宏来提供类对象:DECLARE_OLECREATE_EX 和 IMPLEMENT_OLECREATE_EX.。在一个基于CCmdTarget的类中使用这些宏增加一个COleObjectFactory类型的静态成员到该类中。如果你看一下AFXDISP.H中 COleObjectFactory的定义,你将会看到用在COleObjectFactory 中的MFC的嵌套类宏为实现IClassFactory2定义了一个嵌套类。IClassFactory::CreateInstance的MFC版本使用MFC的动态创建机制(DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE宏打开此功能)来实例化COM类,因此有了MFC的COM支持同样意味着就有了它的动态创建机制。

    最后几个由MFC提供的在ActiveX控件内的基本COM支持是对IDispatch的支持。用Visual C++ 和 MFC实现一个分发接口几乎是微不足道的。在MFC中实现一个分发接口,只需要使用ClassWizard就可以了。ClassWizard中的“Automation”标签有一个按钮用于添加属性,另一个用于添加方法。
    在MFC中,IDispatch支持来自CCmdTarget的类。IDispatch 的MFC的实现实际上在一个叫做COleDispatchImpl 的类中,COleDispatchImpl派生自IDispatch,实现了所有4个IDispatch函数:GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, 和 Invoke.。由CCmdTarget派生的类通过调用EnableAutomation,将IDispatch vptr加入到它们的接口映射中。当客户在基于MFC的ActiveX控件上调用IDispatch 的QueryInterface时,CCmdTarget交出链接在COleDispatchImpl上的vptr。

    每次你使用ClassWizard将一个自动属性或者方法加入到一个类中时,你同时也在该类的分发映射表中加入了一项。一个分发映射表是一个将DISPIDs(用来调用分发成员的符号)和它们的供人读的名字以及和实际完成这个工作的某些C++代码联系起来的简单的表格。COleDispatchImpl的调用以及GetIDsOfNames函数通过在类的分发映射表中查找分发成员并分发DISPID相对应的函数来工作。
    MFC能为某些基于COM的高级技术如OLE文档、OLE拖放和自动操作提供非常好的支持,然而,如果你想更改框架——比如说,你想将分发接口转为双接口的——你就得大动手脚了。另一方面,ATL更加地以COM为中心。(待续)

阅读更多
个人分类: ATL/COM WINDOWS
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭