这一章主要介绍QueryInterface函数,我觉得其中最重要的是了解程序的整体结构.即包含哪几个文件,每个文件有哪些类,每个类是做什么的.书中有个完整的例子.代码如下:
#include < objbase.h >
// To Compile use:cl IUnknown.cpp UUID.lib
void trace( const char * msg)
... {
cout<<msg<<endl;
}
// Interfaces
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;
// Component
class CA: public IX, public IY
... {
virtual HRESULT _stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG _stdcall AddRef()...{return 0;}
virtual ULONG _stdcall Release()...{return 0;}
//interfac IX implementation
virtual void _stdcall Fx()...{cout<<"Fx()"<<endl;}
//interfac IY implementation
virtual void _stdcall Fy()...{cout<<"Fy()"<<endl;}
} ;
HRESULT _stdcall CA::QueryInterface( const IID & iid, void ** ppv)
... {
if (iid == IID_IUnknown)
...{
trace("QuerInterface:Return pointer to IUnknown");
*ppv = static_cast<IX*>(this);
}
else if(iid == IID_IX)
...{
trace("QueryInterface: Return pointer to IX.");
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY)
...{
trace("QueryInterface: Return pointer to IY.");
*ppv = static_cast<IY*>(this);
}
else
...{
trace("QueryInterface: Interface not supported.");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
// Creation function
IUnknown * CreateInstance()
... {
IUnknown* pI = static_cast<IX*>(new CA);
pI->AddRef();
return pI;
}
// IIDs
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 main()
... {
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: Succeeded 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 unsuppoered interface.");
IZ *pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);
if (SUCCEEDED(hr))
...{
trace("Client: Succeeded getting IZ.");
pIZ->Fz();
}
else
...{
trace("Client: Could not get 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: Get interface IUnknown from interface IY.");
IUnknown* pIUnknownfromIY = NULL;
hr = pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownfromIY);
if (SUCCEEDED(hr))
...{
cout<<"Are the IUnknown pointers equal? ";
if (pIUnknownfromIY == pIUnknown)
...{
cout<<"Yes,pIunknownfromIY == pIunknown."<<endl;
}
else
...{
cout<<"No,pIUnknownfromIY != pIunknown."<<endl;
}
}
//Delete the component.
delete pIUnknown;
return 0;
}
/**/ /*输出结果
Client: Get an IUnknown pointer.
Client: Get Interface IX.
QueryInterface: Return pointer to IX.
Client: Succeeded getting IX.
Fx()
Client: Get Interface IY.
QueryInterface: Return pointer to IY.
Client: Succeeded getting IY.
Fy()
Client: Ask for an unsuppoered interface.
QueryInterface: Interface not supported.
Client: Could not get interface IZ.
Client: Get interface IY from interface IX.
QueryInterface: Return pointer to IY.
Client: Succeeded getting IY.
Fy()
Client: Get interface IUnknown from interface IY.
QuerInterface:Return pointer to IUnknown
Are the IUnknown pointers equal? Yes,pIunknownfromIY == pIunknown.
Press any key to continue
*/
其实我们可以把这一个文件按其功能划分为更下的文件,这样对整体结构有个清晰的认识:
接口类 interface.h
// Interfaces
interface IX:IUnknown
... {
virtual void _stdcall Fx() = 0;
} ;
interface IY:IUnknown
... {
virtual void _stdcall Fy() = 0;
} ;
interface IZ:IUnknown
... {
virtual void _stdcall Fz() = 0;
} ;
// IIDs
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}
} ;
实现类 --头文件CB.h
#define CB_H
#include " interface.h "
#include < iostream.h >
// Component
class CB: public IX, public IY
... {
virtual HRESULT _stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG _stdcall AddRef()...{return 0;}
virtual ULONG _stdcall Release()...{return 0;}
//interfac IX implementation
virtual void _stdcall Fx()...{cout<<"Fx()"<<endl;}
//interfac IY implementation
virtual void _stdcall Fy()...{cout<<"Fy()"<<endl;}
} ;
extern void trace( const char * msg);
// Creation function
extern IUnknown * CreateInstance();
#endif
实现类--CB.cpp
HRESULT _stdcall CB::QueryInterface( const IID & iid, void ** ppv)
... {
if (iid == IID_IUnknown)
...{
trace("QuerInterface:Return pointer to IUnknown");
*ppv = static_cast<IX*>(this);
}
else if(iid == IID_IX)
...{
trace("QueryInterface: Return pointer to IX.");
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY)
...{
trace("QueryInterface: Return pointer to IY.");
*ppv = static_cast<IY*>(this);
}
else
...{
trace("QueryInterface: Interface not supported.");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
void trace( const char * msg)
... {
cout<<msg<<endl;
}
// Creation function
IUnknown * CreateInstance()
... {
IUnknown* pI = static_cast<IX*>(new CB);
pI->AddRef();
return pI;
}
注意将this指针转换为IUnknown*是不明确的.因为IX和IY都是从IUnknown继承得到的.因此在这种情况下,返回值应该是static_cast<IUnknown*>(static_cast<IX*>(this))或static_cast<IUnknown*>(static_cast<IY*>(this)),不过在上例中其实是无关紧要的,因为它们使用的是同一实现.
客户端代码--App.cpp
int main()
... {
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: Succeeded 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 unsuppoered interface.");
IZ *pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);
if (SUCCEEDED(hr))
...{
trace("Client: Succeeded getting IZ.");
pIZ->Fz();
}
else
...{
trace("Client: Could not get 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: Get interface IUnknown from interface IY.");
IUnknown* pIUnknownfromIY = NULL;
hr = pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownfromIY);
if (SUCCEEDED(hr))
...{
cout<<"Are the IUnknown pointers equal? ";
if (pIUnknownfromIY == pIUnknown)
...{
cout<<"Yes,pIunknownfromIY == pIunknown."<<endl;
}
else
...{
cout<<"No,pIUnknownfromIY != pIunknown."<<endl;
}
}
//Delete the component.
delete pIUnknown;
return 0;
}
可以看到,在接口类中,我们定义了三个接口,定义了三个IID.
在具体实现类中,实现了QueryInterface,AddRef,Release三个IUnknown接口,这是COM组件必须实现的接口,另外还实现了Fx和Fy接口.在实现类中实现QueryInterface是必要的,因为只有在实现类中,才知道这个实现类到底实现了哪些接口.
在客户端代码中,通过QueryInterface接口可以查询当前实现类有没有实现某个接口.注意QueryInterface的实现方式.对this指针的类型转换会给ppv不同的值.也就是
( void * ) this != (IY * ) this
比如如下定义中:
class CA: public IX, public IY
... {
...
} ;
void foo(IX * pIX);
void bar(IY * pIY);
int main()
... {
CA *pA = new CA;
foo(pA);
bar(pA);
delete pA;
return 0;
}
由于CA同时继承了IX和IY,因此可以使用IX或IY指针的地方均可以使用指向CA的指针.例如可以将指向CA的指针传给接收IX或IY指针的函数,这样此函数仍将能正常工作.foo需要一个指向合法的IX的虚拟函数表的指针,而bar则需要一个指向IY虚拟函数表的指针.这两个虚拟函数表的内容是不一样的.因此将一个IX vtbl传给bar时,此函数不能正常工作.因此编译器将同一指针传给foo和bar是不可能的,它必须对CA的指针进行修改以便它指向一个合适的vtbl指针.下图显示了类CA的内存结构.
由上图可以看出,CA的this指针指向IX的虚拟函数表.因此可以在不改变CA的this指针的值的情况下用它来代替IX指针.但也可以很明显的看出,CA的this指针并没有指向IY的虚拟函数表指针.因此将指向类CA的指针传给一个接收IY指针的函数之前,其值必须修改.为完成这种修改,编译器将把IY虚拟函数表指针的偏移量加到CA的this指针上.