COM规范允许使用多接口,QueryInterface()成员函数可以用来查询组件是否支持某个特定的接口。如果支持,QueryInterface()将返回此接口的指针。其第一个参数为一个IID结构,指出了客户所要查询的接口,查询到的接口指针将存放在ppv所指向的变量中。函数的成功执行与否将返回S_OK或E_NOINTERFACE。但是,在使用时不能简单的将QueryInterface()返回值与其进行比较,而应使用SUCCEEDED或FAILED宏。例如:
IUnknow* pI = CreateInstance(); IX* pIX = NULL; HRESULT hResult = pI->QueryInterface(IID_IX, (void**)&pIX); if (SUCCEEDED(hResult)) pIX->Func1(); |
由于QueryInterface()过于灵活,为避免由此引发的冲突在COM规范中定义了QueryInterface()所有实现都必须遵循的一些规则:
1) 过同一对象各个接口指针所查询得到的IUnknown接口指针必须是指向同一个IUnknown接口的。即,IUnknow接口的唯一性。
2) 如果某接口曾经被成功查询过,那么此后任何时间对该接口的查询也必定会成功。即,接口与查询时间的无关性。
3) 对于已经获取到的接口仍可对其进行再次查询,并且必定会成功。即,接口的自反性。
4) 客户能够从任何接口查询到另外一个接口,而且能够返回到起始接口。即,接口的对称性。
5) 如果能够从某接口获取到某特定接口,那么从任意接口都可以得到此接口。即,接口的传递性。
IUnknown接口的另两个成员函数AddRef()和Release()对对象的生存期进行了控制。每个COM对象都记录有一个引用计数,该引用计数表示了当前引用了此COM对象的有效指针的个数。AddRef()和Release()实现的即是这种引用计数的内存管理技术:引用计数初始为0,客户每得到一个指向此对象的接口指针即通过AddRef()将引用计数加1;在每用完此接口指针后,调用Release()函数将引用计数减1。如果引用计数减到0,则从内存卸载掉此COM对象。关于引用计数的使用,在COM规范中也设置了以下几条简单的规则:
1) 任何能够返回接口指针的函数(如QreryInterface()、CreateInstance()等)在返回接口指针之前,必须用相应的指针调用AddRef()函数。
2) 在使用完任何一个接口后,应及时调用该接口的Release()函数。
3) 在进行接口指针赋值操作后,应调用AddRef()函数。
COM组件的创建可以通过CoCreateInstance()函数来完成,函数原型为:
HRESULT __stdcall CoCreateInstace( const CLSID& clsid, IUnknown* pIUnknownOuter, DWORD dwClsContext, const IID& iid, void** ppv ); |
函数参数clsid是要创建组件的CLSID,pIUnknownOuter用于聚合组件,如果不使用可以设置为NULL。参数dwClsContext则限定了所创建组件的执行上下文。最后两个参数iid和ppv则分别为要使用接口的IID和返回得到的接口指针。在使用时只需将CLSID、IID等作为参数传入即可创建相应的组件并从输出参数ppv得到所请求接口的指针。如果函数是直接创建组件的,那么在函数返回时组件将创建完毕,这样客户将无法对组件的创建过程进行任何干预,灵活性太差。因此,CoCreateInstance()在函数内部实现中通过调用CoGetClassObject()函数先创建一种专门用来创建组件的组件来解决此问题。这种用途的组件被称为类厂(class factory)。
|