COM是开发软件组件的一种方法。组件实际上是一些小的二进制可执行程序,它可以给应用程序,
操作系统以及其他组件提供一些服务。开发定制的COM组件就如同开发动态的,面向对象的
API.多个COM对象可以连接起来以形成应用程序或组件系统。并且组件可以在运行时,在不
重新连接或编译应用程序的情况下被卸下或替换掉。Microsoft的许多技术,入ActiveX,DirectX
以及OLE等都是基于COM而建立起来的。
COM并不是一个大的API,它实际上像结构化编程及面向对象编程方法那样,也是一种编程方法。
COM和OLE的关系非常类似于微积分和物理的关系。
第一章 组件
组件,单个的应用程序分隔成多个独立的部分。
组件的好处,可以随着技术的不断发展而用新的组件取代已有的组件。
COM,即组件对象模型,是关于如何建立组件以及如何通过组件建构应用程序的一个规范。
使用组件还有一些可以使对已有应用程序的升级更加方便和灵活的优点,如应用程序的定制。组件库
以及分布式组件等。
组件架构最引人注目的优点之一是快速应用程序开发。使用组件的种种优点直接来源于可以动态地将
它们插入或卸出应用程序。
程序员可以利用ActiveX空间以加速应用程序及Web页面的开发。
分布式组件是由分布在网络上的多个部分组成的应用程序。
建立组件的需求:第一,组件必须动态连接。第二,它们必须隐藏(或封装)其内部实现细节。
COM组件是以Win32动态链接库(DLLs)或可执行文件(EXEs)的形式发布的可执行代码组成的。
实际上COM是使用了DLL来给组件提供动态链接的能力。
COM当然并不仅仅是一套API--它也确实需要一些具体的实现。COM具有一个被称作是COM库的API.
它提供的是对所有客户及组件都非常有用的组件管理服务。
在非Windows系统下开发COM风格的组件时,要实现此API中的大多数函数并不是一件太难的事。
COM库可以保证对所有组件大多数重要的操作都可以按相同的方式完成。
COM最值得称道的地方也许就是我们可以将其作为一种编写程序的方法。例如,可以在任何操作系统上
使用任何编程语言按COM风格进行编程。
同结构化编程及面向对象的编程方法一样,COM也是一种组织软件的方法。好的软件设计概念在COM
规范中得到了充分的体现。
第二章 接口
接口提供了两个不同对象间的一种链接。
计算机程序是通过一组函数而连接起来的。这组函数实际上就定义了程序中不同部分的接口。
COM中的接口也涉及到一组由组件实现并提供给客户使用的函数。对于COM来说,接口是一个包含一
个函数指针数组的内存结构。每一个数组元素包含的是一个由组件所实现的函数的地址。对于COM
而言,接口就是此内存结构。
在C++中,可以使用抽象基类来实现COM接口。由于一个COM组件可以支持任意数目的接口,因此对
于此类组件,我们将用抽象基类的多重继承来实现之。
接口的作用:在COM中接口就是一切。对于客户来说,一个组件就是一个接口集。客户只能通过接口才
能通COM组件打交道。
由于COM是与语言无关的,对于什么是接口,它有一个二进制的标准。也就是说,表示一个接口的内存
块必须具有一定的结构。幸运的是,当使用纯抽象基类时,许多C++编译器将可以生成具有这种
结构 的内存块。
在本书中我们并不将一个接口定义成一个类,而是使用了Microsoft Win32软件开发工具(SDK)中
OBJBASE.H头文件中的定义:
#define interface struct
在上述定义中使用struct的原因在于struct的成员将自动具有公有的属性,因此不需要另外在定义中
加上public关键字。
接口是由没有实现细节的纯虚基类实现的。
当客户和组件在同一源文件中时,并没有必要将它们分开。但是当客户和组件是在动态链接的情况下,
此种隔离则是必需的。在没有源代码的情况下更是如此。
类并非组件。接口并非总是继承的。一个系统是一系列组件的集合;每个组件提供了一个接口集;
而每一个接口则包含一系列函数。
第三章 QueryInterface函数
本章将讨论客户如何向组件询问关于它所支持的接口,组件如何回答,以及这种请求应答方式的结果。
另外我们还将看到这种使客户请求接口的方式如何提供了一个可以无缝地处理组件版本变化的
强壮系统。
客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成的。这个
接口就是IUnknown.
interface IUnknown
{
virtual HRESULT _stdcall QueryInterface(const IID& iid,void ** ppv)=0;
virtual ULONG _stdcall AddRef()=0;
virtual ULONG _stdcall Release()=0;
};
在IUnknown中定义了一个名为QueryInterface的函数。客户可以调用QueryInterface来决定组件
是否支持某个特定的接口。另外两个函数AddRef,Release用来控制接口的生命期。
所有的COM接口都继承了IUnknown。每个接口的vtbl中的前三个函数都是QueryInterface,AddRef和
Release.
如何获取IUnknown指针如何获取?
用CreateInstance这个函数,它可以建立一个组件并返回一个指针。
IUnkown *CreateInstance();
关于QueryInterface:
客户可以通过此函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface将返回
一个指向此接口的指针,否则返回值将是一个错误代码。然后客户可以接着查询其他接口或将
组件卸载。
QueryInterface带有两个参数:
HRESULT _stdcall QueryInterface(const IID& iid, void **ppv);
第一个参数也标识客户所需的接口。此参数是一个“接口标识符”(IID)结构。可以将其当成是
一个标识所需接口的常量。
第二个参数是QueryInterface存放所请求接口指针的地址。
QueryInterface返回的是一个HRESULT值。此值实际上并不像其名称所表示的那样是标识某个
结果的句柄。相反它是一个具有特定结构的32位值。QueryInterface可以返回S_OK或
E_NOINTERFACE.客户不应将QueryInterface的返回值直接同这两个值进行比较,而应
使用SUCCEEDED宏或FAILED宏。
QueryInterface的实现
根据某个给定的IID返回指向相应接口的指针。若组件支持客户指定的接口,那么应返回S_OK
以及相应的指针。若不支持,返回值应是E_NOINTERFACE并将相应的指针返回值置成
NULL.