COM原理及应用---- 用Visual C++开发COM应用

来源:互联网 发布:js中event属性 编辑:程序博客网 时间:2024/04/30 08:03

COM原理及应用---- 用Visual C++开发COM应用

1、MFC和ATL

    对于COM应用的开发来说,建立一套标准的、有能够不断发展的类库是非常有意义的,这样可以实现程序的重用性。
Microsoft Visual C++提供了两套满足这样需要的类库:MFC库和ATL模板库。MFC不仅可用于建立COM应用,它更是一套Windows平台上各种应用开发的基本类库,而ATL则主要侧重于COM应用的开发,利用ATL可建立一些小巧、快捷的COM组件。

2、Win32 SDK

    Win32 SDK是开发Windows应用程序最基本的开发工具,也是功能最强大的开发工具,但Win32 SDK只提供了C语言的API,所有的接口均以C函数和C结构的形式提供,这使得开发应用程序的代码量很大。而且Windows应用程序的界面不易实现。

3、Win32 SDK对COM的支持

    由于Win32 SDK是Windows最基本的开发工具,而COM又是Windows系统的基本软件模型,尤其OLE完全建立在COM的基础上,所以对COM的支持也是Win32 SDK的一个重要组成部分,在Win32 SDK中我们也可以看到COM库的所有API函数,以及COM和OLE定义的标准接口。

Win32 SDK提供的C/C++头文件定义了COM和OLE标准接口和API函数的定义和声明,也提供了实现这些函数的静态连接库。头文件均按照Microsoft C/C++语言标准语法对接口以及COM函数或者数据类型进行定义和说明,而且这些数据结构也与MIDL相兼容。

4、MFC基础

    应用类(CWinApp的派生类)实例在MFC程序全局均可访问,可以通过MFC提供的辅助函数AfxGetApp得到此实例对象。CWinApp类有几个虚函数值得注意:InitInstance为应用的初始化函数,ExitInstance为应用的终结处理函数,OnIdle为消息循环在程序接收到消息的间隙调用的空闲时间的处理函数,Run为消息循环函数。CWinApp中还包含了一个窗口类(CWnd)数据成员m_pMainWnd,它代表了应用程序的主窗口,对于不同类型的MFC程序,此窗口类也有所不同。由于应用类实例是个全局对象,所以实际上主窗口对象也是个全局生存周期的对象,可以通过对种途径获取,如AfxGetApp->GetMainWnd()、AfxGetApp->m_pMainwnd或者直接调用AfxGetMainWnd辅助函数。

    MFC支持三种DLL应用:静态连接MFC库的正规DLL、动态连接MFC库的正规DLL和MFC扩展DLL。COM进程内组件程序应该使用前两种DLL应用。正规DLL是一种标准的DLL程序,可以被各种语言编写的程序调用,而MFC扩展DLL只能被MFC程序调用,但这种DLL可以直接引出整个C++类,使用MFC的C++程序员可以用这种类型的程序提供对MFC的扩展库。

5、MFC对COM的支持

    (1)支持OLE服务或者包容器的SDI应用,更进一步,可以支持ActiveX文档服务或者包容器。

(2)支持OLE服务或者包容器的MDI应用,更进一步,可以支持ActiveX文档服务或者包容器。

(3)支持自动化服务的SDI或者MDI程序。

(4)ActiveX控制应用,生成OCX应用程序。

我们也可以很方便地产生进程内COM组件程序框架,如果要生成进程外COM组件程序框架,可以生成一个支持自动化特性的对话框应用程序,然后去掉与对话框有关代码即可。

6、MFC库结构
    CObject是MFC库中最基本的类,它提供了一些基本的功能,包括序列化(serialization)支持、运行时刻类信息、对象诊断输出、与集合类型相兼容。运行时刻类型信息是MFC对象动态创建的基础,更进一步序列化支持使得对象可以在序列化过程中被动态创建,而且,利用运行时刻类型信息可以在程序运行过程中判断C++对象是否与某个类(通过IsKingOf成员函数)相关联。对象诊断输出特性使得应用程序在调试版本中可以获得对象的状态信息,用于检查内存泄漏、运行过程中转储对象内容等操作。

    CCmdTarget类或者其派生类的实例对象拥有自己的消息处理机制,它可以接收消息,并按照它的消息映射表调用相关的消息控制成员函数。而且CCmdTarget类提供了对COM接口的支持。

    在MFC库的类中,集合类型也属基本类型之一,常用的集合类型包括两种:数组类型和列表类型,用CObject对象作为元素的集合CObList和CObArray具有一些很好的特性,如序列化、列表诊断信息转储、对象数目自动增长等。

7、用嵌套类实现COM接口

    在第二、三章中实现COM接口和对象时,一直用C++(多)继承的方法实现一个(或多个)COM接口,用这种方法实现一个或少数几个接口显得非常方便,而且,在IUnknown::QueryInterface函数中获取新的接口指针也非常方便,直接把this指针转换为相应的接口基类即可,利用C++的vtable实现COM的vtable。MFC没有采用这种继承机制来实现COM接口,而采用了C++嵌套类的原理实现COM接口。用嵌套类实现COM对象和COM接口比较好地反映了二者之间的关系:COM接口只是提供接口服务,而COM对象本身才保持对象的状态。

8、接口映射表

    MFC对COM的支持从CCmdTarget类开始,CCmdTarget使用了一种与消息映射表非常类似的机制来实现COM接口,即接口映射表。接口映射表的基本思想就是使用嵌套类,但它通过一组宏把这些细节隐藏起来了。

    实现COM接口关键是引用计数和QueryInterface函数的实现。MFC的引用计数实现方法很简单,在CCmdTarget类中使用m_dwRef数据成员作为计数器,然后按照COM规范维护计数器,所有的接口共享同一个引用计数器。QureyInterface函数的实现取决于COM接口的实现机制(多继承方式和嵌套类方式)。

    接口映射表记录了CCmdTarget类中每一个嵌套类的接口ID以及接口vtable与父类this指针之间的偏移量,因为嵌套类的作用域在父类作用域的内部,嵌套类不能直接访问父类的成员,CCmdTarget利用嵌套类vtable与父类this之间的偏移量,使嵌套类可以计算出父类的this指针,从而访问父类的成员。此偏移量实际上是个常数,用offsetof宏可以给出成员与父类之间的偏移量,编译器在编译时刻计算此值。MFC使用的接口映射表隐藏了这些细节,使程序编写更加简捷。

    宏BEGIN_INTERFACE_MAP、INTERFACE_PART、END_INTERFACE_MAP在Afxdisp.h头文件中可以找到。

宏BEGIN_INTERFACE_PART、INIT_INTERFACE_PART、END_INTERFACE_PART_STATIC封装了嵌套类的定义。宏BEGIN_INTERFACE_PART定义了接口的前三个成员函数:QueryInterface、AddRef、Release。宏INIT_INTERFACE_PART中定义了记录偏移量的数据成员m_nOffset,并在嵌套类的构造函数中对m_nOffset进行初始赋值。这些宏定义在Afxdisp.h头文件中可以找到。

9、接口映射表实现的几个部分

(1)              在CCmdTarget类和其派生类定义中使用宏DECLARE_INTERFACE_MAP声明接口映射表使用的一些静态成员以及两个成员函数。

(2)              在类的实现部分使用BEGIN_INTERFACE_MAP、INTERFACE_PART、END_INTERFACE_MAP宏定义接口映射表。

(3)              为每个接口定义嵌套类成员。

(4)              实现嵌套类。

10、CCmdTarget类实现IUnknown

    IUnknown是所有接口的基础,CCmdTarget类提供了一种标准实现,并且支持对象被聚合的情形,所以CCmdTarget类实现了两个版本的IUnknown。在CCmdTarget实现的两个IUnknown中,称为内部IUnknown和外部IUnknown,分别对应聚合模型中的非委托IUnknown和委托IUnknown。

    CCmdTarget也可以聚合其他的COM对象,此时m_xInnerUnknown成员记录了聚合对象的非委托IUnknown接口指针。因此利用CCmdTarget类构造COM对象可以很方便地支持聚合和被聚合两种特性,我们可以直接利用CCmdTarget类提供的IUnknown实现。

    在嵌套内部实现IUnknown的三个成员函数时,必须调用父类的外部IUnknown的相应成员函数,这是聚合模型所必须要求的。

11、COM引出函数

    Visual C++的创建向导为COM提供了标准的引出函数。如DllGetClassObject和DllCanUnloadNow,它们是COM程序所必须要求实现的;又如DllRegisterServer是为了实现注册而提供的可选函数;Visual C++没有提供DllUnregisterServer函数,如果需要,必须自己实现。

12、COM的类厂实现

    MFC提供了一个通用的类厂类COleObjectFactory,值得注意的是类名中“OleObject”实际上是指“ComObject”。类厂本身也是一个COM对象,所以COleObjectFactory从CCmdTarget派生,并且它实现了IClassFactory2接口,除了包含CreateInstance和LockServer成员函数,还提供了COM对象许可证支持。

    在COleObjectFactory的成员中,最主要的信息是对象的CLSID和对象的类型信息,类厂的CreateInstance成员函数利用这些类型信息在运行过程动态中创建COM对象。类厂的构造函数中包含了COM对象的CLSID和COM对象动态创建类型信息(由RUNTIME_CLASS提供)。客户要创建一个COM对象必须分两步进行:第一步,客户调用引出函数SllGetClassObject得到类厂对象;第二步,类厂对象再创建COM对象。

    每个MFC程序(DLL 和 EXE)都有一个状态结构AFX_MODULE_STATE,状态结构中记录了当前程序中应用类对象、程序实例句柄、资源句柄、线程信息等许多全局信息。对于COM程序,状态结构也包括一个类厂表,类厂表记录了当前程序所支持的所有类厂对象。所以DllGetClassObject函数调用AfxDllGetClassObject全局函数,AfxDllGetClassObject调用AfxGetClassObject函数得到全局状态结构,进而取出类厂表,然后检查类厂表中每一个类厂对象所记录的CLSID,如果找到客户请求的类厂对象,则返回类厂对象接口即可。在类厂对象的构造函数中,类厂对象把自己加入到状态结构的类厂表中;在析构函数中,它把自己从类厂表中删除,所以类厂表并不需要专门进行维护,其增删操作由类厂对象自己完成。

13、MFC对COM支持小结

    MFC对COM的支持可以从两个方面进行讨论:单个COM对象的实现和类厂的支持。CCmdTarget类提供了COM对象实现的所有支持,它用接口映射表机制可实现任意多个接口,并且CCmdTarget实现的IUnknown很好地支持了对象被聚合的情形。COleObjectFactory类实现了通用的类厂,它从CCmdTarget类派生,并且与CCmdTarget类协作,利用COM对象提供的CLSID和运行时刻类型信息完成对象的创建工作。

    进程内组件程序的几个标准引出函数通过程序状态结构中的类厂表与类厂对象联系起来,而类厂对象又与CCmdTarget派生类联系起来,因此客户程序和COM库可通过这些函数实现对组件程序的各种操作。

    COleObjectFactory也可以用作进程外组件程序的类厂,COleObjectFactory::Register成员函数负责把类厂注册到全局对象表中,以便客户进程调用类厂对象创建COM对象。

14、VidualC++对COM应用的支持

    Vidual C++支持各种COM应用,包括进程内COM程序和进程外COM程序。对于各种建立在COM基础上的应用技术,包括自动化对象、ActiveX控制、OLE服务程序和OLE包容器程序,Vidual Studio都提供了快速的向导支持;而ATL工程可以建立其他一些COM对象,如MTS对象、ASP对象等。

15、ATL

    ATL(Active Template Library)是Vidual C++提供的一套基于模板的C++类库,利用这些模板类,可以建立小巧、快速的COM组件程序。所以说,ATL主要是针对COM应用开发的,它内部的模板类实现了COM的一些基本特征,比如一些基本COM接口IUnknown、IClassFactory、IDispatch等,也支持COM的一些高级特性,如双接口(dual interface)、连接点(connection point)、ActiveX控制等,并且ATL建立的COM对象支持套间线程和自由线程两种模型。

    模板是C++语言的高级语法特性,它是更高层次上的抽象,在使用模板库时,我们不再通过继承的方式使用模板,而是对模板进行实例化以便得到类,可以说模板是对类的抽象,因此,使用和编写模板对程序员的要求更高,尤其要求模板本身的代码必须是类型安全的(type-safe)。

    ATL 实现COM接口的方式与MFC的方式有所不同,MFC使用嵌套类的方式实现COM接口,用接口映射表提供多接口支持;ATL使用多重继承的方式实现COM接口。虽然MFC和ATL实现接口的原理不一致,但两者的支持形式有非常相似,MFC采用接口映射表,定义了一组宏;ATL采用了COM映射表,也定义了一组形式上很类似的宏。

ATL提供了两个基本模板类:CComObjectRootEx和CComCoClass,每一个标准的COM对象都必须继承与这两个模板类。CComObjectRootEx模板类实现了IUnknown成员方法,包括对引用计数的处理以及对接口请求的处理,它也支持对象被聚合的情形;CComCoClass模板类为对象定义了缺省的类厂,并且支持聚合模型。

16、ATL的COM映射表

    COM映射表的原理与MFC的接口映射表相仿,也是建立一张静态成员表,表信息记录了每一个接口IID所对应的vtable与this指针的偏移量,但因为ATL使用多继承方式,所以偏移量是指基类与派生类指针之间的位置差,并且ATL使用了更为灵活的机制,不仅可以指定其偏移量,还可以指定一个函数指针,通过函数间接得到接口vtable的地址。

17、ATL的对象映射表

    为了支持COM对象的创建操作,ATL好定义了对象映射表。对象映射表是一张全局表,它被放在ATL应用的主要文件中。对象映射表中每一项指定了对象的CLSID及它的一些创建函数,包括注册表操作函数指针以及类厂的接口指针等信息,全局的DllGetClassObject函数利用对象映射表获取类厂对象。

18、ActiveX技术

    实际的开发过程中,一般不是对COM的直接开发,而是使用更高层的技术,这些技术以COM为基础,内容涉及到计算机应用的各个领域,从桌面程序之间的通信到Internet应用,从简单的数据处理应用到大型的数据库应用,Microsoft把所有这些以COM为基础的技术统称为ActiveX技术。Visual C++是开发ActiveX应用最重要的工具,MFC和ATL分别为实现这些应用提供了全面的支持。

19、MFC对自动化的支持

    自动化是大多数其他ActiveX技术的基础,它可使解释性的宏语言(如Visual Basic)能够在不了解应用程序实现细节的情况下控制自动化对象。自动化技术直接以COM为基础,所有的自动化对象都实现了标准的IDispatch接口,通过IDispatch接口暴露对象的属性和方法,以便在客户程序中使用这些属性并调用它所支持的方法。自动化对象的客户程序通过类型库(type library)获得对象运行时刻的类型信息,客户程序对对象属性和方法的引用实际上被转化为对IDispatch::Unvoke的调用。MFC提供的自动化支持分为两个方面:

(1)CCmdTarget类本身已经提供了自动化支持,其派生类就具有了IDispatch接口,并且可以通过ClassWizard添加自动化对象的属性和方法。

(2)在使用MFC AppWizard(.exe)生成SDI或MDI应用程序时,如果选择了Automation支持,则工程的CDocument或其派生类支持IDispatch接口。

CCmdTarget类提供了与接口映射表类似的机制实现IDispatch对属性和方法的管理,称为分发映射表,分发映射表的原理和用法与接口映射表非常类似。分发映射表不必手工维护。

20、MFC对ActiveX控制的支持

    MFC对ActiveX控制提供了全面的功能支持,包括基本的自动化属性和方法的支持、永久数据属性的支持、用户界面实地编辑的支持、激发事件的支持等。ActiveX工程中包括一个主要的控制类,它从COlControl派生,而COlControl继承于CWnd和CCmdTarget,它继承了CWnd的所有窗口特性,又利用CCmdTarget提供的接口映射表实现了ActiveX控制所要求的所有接口。因此,在派生类中可以不再编写与COM有关的代码,而只需按一般的MFC窗口类进行编程。

21、MFC对复合文档的支持

    复合文档是OLE的核心技术,也是ActiveX技术的重要部分,但是复合文档技术非常复杂,它本身形成了一套完整的规范,包括复合文档服务程序与包容器程序之间各种交互操作,如界面实地编辑特性、菜单合并标准、数据保存和读取、激活及无效操作等。MFC提供了一组类,实现了复合文档服务程序与包容器程序交互过程中的所有特性,使复合文档程序的开发变得非常简单。

    从COM技术的角度看,实际上复合文档服务程序与包容器程序之间通过一组COM接口进行交互,服务程序和包容器程序只需按照复合文档规范分别实现这些接口,二者就可以在运行时刻进行各种交互操作。 

原创粉丝点击