QueryInterface 接上篇
一.COM接口的开始IUnknown
com起于接口,又归于接口.
com之所以是com,是因为其继承了一个名为IUnknown接口.
IUnknown接口是一个非常有趣的名字.我们第一次看到这个接口,都会非常好奇,名字的由来开发内部肯定是有故事的,或者他们想不出啥好名字也是有可能的.
下面我们来看一下IUnknown接口定义了哪些成员,这篇只讲QueryInterface
interface IUnknown { virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) =0;virtual ULONG __stdcall AddRef() =0 ; virtual ULONG __stdcall Release() =0 ;};
二.QueryInterface的实现与使用
1.作用
看命名就可以猜这个方法是用来查询组件是否支持某个特定接口。若支持泽返回指向此接口的指针。
2.参数
QueryInterface方法有两个参数,
第一个为接口标识符,简称IID,全名(Interface Identifier),现在可以只理解为一个常量
第二个参数即为返回的接口指针
3.使用方法
我们先来看示例代码
定义一个继承自IUnknown接口的IX
interface IX : IUnknown { virtual void __stdcall Fx() = 0 ; } ;
若CA实现了该IX接口,则使用如下
static const IID IID_IX = {0x32bb8320, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; void foo(IUnknown* pI) { IX* pIX=NULL; HRESULT hr=pI->QueryInterface(IID_IX,(void**)&pIX); if(SUCCEEDED(hr) { pIX->Fx(); } }
3.1 HRESULT
HRESULT是一个查询结果状态集合,并非true或false可以表示的,所以用SUCCEEDED方法来判断查询结果是否成功,失败的话,接口指针为NULL.
4.QueryInterface的实现
CA继承了IX,由于IX继承了IUnknown接口,所以CA也必须实现IUnknown接口成员,我们重新来看一下QueryInterface方法的参数
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) =0;
该方法会根据传输不同iid,而返回相对应的接口指针,为了演示,我们再定义一个接口IY,CA实现IX和IY,
如下实现
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IX) { *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IY) { *ppv = static_cast<IY*>(this) ; } else { *ppv = NULL ; return E_NOINTERFACE; } return S_OK ; }
是不是很简单?,获取到以后用static_cast来转换类型。IID_IUnknown,S_OK,E_NOINTERFACE等都是内置的状态码
5.简单创建IUnknown
IUnknown* CreateInstance() { IUnknown* pI = static_cast<IX*>(new CA) ; pI->AddRef() ; return pI ; }
6.调用接口
int main() { HRESULT hr ; IUnknown* pIUnknown = CreateInstance() ; IX* pIX = NULL ; hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ; if (SUCCEEDED(hr)) { pIX->Fx() ; // Use interface IX. } IY* pIY = NULL ; hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ; if (SUCCEEDED(hr)) { pIY->Fy() ; // Use interface IY. } // Delete the component. delete pIUnknown ; return 0 ; }
三.QueryInterface实现准则
1.QueryInterface返回的总是同一IUnknown指针,贴图了
2.若用户曾获取过某个接口,那么它将总能获取此接口
3.用户可获取再次获取过的接口
4.用户可以返回到起始接口
3和4都可以理解为接口之间的切换。
5.接口可获取-获取到接口的接口
通过A接口得到B接口,B接口可以获取C接口,那么A接口可以直接获取C接口.
以上就如数学公式一般,犹如com编程的不变真理.