上一部分内容介绍了COM最核心的概念接口,当然那不是真正意义上的COM接口,真正的COM接口都是从IUnknown接口继承的,并且其vtbl指针的前三个函数必须是:QueryInterface()、AddRef()、Release()。
1、IUnknown接口
首先一切接口都要继承自IUnknown接口,其在Unknwn.h中的定义摘录如下:
interface Iunknown{
virtualHRESULT _stdcall QueryInterface(const IID& iid,void** pv);
virtualULONG _stdcall AddRef(){return 0;}
virtualULONG _stdcall Release(){return 0;}
}
第一个函数实现了接口间的查询也就是切换工作,第二个函数与第三个函数作用相反,一个增加组件对象的引用计数一个减少组件对象的引用计数。
创建一个Iunknow接口的方法如下:
Iunknown* CreateInstace();
2、QueryInterface
Returns a pointer to a specifiedinterface on an object to which a client currently holds an interface pointer.
客户可以通过此函数来查询某个组件是否支持某个特定的接口,如果支持则返回该接口的指针。函数原型如下:
HRESULT QueryInterface(
REFIID iid, //Identifier of the requestedinterface
void ** ppvObject //Addressof output variable that receives the
//interface pointerrequested in iid
);
参数
iid
[in]Identifier of the interface being requested.
输入参数,所请求的接口的ID,IID=interface ID
ppvObject
[out]Address of pointer variable that receives the interface pointer requested inriid. Upon successful return, *ppvObject contains the requested interfacepointer to the object. If the object does not support the interface specifiediniid, *ppvObjectis set to NULL.
输出参数:所请求接口的指针地址
使用举例:
voidfoo(IUnknown* pI)
{
//所请求的接口
IX* pIX=NULL;
HRESULThr=pIX->QueryInterface(IID_IX,(void**)&pIX);
//检查返回值
if(SUCCEEDED(hr)){
pIX->Fx();
}
}
可以卡到在使用QI前我们要先声明一个所请求的接口指针并将其赋值为NULL。另外检查时候成功QI时一定要使用SUCCEEDED宏和FAILED宏,而不用直接hr==true这种方式。原因在于,首先这里的HRESULT有S_OK NOERROR S_FALSE E_NEXPECTED等多个值,此外S_FALSE被定义成1,S_OK被定义成0,这和C++中的编译方法恰好是相反的。故HRESULT的返回值判断一定要采用上述两个宏。
下面我们给出一个完成的例子来更好的理解QI
#include <iostream> #include <ObjBase.h> using namespace std; void trace(const char* pMsg) { cout<<pMsg<<endl; } // 接口 [3/13/2014 pan] interface IX:IUnknown { virtual void _stdcall Fx()=0; }; interface IY:IUnknown { virtual void _stdcall Fy()=0; }; interface IZ:IUnknown { virtual void _stdcall Fz()=0; }; //GUID extern const IID IID_IX; extern const IID IID_IY; extern const IID IID_IZ; //组件 class CA:public IX,public IY { //IUnkonw 实现部分 virtual HRESULT _stdcall QueryInterface(const IID& iid,void** pv); virtual ULONG _stdcall AddRef(){return 0;} virtual ULONG _stdcall Release(){return 0;} //IX 接口实现 virtual void _stdcall Fx(){cout<<"Fx"<<endl;} //IY 接口实现 virtual void _stdcall Fy(){cout<<"Fy"<<endl;} }; //QI的实现细节 HRESULT _stdcall CA::QueryInterface(const IID& iid,void** ppv) { if(iid==IID_IUnknown){ trace("接口查询,返回IUnkonw接口"); *ppv=static_cast<IX*>(this); } else if(iid==IID_IX){ trace("接口查询,返回IX接口"); *ppv=static_cast<IX*>(this); } else if(iid==IID_IY){ trace("接口查询,返回IY接口"); *ppv=static_cast<IY*>(this); } else{ trace("接口查询,不支持该接口"); *ppv=NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } ////////////////////////////////////////////////////////////////////////// //主函数 IUnknown* CreateInstance() { IUnknown* pI=static_cast<IX*>(new CA); pI->AddRef(); return pI; } ////////////////////////////////////////////////////////////////////////// //IID static const IID IID_IX={0x32bb8320,0xb41b,0x11cf, {0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}}; static const IID IID_IY={0x32bb8321,0xb41b,0x11cf, {0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}}; static const IID IID_IZ={0x32bb8322,0xb41b,0x11cf, {0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}}; ////////////////////////////////////////////////////////////////////////// //客户 int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr; trace("Client: 得到IUnknown接口"); IUnknown* pIUnknown=CreateInstance(); trace("Client: 得到IX接口"); IX* pIX=NULL; hr=pIUnknown->QueryInterface(IID_IX,(void**)&pIX); if(SUCCEEDED(hr)){ trace("Client: 成功得到IX接口"); pIX->Fx(); } trace("Client: 得到IY接口"); IY* pIY=NULL; hr=pIUnknown->QueryInterface(IID_IY,(void**)&pIY); if(SUCCEEDED(hr)){ trace("Client: 成功得到IY接口"); pIY->Fy(); } trace("Client: 请求不支持的接口"); IZ* pIZ=NULL; hr=pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ); if(SUCCEEDED(hr)){ trace("Clietn: Succeed in getting interface IZ"); pIZ->Fz(); } else{ trace("Client: Could not get interface IZ"); } trace("Clietn: Get interface IY from IX"); IY* pIYfromIX=NULL; hr=pIX->QueryInterface(IID_IY,(void**)&pIYfromIX); if(SUCCEEDED(hr)){ trace("Client: Succeed getting IY"); pIYfromIX->Fy(); } trace("Client: get interface IUnknow from IY"); IUnknown* pIUnknowfromIY=NULL; hr=pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknowfromIY); if(SUCCEEDED(hr)){ } return 0; }
在QI的细节中我们可以观察得到这个结论——QueryInterface定义了组件。
从上面的具体实现细节可以看到,组件所支持的接口集就是QueryInterface能够为之返回的接口指针的那些接口。(原因在于,除了QI之外我们没有其他方法去获得组件接口的指针,也就无法使用QI不支持的接口所对应的函数方法)这一定是由QueryInterface的实现细节所决定的,而不是有实现组件的C++类(上面例子中的class CA)所决定的。由于客户并不知道QueryInterface的实现,因此,它将无法知道一个组件所支持的所有接口。客户了解组件所支持的接口的唯一方法就是进行查询。这一点同C++有着很大的不同(C++可以同头文件来查看类的所有成员)。
结论:一个组件仅仅是由QueryInterface的实现决定的。