上一部分内容介绍了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的实现决定的。