COM标准建立在二进制可执行代码级的基础上,不论何种工具、语言开发的组件,只要符合COM规范,就可复用于VC、VB、Delphi、BC等各种
4006-054-001 立即咨询发布时间:2022-10-05 21:43 热度:
摘要:COM标准建立在二进制可执行代码级的基础上,不论何种工具、语言开发的组件,只要符合COM规范,就可复用于VC、VB、Delphi、BC等各种开发环境中。COM的语言无关性将软件复用的层次从源代码级推进到了二进制级,复用更方便,也更安全。
关键词:COM组件,接口,生命周期,C++类,ATL组件类,C++基类,ATL模板基类,继承
1. 问题的提出
自从Microsoft公布了COM(Component Object Model,组件对象模型,简称COM)技术以后,Windows平台上的开发模式发生了巨大的变化,以COM为基础的一系列组件技术将Windows编程带入了组件化时代,传统的面向对象的软件开发方法已经逐渐被面向组件的方法所取代。
然而,COM技术带来全新的软件设计和开发模式的同时,也带来了新的问题。
许多软件公司在开发自己的软件产品过程中,都累积了大量C++类,这些代码设计精良,功能完备,以面向对象的标准来检验无可挑剔。然而,这些代码不支持COM,将无法在COM时代继续被复用。如果它们在软件组件化的趋势中被淘汰,那对软件公司和开发人员来说都是极大的损失。
COM专家Don Box曾说过,“COM is a super C++”。这给了我们一个启示,是否可以实现一种技术,能够动态的为普通C++类加上一层COM的封装呢?这样,既可以保持这些代码自身的完整和特性,使它们能继续应用于原来的系统,也可以在需要作为组件使用的时候,把它们动态转变成组件,复用于新系统。
一个自然而然的想法是,为每一个C++类开发一个只暴露一个接口的COM组件,将原C++类的每个public方法都对应于该接口的一个方法,接口方法的实现可以简单的调用相对应的C++类方法即可。这样,程序逻辑由原有的C++类控制,但COM层的封装则由组件提供。基本思路如下图所示:
本文就这一技术展开讨论,最终提供一套由普通C++类平滑过渡到COM组件的完整解决方案。我们选用ATL(Active Template Library,活动模板库,简称ATL)作为COM组件的开发工具,开发环境为Visual Studio 6.0。如没有特殊说明,下文中的“C++类”指没有组件特性C++类,“C++对象”指C++类的实例;“ATL组件类”指用于包装的ATL类,“ATL对象”指ATL组件类的实例。
2. 用ATL包装C++类
按上述思路将C++对象动态组件化后,所得的组件实际上由两部分组成:ATL组件对象和绑定的C++对象。两者的生命周期互相牵制,但要保持一致。生命周期的管理是C++类动态组件化的首要难点。
C++类分为两种,一种是简单的C++类,一种是集合型的C++类。集合型的C++对象管理一组C++对象,负责其创建和删除,维护它们的生命周期。下面,分别就简单C++类和集合型C++类的组件化技术进行说明,展示解决方案的核心技术。
2.1. 简单C++类的组件化
为使ATL组件类可以自由调用C++类的方法,需要:
l 为ATL组件类安插一个指针成员变量,指向C++类
l 提供ATL对象和C++对象的绑定机制
我们可以在ATL组件类初始化时创建一个C++类,用成员变量m_pCPPObj记录,在析构时删除,从而实现ATL组件类和C++类的天然绑定。但出于灵活性考虑,使得ATL组件对象可以绑定任意C++类的对象,我们为ATL组件类添加一个绑定函数Link2CPPObj(CImplement* pObj)。
在ATL组件类的构造函数内,创建一个C++对象,用m_pCPPObj记录。
如果调用了Link2CPPObj,则将m_pCPPObj指向的对象删除,改用传入的C++对象。
在ATL组件类的的析构函数内,删除其绑定的C++对象。由构造函数和Link2CPPObj函数的定义可知,m_pCPPObj指针总是有意义的。
2.2. 集合型C++类的组件化
集合型C++类的情况有所不同。
集合型C++类以数组(array)、列表(list)、映射表(map)的形式管理其它C++对象。集合对象和它管理的元素对象都被包装成组件后,集合型ATL对象可能调用一个“Destroy”方法,期望删除某一个元素ATL对象;这一操作的实质却是,集合型C++对象的“Destroy”方法被调用,将元素C++对象删除了,而元素ATL对象却不知道。这一操作的结果导致了元素的ATL对象存在,而其绑定的C++对象却被删除的情况,两者的生命周期出现了不一致。
为了解决这个问题,我们需要在C++对象被删除时,能将ATL对象同时删除;而在ATL对象的引用计数为0需要删除自身时,也能把C++对象删除。可行的解决方案是:
l 在C++类中保存一个接口指针,指向绑定在一起的ATL对象;为该接口指针赋值的最佳地点显然是提供绑定机制的Link2CPPObj函数内部,为此,还需要给Link2CPPObj添加一个IUnknown*参数
l 在C++类的析构函数中,判断该接口指针是否为空,如果不为空,则Release对接口的引用,引发ATL对象自身的析构
2.3. 内部创建的组件和外部创建的组件
集合型C++类组件化后仍然是集合型ATL组件,它可以创建、删除自己管理的组件。这样,组件的创建就可能有两种情况:
l 由客户调用集合型组件的接口方法间接创建
创建方式的不同导致了组件生命周期管理的复杂性。一般说来,组件的创建者负责维护组件的生命周期。上述两种情况下,分别由客户和集合型组件维护被创建组件的生命周期。然而,另有一种情况是,客户创建了一个组件,然后送交一个集合型组件管理,现在维护组件生命周期的责任就由客户转交给了集合型组件。
我们的解决方案必须提供这样的健壮性和灵活性,以维护各种情况下组件的生命周期。我们为ATL组件类添加一个BOO成员m_bInnerManage,作为组件的维护标识。内部维护意味着组件的生命周期由其它组件(集合型组件)维护;外部维护则是由客户维护。
缺省情况下,组件是外部创建并维护的,在组件的构造函数内设置外部维护标识。集合型组件创建元素时,需要为元素分别创建一个C++对象和一个ATL对象,然后调用ATL对象的Link2CPPObj函数将两者绑定在一起,在Link2CPPObj函数内修改维护标识。对于第三种情况,可以在外部创建组件由客户转交给集合型组件时,在集合型组件相应方法内重新设置维护标识。
2.4. C++基类
为了对现有C++类的改动最小,我们设计一个基类封装需要为C++类添加的功能。所有需要动态组件化的C++类都必须从这个基类派生,以保证动态组件化中C++对象与ATL对象生命周期的一致。如下图示:
摘要:虚拟参考站的出现是GPS定位的有一项突破,它标志着GPS的发展进入了一个新阶段,它不仅使GPS提高了精度,同时扩大了...
20世纪70年代功能翻译理论在德国蓬勃发展。1971年卡塔琳娜·赖斯 (KatharinaReiss) 在《翻译批评的可能性与局限性》( Possibiliti...
近年来,包括我国在内的世界各国先后发布限时停售传统燃油车的政策。预计10年后,在我国销售的增量新车中,将没有内燃...
结合某项目的太阳能光伏照明设计,具体阐述并网发电系统的构成及功能,并对光伏发电的效益做一定的分析...
随着信息时代的到来,数字化技术被广泛应用。数字化技术在飞机装配中的应用显示了许多优势,使我国航空产品的开发发生...
本文简要介绍了通过AutoCAD的DXF数据格式与MAPGIS的MPJ数据格式直接转换在地质工作中存在的一些不足之处,重点阐述了AutoCAD的...