COM技术纵横谈(3)
这里有一个概念需要说明一下:组件并不是类,上面我们用一个类就实现了两组接口,同样我们也可以用它来实现更多接口。组件本身其实只是一个接口集及其实现的集合。一个组件可能包含了多个接口,每一个接口都有各自的实现。同时,接口并非总是继承的,COM规范没有要求实现某个接口的类必须从那个接口继承。这是因为客户并不了解COM组件的继承关系。对接口的继承只不过是一种实现细节而已。
下面将介绍QueryInterface函数。这个函数被用来查询其他接口。客户于组件之间的通讯是通过接口完成的。哪怕是客户查询其他一个组件时,也需要通过一个接口(换而言之,如果一个组件不支持这个接口,那他一定不是一个COM组件)这个接口的名字叫IUnknown,它有三个函数,如下所示:
interface IUnknown
{
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) = 0;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
};
COM组件的所有接口都继承了IUnknown,这样一来,每一个接口的前三个函数都是QueryInterface,这就是的所有的COM接口都可以被当成是IUnknown来处理。客户只要通过一个CoCreateInstance函数就可以创建该组件的实例并且获取其IUnknown*。
HRESULT __stdcall CoCreateInstance(
const CLSID& clsid,
IUnknown* pIUnknownOuter,
DWORD dwClsContext,
const IID& iid,
void** ppv
);
下面的CODE演示创建一个组件:
extern "C" const GUID CLSID_COM1 = (
0x32bb8230, 0xb41b1 0x11cf, 0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82 );
extern "C" const GUID IID_IX = (
0x32bb8230, 0xb591c 0x11ff, 0xc1, 0xb0, 0xc7, 0xf8, 0x21, 0x35, 0x1c, 0x2f );
CoInitialize();
Ia* pIx = NULL;
HRESULT hr = ::CoCreateInstance(
CLSID_COM1,
NULL,
CLSCTX_INPROC_SERVER,
IID_IX,
(void**) &pIx
);
if (SUCCEEDED(hr))
{
pIx -> Fx();
pIx -> Release();
}
extern "C" const GUID其实是所谓的"全局唯一标示符"(Globally Unique Identifier)。我们规定用它来表示不同的接口。换而言之,如果你发现有两个GUID完全相同,你完全有理由相信他们标示的是同一个接口。(有专门的算法来产生该结构,确保它在时间和空间上都是唯一的。) 接下来的CoInitialize函数初始化COM库。这一步是非常重要的,如果没有初始化,以后进行的操作都将失败。