课件18 使用MFC进行COM编程.pdf
《课件18 使用MFC进行COM编程.pdf》由会员分享,可在线阅读,更多相关《课件18 使用MFC进行COM编程.pdf(11页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Windows 程序设计程序设计 版权所有(C),户现锋,2004 年 课件 18 使用 MFC 进行 COM 编程 基本目标:掌握使用 MFC 进行 COM 组件的开发 许多人认为 MFC 是一个主要用于创建图形用户界面的类库,这一点当然无可辩驳。然而在 MFC 中还有大量的 OLE 代码,可以方便 COM 客户和服务器的创建。创建 COM 组件可以使用 ATL 和 MFC,那种方法更好呢?实际上,它们都是选择。一个比较好的原则是,如果 COM 对象对 GUI 有大的需求,那么使用 MFC 可能更好。如果COM 组件的大小和速度是直观重要的,可以采用 ATL。MFC 对 COM 组件的支持依
2、赖于 MFC 对多重接口的支持。多重接口、多重继承和嵌套类多重接口、多重继承和嵌套类 多重继承是实现多重接口的很好的选择。使用多重继承可以很容易地把这两个接口绑定到一个组件类。但是使用多重继承容易导致符号冲突的问题。最简单的情形是每一个接口都从 IUnknown 继承,所以多重继承在 IUnknown 的三个方法上都存在冲突。一般来说,这并没有什么大的问题。但是一个复杂的组件可能接口级别进行引用计数,而不是在对象级别进行引用计数,多重继承在这种情况下几乎是不可能的,因为只有一套实现 IUnknown 的方案,代码不能很好地区分调用是从那个接口进来的,即使可以,也是比较复杂的。在 MFC 中,没
3、有使用多重继承来支持多重接口,而是使用嵌套类的方法来支持多重接口。CCmdTarget 类中的类中的 IUnknown 实现实现 MFC 组件不直接从 IUnknown 继承,MFC 的 COM 类从 CCmdTarget 继承。我们可以把CCmdTarget类想象为MFC的IUnknown,CCmdTarget不仅包含了一些重要而且基本的COM功能,它还提供了在 MFC 中每个窗口类都必须的关键框架。考虑到对自动化和消息处理的支持,我们应该知道对于 COM 和用户接口类等来说,CCmdTarget 是一个非常重要的类。对于 COM,CCmdTarget 给 COM 对象提供了一个现成的 IU
4、nknown 实现的基类。下面的代码是从 MFC 的头文件中摘录的代码:class CCmdTarget:public CObject public:/these versions do not delegate to m_pOuterUnknown DWORD InternalQueryInterface(const void*,LPVOID*ppvObj);DWORD InternalAddRef();DWORD InternalRelease();/these versions delegate to m_pOuterUnknown DWORD ExternalQueryInterfac
5、e(const void*,LPVOID*ppvObj);DWORD ExternalAddRef();DWORD ExternalRelease();在 CCmdTarget 中,有两组使用 Internal 或者 External 前缀的方法。只有在你的 COM 对象是聚合关系的一部分时,内部和外部 IUnknown 方法之间的区别才变得重要起来。在我们实现接口时,我们需要调用外部的 IUnknown 实现。声明嵌套类 在使用 MFC 创建 COM 组件时,你实际上是在创建一个外部封装类,该类为每一个组件 支 持 的 接 口 维 护 一 个 嵌 套 类。为 了 帮 助 声 明 嵌 套 类,
6、MFC提 供 了BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 两个宏。下面是 BEGIN_INTERFACE_PART 的定义,该宏有两个参数,给嵌套类指定的名称和派生嵌套类的接口类:#define BEGIN_INTERFACE_PART(localClass,baseClass)class X#localClass:public baseClass public:STDMETHOD_(ULONG,AddRef)();STDMETHOD_(ULONG,Release)();STDMETHOD(QueryInterface)(REFIID iid,LPVO
7、ID*ppvObj);下面是 END_INTERFACE_PART 宏的定义,该宏结束类的声明,声明了该类的一个实例,并且将嵌套类声明为外部类的友类,这使得任何内部类都可以自由地存取外部的数据成员和方法:#define END_INTERFACE_PART(localClass)m_x#localClass;friend class X#localClass;在 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 之间,我们需要放置接口类的除了 IUnknown 的三个方法之外的所有方法。实现实现 QueryInterface 的方法的方法 MFC COM 类的
8、 QueryInterface 实现象消息映射一样,依赖于映射表格。MFC 中存在许多不同的映射,有连接映射、调度映射、事件接收器映射、消息映射和接口映射等等。一般来说,不管是那种类型的映射,都可以把映射看作是 MFC 基于某种唯一标识符来查找某块执行代码的方法。比如在消息映射中,MFC 使用消息 ID 来查找属于某个 MFC 子类的处理函 数。这 个 表 格 主 要 由 以 下 几 个 宏 构 成:DECLARE_INTERFACE_MAP、BEGIN_INTERFACE_MAP、END_INTERFACE_MAP和INTERFACE_PART。MFC IUnknown接口是接口表格的第一个
9、接口进行类型转换得到的。宏 INTERFACE_PART 主要用来填充表格,说明 COM 类支持的接口,该宏有三个参数,组件类的名称、接口 IID 和负责实现该接口的嵌套类的名字。定义如下:#define INTERFACE_PART(theClass,iid,localClass)&iid,offsetof(theClass,m_x#localClass),MFC COM 类工厂类工厂 MFC 使用宏 DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE 给 COM 提供了动态创建的能力。这个宏并不是专门为 COM 或者 OLE 提供的,MFC 中的很多功能都依赖于
10、类的动态创建的特性。MFC使用宏DECLARE_OLECREATE和IMPLEMENT_OLECREATE宏为COM提供了类工厂的实现,但是这两个宏依赖上面提到的提供动态创建能力的宏。这两个宏的定义如下:/Macros for creating creatable automation classes.#define DECLARE_OLECREATE(class_name)public:static AFX_DATA COleObjectFactory factory;static AFX_DATA const GUID guid;#define IMPLEMENT_OLECREATE(cl
11、ass_name,external_name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)AFX_DATADEF COleObjectFactory class_name:factory(class_name:guid,RUNTIME_CLASS(class_name),FALSE,_T(external_name);AFX_COMDAT const AFX_DATADEF GUID class_name:guid=l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8 ;进行组件生命期的管理进行组件生命期的管理 为了对组件的生命期进行管理,需要对组件中的 COM
12、对象进行计数。MFC 是通过两个函数 AfxOleLockApp()和 AfxOleUnlockApp()来完成计数工作的。一般在 COM 的构造函数中调用 AfxOleLockApp()增加组件的全局对象的引用计数,在析构函数中减少组件的全局对象的引用计数。MFC将全局对象的引用计数和服务器所计数合二为一,也就是说对于类厂的LockServer调用,也是简单地调用上述两个函数。为嵌套类实现为嵌套类实现 IUnknown 接口接口 每个嵌套类都从一个接口类继承,而该接口类从 IUnknown 继承,我们必须实现嵌套类中的所有的函数,包括 IUnknown 的方法,否则无法实例化类。在实现的时刻
13、,我们必须把嵌套类的IUnknown接口重定向到外部类所支持的IUnknown接口上。下面是一个嵌套类的实现:STDMETHODIMP_(ULONG)COuterCls:XCInterCls:AddRef()METHOD_PROLOGUE(COuterCls,CInterCls)return pThis-ExternalAddRef();STDMETHODIMP_(ULONG)COuterCls:XCInterCls:Release()METHOD_PROLOGUE(COuterCls,CInterCls)return pThis-ExternalRelease();STDMETHODIMP
14、COuterCls:XCInterCls:QueryInterface(REFIID riid,void*ppv)METHOD_PROLOGUE(COuterCls,CInterCls)return pThis-ExternalQueryInterface(&riid,ppv);仔细看以下我们可以发现,每个方法的第一行都使用一个宏 METHOD_PROLOGUE,下一行把对 IUnknown 方法的调用转发到 pThis 变量,pThis 从那而来呢?pThis 变量是 COuterCls 对象的指针,该指针是根据 this 指针计算出来的,this 指针是COuterCls 内部的类 XCI
15、nterCls 的变量 m_xCInterCls 的指针,而不是指向 COuterCls 实例本身。而 pThis 变量是 COuterCls 对象的指针,有了该指针,把方法调用重定向到 COuterCls从 CCmdTarget 继承而来的 IUnknown 方法就很简单了。pThis 的声明和计算是宏 METHOD_PROLOGUE 的结果。其定义如下:#define METHOD_PROLOGUE(theClass,localClass)theClass*pThis=(theClass*)(BYTE*)this-offsetof(theClass,m_x#localClass);AFX_
16、MANAGE_STATE(pThis-m_pModuleState)pThis;/avoid warning from compiler 我们可以看出,从当前的 this 指针的值减去减去嵌套类的成员在外部类中的偏移地址即可得到 pThis,就可以得到 COuterCls 对象的指针。因此,有了 pThis 指针,可以自由地存取外部类的方法和数据。其余的事情其余的事情 MFC 为 组 件 的 注 册 和 解 除 注 册 提 供 了 相 应 的 实 现,主 要 是 通 过 调 用 函 数COleObjectFactory:UpdateRegistryAll()来完成函数 DllRegisterS
17、erver(),该函数在内部查询组件内部的每个类工厂的 CLSID 并创建合适的注册表入口,没有比这更简单的了。解除组件的注册可以使用 COleObjectFactory:UnregisterAll()函数来轻松实现 DllUnregisterServer。MFC 还通过函数 AfxDllGetClassObject()轻松实现了 DllGetClassObject 入口,利用AfxDllCanUnloadNow()函数实现了 DllCanUnloadNow 入口。利用利用 MFC 创建创建 COM 组件例子组件例子 下面我利用 MFC 创建一个 COM 组件,该组件支持两个接口,IDispa
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 课件18 使用MFC进行COM编程 课件 18 使用 MFC 进行 COM 编程
限制150内