QueryInterface 接口查询
IUnknown:
所有的COM接口均需要继承IUnknown接口。因此,若某个用户拥有一个IUnknown接口指针,它并不需要知道它所拥有的接口指针到底是什么类型的,而只需要通过此接口就可以用来查询其他接口就行了。
由于所有的COM接口都继承了IUnknown,每个接口的vbtl的前三项都是QueryInterface,AddRef,Release(如图1所示)。这使得所有的COM接口都被当成是IUnknown接口来处理。由于所有的接口指针也将是IUnknown指针,客户并不需要单独维护一个代表组件的指针,它所关心的将仅仅是接口的指针。
HRESULT__stdcall QueryInterface(const IID& iid,void** ppv);
iid:接口标示符结构。
ppv:用来存放请求的接口指针的地址
ex:IX *pIX=NULL;
HRESULT hr=pI->QueryInterface(IID_IX,(void**)&pIX);
If(SUCCEEDED(hr))
{
pIX->Fx();
}
Else
{
pIX=NULL;
}
在调用QueryInterface之前,我们将pIX初始化为NULL,这是一种比较好的编程习惯,但由于QueryInterface是由程序员而不是由系统实现的,因此在查询失败的时候,将此指针置为NULL.
QueryInterface 的实现
Interface IX: IUnknown{};
Interface IY: IUnknown{};
Class CA:public IX,public IY{};
类CA及其接口的继承关系如图(2):
QueryInterface(const IID& iid,void**ppv);
//注意由QueryInterface 返回的IUnknown指针一定要和
//CreateInstance 返回的IUnknown指针一样。
//这里均采用了static_cast<IX* >(this);
具体实现:
HRESULT __stdcall CA::QueryInterface(constIID& iid,void** ppv)
{
If(iid==IID_IUnknown)
{
//TheClient wants the IUnknown interface
*ppv=static_cast<IX*>(this);
}
Elseif(iid==IID_IX)
{
*ppv=static_cast<IX* >(this);
}
Elseif(iid==IID_IY)
{
*ppv=static_cast<IY *>(this);
}
Else
{
//we don’t support the interface theclient
//wants. Be sure to set the resulting pointer
// to NULL.
*ppv=NULL;
Return E_NOINTERFACE;
}
Static_cast<IUnknown*>(*ppv)->AddRef();
Return S_OK;
}
在客户所查询的接口不被支持的时候,QueryInterface将*ppv置为NULL。这是COM规范所要求的,而且从其本身来讲也是一种很好的做法。
类型转换:
这里将this指针转化成为一个IX指针所得到的地址与将其转化成为一个IY指针所得到的地址是不同的,例如:
Static_cast<IX*>(this)!=static_cast<IY* >(this);
Static_cast<void*>(this)!=static_cast<IY*>(this);
换用某些程序员更加常用的旧方式的类型转化,即使:
(IX*)this!=(IY*)this;
(void*)this!=(IY*)this;
将this指针进行类型转换将会导致其值的改变,这一点主要是由于C++的实现方式。
一般在将this指针赋值给某个void指针时候,应先将其转化为合适的类型。一个有趣的例子是返回IUnknown指针的情形:
*ppv=static_cast<IUnknown* >(this);// Ambiguous. 含糊不清的
但是将this指针转化成IUnknown* 是不明确的。这是由于IX和IY都是从IUnknown继承得到的。因此在这种情况向下,返回值应该是static_cast<IUnknown *>(static_cast<IX*>(this))或者是
Static_cast<IUnknown*>(static_cast<IY*>(this)).使用哪一个不重要,因为他们使用的是同一个实现。但是在代码中要保持一致,这是因为这两个指针是不一样的,并且COM要求对IUnknown接口返回相同的指针。
多生继承及类型转换
通常将一种类型的指针转化成为另外一种类型,并不会改变它的值,但是为了支持多重继承,在某些情况下,C++必须改变类指针的值。许多C++程序员并不清楚多重继承的此种负面效果。例如假定有一个类CA定义如下:
Class CA:public IX,public IY{};
由于CA同时继承了IX和IY,因此在可以使用IX或者IY指针的地方均可以使用指向CA的指针。例如可以将指向CA的指针传给接受IX和IY指针的函数,这样此函数仍将能够正常工作。例如:
Void foo(IX* pIX);
Void bar(IY* pIY);
Int main()
{
CA*pA=new CA();
Foo(pA);
Bar(pA);
DeletepA;
}
Foo需要一个指向合法的IX的虚拟函数表指针,而bar则需要一个指向IY虚拟函数表的指针。当然IX和IY的虚拟函数表中的内容是不一样的。因此在将一个IX vtbl传给bar时候,此函数将不能正常工作。因此编译器将同一指针传给foo和bar是不可能的,他必须对CA指针进行修改一遍它指向一个合适的vtbl指针。图三显示了类CA对象的内存结构:
从图3-3来看,CA的this指针指向IX的虚拟函数表。因此可以再不改变CA的this指针的值的情况下用它来代替IX指针。但是从图中可以很明显看出,类CA的this指针并没有指向IY的虚拟函数表。因此在将指向类CA的指针传给一个接受IY指针的函数之前,其值必须修改。为了完成这种修改,编译器将把IY虚拟函数表指针的偏移量(IY)加到CA的this指针上,一次编译器把下面的代码:
IY*pC=PA;
转化成为下面类似的代码:
IY*pC=(char*)pA+IY;
一个完整的例子:
void trace(const char* msg)
{
cout<<msg<<endl;
}
interface IX:IUnknown
{
virtual void __stdcall Fx()=0;
};
interface IY:IUnknown
{
virtual void __stdcall Fy()=0;
};
interface IZ:IUnknown
{
virtual void __stdcall Fz()=0;
};
extern const IID IID_IX;
extern const IID IID_IY;
extern const IID IID_IZ;
class CA:public IX,public IY
{
//IUnknown implementation
virtual HRESULT __stdcall QueryInterface
(const IID& iid,void **ppv);
virtual ULONG __stdcall AddRef(){return 0;}
virtual ULONG __stdcall Release(){return 0;}
//Interface IX implementation
virtual void __stdcall Fx()
{
cout<<"Fx"<<endl;
}
virtual void __stdcall Fy()
{
cout<<"Fy"<<endl;
}
};
HRESULT __stdcallCA::QueryInterface(const IID &iid, void **ppv)
{
if(iid==IID_IUnknown)
{
// trace("QueryInterface:Returnpointer to Iunknown.");
*ppv=static_cast<IX* >(this);
}
else if(iid==IID_IX)
{
// trace("QueryInterface:ReturnPointer to IX");
*ppv=static_cast<IX* >(this);
}
else if(iid==IID_IY)
{
// trace("QueryInterface:ReturnPointer to IY.");
*ppv=static_cast<IY* >(this);
}
else
{
// trace("QueryInterface:Interfacenot support.");
*ppv=NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
IUnknown *CreateInstance()
{
IUnknown *pI=static_cast<IX*>(new CA);
//注意这里的static_cast<IX* >(new CA);
//和static_cast<IY* >(new CA)是不一样的
//所以说IUnknown *pI的上下文环境要一致
pI->AddRef();
return pI;
}
static const IID IID_IX=
{0x32bb8320,0xb41b,0x11fc,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
static const IID IID_IY=
{0x32bb8f21,0xb41b,0x11cf,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
static const IID IID_IZ=
{0x32bb8322,0xb1b,0x11cf,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
bool SameComponents(IX* pIX,IY* pIY)
{
IUnknown *pI1=NULL;
IUnknown *pI2=NULL;
//Get IUnknown pointer from pIX
pIX->QueryInterface(IID_IUnknown,(void**)&pI1);
pIY->QueryInterface(IID_IUnknown,(void**)&pI2);
return PI1==PI2;
}
void f(IX* pIX)
{
IX *pIX2=NULL;
//Query IX for IX
HRESULT hr=pIX->QueryInterface(IID_IX,
(void**)&pIX2);
assert(SUCCEEDED(hr));
//query must succeed.
}
void fi(IX* pIX)
{
HRESULT hr;
IX* pIX2=NULL;
IY* pIY=NULL;
hr=pIX->QueryInterface(IID_IY,(void**)&pIY);
if(SUCCEEDED(hr))
{
hr=pIY->QueryInterface(IID_IX,(void**)&pIX2);
//QueryInterface must be succeeded.
assert(SUCCEEDED(hr));
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr;
trace("Client:Get an IUnknown pointer");
IUnknown *pIUnknown=CreateInstance();
trace("Client:Get interface IX.");
IX* pIX=NULL;
hr=pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
if(SUCCEEDED(hr))
{
trace("Client:Succeed getting IX.");
pIX->Fx();
}
trace("Client:Get interface IY");
IY* pIY=NULL;
hr=pIUnknown->QueryInterface(IID_IY,(void**)&pIY);
if(SUCCEEDED(hr))
{
trace("Client:Succeeded getting IY.");
pIY->Fy();
}
trace("Client:Ask For an unsupported interface");
IZ *pIZ=NULL;
hr=pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);
if(SUCCEEDED(hr))
{
trace("Client:Succeeded in getting interface IZ");
pIZ->Fz();
}
else
{
trace("Could notget interface IZ.");
}
trace("Client:Get interface IY from interface IX.");
IY* pIYfromIX=NULL;
hr=pIX->QueryInterface(IID_IY,(void**)&pIYfromIX);
if(SUCCEEDED(hr))
{
trace("Client:Succeeded getting IY.");
pIYfromIX->Fy();
}
trace("Client:Getinterface IX from interface IY");
IX* pIXfromIY=NULL;
hr=pIY->QueryInterface(IID_IX,(void**)&pIXfromIY);
if(SUCCEEDED(hr))
{
trace("Client:Succeeded getting IX.");
pIXfromIY->Fx();
}
trace("Client:Get interface IUnknown from IY");
IUnknown *pIUnknownFromIY=NULL;
hr=pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownFromIY);
if(SUCCEEDED(hr))
{
cout<<"Arethe IUnknown pointers equal?"<<endl;
if(pIUnknownFromIY==pIUnknown)
{
cout<<"Yes,pIUnknownFromIY==pIUnknown."<<endl;
}
else
{
cout<<"No,pIUnknownFromIY!=pIUnknown."<<endl;
}
}
delete pIUnknown;
return 0;
}