关于IUnknown
所有的com接口都继承了IUnknown,每个接口的前三个函数是QueryInterface、AddRef、Release。这使得所有的接口都可以当成IUnknown来使用。因此组件的任何一个接口都可以被客户用来获取他所支持的其他接口。
IUnknown指针的获取
可以通过一个CreateInstance函数来返回一个IUnknown指针而不必使用new操作符
关于QueryInterface
通过QueryInterface函数来查询某个组件是否支持某个特定的接口,若支持将返回一个指向这个接口的指针,否则返回一个错误代码。
HRESULT __stdcallQueryInterface(const IID& riid, void** ppObject);
第一个参数表示客户所需要的接口,是一“接口标识符”结构。
第二个参数是QueryInterface存放错请求接口指针的地址。
返回一个HRESULT值,是一个32位的特定结构值。
QueryInterface的使用
假定用户有一个指向IUknown的指针pI,则可如下:
voidfoo(IUnknown * pI)
{
//Define a pointer for the interface
IX*pIX = NULL;
//Ask for interface3 IX
HRESULThr = pI->QueryInterface(IID_IX, (void**)&pIX);
//Check return value
if(SUCCEEDED(hr))
pIX->Fx();
}
查询了pI是否支持IID_IX所标识的接口。首先将pIX初始化为NULL 是一个号的编程习惯
QueryInterface的实现
QueryInterface根据某个给定的IID返回相应的接口。支持做返回S_OK和相应的指针,不支持返回E_NOINTNTERFACE并将相应的指针返回值赋值为NULL。以CA组件为例:
interface IX : IUnknown{};
interface IY : IUnknown{};
class CA : public IX, publicIY{};
注:IUnknown并不是虚拟继承,IX和IY并不是按虚拟继承方式继承IUnknown,这是由于会导致与Com不兼容的vtbl。
HRESULT __stdcallCA::QueryInterface(REFIID riid, void**ppvObject)
{
if(riid == IID_IUnknown)
*ppvObject= static_cast<IX*>(this);
else if(riid ==IID_IX)
*ppvObject= static_cast<IX*>(this);
else if(riid ==IID_IY)
*ppvObject= static_cast<IY*>(this);
else
{
*ppvObject= NULL;
return E_NOINTERFACE;
}
static_cast<IUnknown*>(*ppvObject)->AddRef();
return S_OK;
}
关于类型转换
IX指针所得到的地址与将其转换成一个IY指针所得到的地址是不一样的:
static_cast<IX*>(this) != static_cast<IY*>(this)
static_cast<void*>(this) != static_cast<IY*>(this)
一般在讲this指针赋值给某个void指针时,应该先将其转换成合适的类型,如下
*ppvObject = static_cast<IUnknown*>(this);
这时this指针转换成IUnknown*时不明确的,因为IX和IY都是有IUnknown继承来的,所以应该是*ppvObject = static_cast<IUnknown*>(static_cast<IX*>(this));
C++中的多重继承内存结构如下:(以CA为例)
规则:
同一IUnknown
组件的实例只有一个IUnknown接口,当查询实例的IUnknown接口时,不论通过哪个接口得到的将是同一指针。
BOOL SameComponents(IX* pIX, IY* pIY)
{
IUnknown*pI1 = NULL;
IUnknown*pI2 = NULL;
//get IUnknown from IX
pIX->QueryInterface(IID_IUnknown,(void**)&pI1);
//Get IUnknown from IY
pIY->QueryInterface(IID_IUnknown,(void**)&pI2);
//are same??
return (pI1 == pI2);
}
客户可以获取曾经得到的接口
如果组件所支持的接口会不时的发生变化将会导致客户编写代码极为困难,将无法通过编程的方法来决定一个组件到底具有什么样的功能。
可以再次获取已经拥有的接口
如果客户拥有一个IX接口,则可以通过IX获取IX的接口指针,并且一定成功。
客户可以从任何接口返回到起始接口
若客户拥有一个IX接口指针并成功的使用它来获取一天IY接口,那么将可以使用IY来查询一个IX接口。
若能从某接口获取某特定接口,则从任意接口都将能够获取此接口
例如可以通过从接口IX获取IY,通过IY接口可以获取IZ接口,那么从IX也讲可以获取IZ接口。
一个组件实际就是有QueryInterface定义了,组件所支持的接口集就是QueryInterface能够为之返回接口指针的那些接口。客户了解组件支持的接口唯一方法就是查询。