COM编程
一、COM组件基础
1.程序的编写方式
1) 面向过程的结构化编程
自顶向下的编程方式(流程图)
2) 面向对象的编程
以对象或以类为中心,更符合人的思维习惯,通过模拟现实情况完成软件的编写。(类图)
3) 面向组件的编程
将一些功能直接封装成可以执行的二进制模块,类似于搭积木的方式。更适合大型项目,功能需求频繁发生变更的项目的开发,功能的复用,复用性更高的。
2. 组件及组件的特点
组件就是可以执行的二进制代码,可以为系统,软件或者其他组件提供相应的功能。
特点:
- 二进制代码,可以执行
- 动态加载和卸载
- 和具体的编程语言无关
3. 组件的标准
COM提供可组件编写的彼标准,任何开发的语言编写组件时。都要遵循这个标准。
二、COM接口
1.接口Win API –Win32应用程序编程接口(函数库)
C语言 接口就是函数、
C++语言,接口就是类的公有函数集合
DLL的接口就是它所输出的那些函数
COM接口 就是一组纯虚函数集合,是一个包含一个函数指针数组的内存结构,每个数组元素包含的是一个由组件所实现的函数的地址。
2.C++接口的实现
1.定义纯虚函数,不能有任何成员变量和非纯虚函数
2.基于纯虚类,派生实现类
3.添加创建对象的函数,并使用接口返回创建的对象
引入c++接口之后,当功能的提供者发生变化时,功能的使用者代码不用修改,两者之间的耦合性降低了。
Dll接口的实现
功能的提供者放到动态库中和功能的使用者分开两个不用的工程
1.创建Win32 dll的工程
2.添加头文件XXX.h,在文件中添加接口的定义
3.在工程的实现文件中添加实现类以及创建对象的函数
- 导出创建对象的函数,通过添加def文件导出该函数
Dll 接口的使用
调出导出函数的步骤:
- 加载动态库,LoadLibrary获得句柄。
- 获取导出函数的函数地址,GetProcAddress
- 调用导出函数,创建对象,返回接口。
通过接口调用函数完成相关的功能
三、COM组件
它是以win32动态链接库(DLLs)或可执行文件(EXEs)的形式发布的可执行代码组成的。
COM组件是动态链接的,使用DLL将组件动态链接,其次必须还是封装的。组件本身只是接口的实现细节,在COM中接口比实现接口的组件更为重要。COM组件是完全与编写语言无关的。开发组件不一定非用类,只是用类来实现组件更容易。
以二进制形式发布的
在不妨碍老客户的情况下被升级
可以透明地在网络上被重新分配,对远程机器上的组件同本地及其上的组件的处理方式没有差别。
COM使用了DLL来给组件提供动态链接的能力。
C++中可以使用抽象基类来实现COM接口,一个COM组件
可以支持任意数量的接口,可以有通过多重继承来实现一个提供多个接口的组件。一个接口是一个函数集合,一个组件是一个接口集,而一个系统则是一系列组件的集合。
COM组件与COM接口:
组件实现信息的隐藏,有利于代码的保护。
COM组件的实现:
每个组件ID:每个组件都通过GUID来标识。
在IDL文件中定义组件
接口类定义:(objbase.h头文件中)
#define interface struct
接口的性质:接口不变性、多态以及接口继承
接口不变性:当对组件升级时一般不会修改已经有的接口,而是加入一些新的接口。此时多重继承为组件和客户可以智能地同对方的新版本进行交互提供可坚实的基础。
多态:以同一种方式处理不同的对象。客户可以按照相同的方式处理不同的组件,一个组件所支持的接口越多,组件就应该越小,较小的接口表现简单的行为,大的接口表现更多的行为。
一个接口的行为越多,则特定性越强,被其他组件复用的可能性将越小,对于不能复用的接口,使用此接口的客户代码也将不能复用。所有的COM接口都必须继承一个名为IUnknown的接口。
COM需要的所有的接口都支持三个函数,QueryInterface函数、AddRef函数与Release函数。使得的所有的COM接口前三个函数均为它们。
QueryInterface函数:查询组件其他的接口
IUnknown接口(头文件UNKNWN.H):
客户调用QueryInterface决定组件是否支持某个特定的接口。
AddRef与Release函数可以控制接口的生命期。
获取一个指向IUnknown接口的指针:通过CreateInstance函数,他可以建立一个组件并返回一个IUnkown指针。
IUnkown *CreateInstance();
idd也标识客户所需的接口,是一个”接口标识符”IDD(GUID)结构。如:
typedef struct _GUID
{
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
ppv是QueryInterface存放所请求的指针地址,返回值HRESULT返回为S-OK或E_NOINTERFACE,对于此返回值,需要使用SUCCEEDED宏或FAILED宏来比较判断返回值,而不能直接使用。
QueryInstanceface对所有的IUnknown接口查询请求都必须返回相同的指针。
QueryInstanceface实现规则:
1.QueryInstanceface返回的总是同一IUnknown指针。
2.若客户曾经获取过某个接口,那么他将总能获取此接口。
3.客户可以再次获取已经拥有的接口。
4.客户可以从任何接口返回到起始接口
5. 若能从某个接口获取某特定接口,那么可以从任意接口都可以获取此接口。
组件的实例只有一个IUnknown接口,当查询组件实例的IUnknown接口时,不论哪个接口,所得到的均是同一指针值。
客户曾经获取过某个接口,那么他将总能获取此接口,同样若是失败,以后也会失败,除非当创建组件的一个新实例时,此条规则不一定适用。
所有的接口都继承了IUnknown,许多函数都需要一个IUnknown指针作为参数。他们应该使用任何IUnknown指针获取任何接口;
《COM技术内幕》P44,即60页