您的位置:首页 > 产品设计 > UI/UE

COM技术内幕--QueryInterface函数

2016-12-15 11:19 495 查看
接口查询:

在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown.

头文件包含在Win32 SDK的unknwn.h头文件中。 引用如下:

interface IUnknown

{

virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv)=0;

virtual ULONG __stdcall AddRef()=0;

virtual ULONG __stdcall Release()=0;

}
1)关于IUnknown

所有的COM接口都继承了IUnknown,每个接口的vtbl中的前三个函数都是QueryInterface、AddRef和Release。如下图:

//////////////////////////////////////////////////////////////////////



2)IUnknown指针的获取

能过CreateInstance的函数,它可以建立一个组件返回一个IUnkown指针

IUnkown* CreateInstance();

在创建组件时,客户可以使用CreateInstance而不必再使用new操作符。
3)关于QueryInterface

IUnknown中包含一个名为QueryInterface的成员函数,客户可以通过 此函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface将返回一个指向此接口的指针;否则返回值将是一个错误代码。

virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv)=0;

返回S_OK或E_NOINTERFACE。客户不应将QueryInterface 的返回值直接同这两个值进行比较,而应使用SUCCEEDED宏或FAILED宏。
4)QueryInterface的使用

假定客户有一个指向IUnknown指针pI,为知道相应的组件是否支持某个特定的接口,可以调用QueryInterface,并传给它一个接口标识符。若QueryInterface成功返回,那么就可以使用它返回的指针了。代码如下:

void foo(IUnknown* pI)

{

// Define a pointer for the interface

IX* pIX = NULL;

// Ask for interface IX.

HRESULT hr = pI->QueryInterface(IDD.IX,(void**)&pIX);

// check return value.

if (SUCCEEDED(hr))

{

// Use Interface

pIX->Fx1();

}

}
5)QueryInterface的实现

interface IX:IUnknown {

/*....*/

}

interface IY:IUnknown {

/*....*/

}

class CA:public IX,public IY

{

/*....*/

}

类CA及其接口的继承关系如下:

//////////////////////////////////////////////////////////////////////



非虚拟继承:

注意IUnknown并不是虚拟基类。IX和IY并不能按虚拟方式继承IUnknown,这是由于会导致与COM不兼容的vtbl。若IX和IY按虚拟方式继承IUnknown,那么 IX和IY的vtbl中的头三个函数指向的将不是IUnknown的三个成员函数。

HRESULT __stdcall CA::QueryInterface(const IID& iid,void ** ppv)

{

if (iid==IID_IUnknown)

{

// The client wants the IUnknown interface.

*ppv = static_cast<IX*>(this);

}

else if (iid==IDD.IX)

{

// The client wants the IX interface.

*ppv = static_cast<IY*>(this);

}

else if (iid==IID.IY)

{

// The client wants the IY interface.

*ppv = static_cast<IY*>(this);

}

else

{

// We don't support the interface the client

// wants. Be sure to set the resulting pointer to NULL

*ppv = NULL;

return E_NOINTERFACE;

}

static_cast<IUnknown*>(*ppv)->AddRef();

return S_OK;

}
6)关于类型转换

将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

多重继承及类型转换

通常将一种类型的指针转换成另外一种类型并不会改变它的值。但为了支持多重继承,在某些情况下,c++必须改变类指针的值。许多c++程序员并不清楚多重继承的此种负面效果。

例如:

class CA:public IX,public IY{

/*...*/

}

void foo(IX* pIX);

void bar(IY* pIY);

int main(void)

{

CA* pA = new CA;

foo(pA);

bar(pA);

delete pA;

return 0;

}

foo需要一个指向合法的IX虚拟函数表的指针,而bar则需要一个指向IY虚拟函数表的指针。当然IX和IY的虚拟函数表中的内容是不一样的。因此在将一个IX vtbl传给bar时,此函数将不能正常工作。因此编译器将同一个指针传给foo和bar是不可能的,它必须对CA的指针进行修改以便它指向一个合适的vtbl指针。内存结构如下:

//////////////////////////////////////////////////////////////////////



7)一个完整的例子

#include <iostream>

#include <objbase.h>

using namespace std;

void trace1(const char* pMsg){

cout<<pMsg<<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

{

virtual HRESULT __stdcall QueryInterface(const IID&iid,void** ppv);

virtual ULONG __stdcall AddRef(){

return 0;

}

virtual ULONG __stdcall Fx(){

cout<<"Fx"<<endl;

}

virtual void __stdcall Fy(){

cout<<"Fy"<<endl;

}

}

HRESULT __stdcall CA::QueryInterface(const IID &iid,void** ppv)

{

if (iid==IID_IUnknown)

{

trace1("QueryInterface: Return pointer to IUnknown");

*ppv = static_cast<IX*>(this);

}

else if (iid==IID_IX)

{

trace1("QueryInterface: Return pointer to IX");

*ppv = static_cast<IX*>(this);

}

else if (iid==IID_IY)

{

trace1("QueryInterface: Return pointer to IY");

*ppv = static_cast<IY*>(this);

}

else

{

trace1("QueryInterface: Interface not supported.");

*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;

}

// {0F36BEC7-4D94-4096-AEF9-B37B20FD3196}

static const IID IID_IX =

{ 0xf36bec7, 0x4d94, 0x4096,{ 0xae, 0xf9, 0xb3, 0x7b, 0x20, 0xfd, 0x31, 0x96 } };

// {509A308E-D4FA-4850-B2CF-513B0CC76F5F}

static const IID IID_IY =

{ 0x509a308e, 0xd4fa, 0x4850,{ 0xb2, 0xcf, 0x51, 0x3b, 0xc, 0xc7, 0x6f, 0x5f } };

// {D5D2F831-0CDF-4C04-A130-EADE52DA05EB}

static const IID IID_IZ =

{ 0xd5d2f831, 0xcdf, 0x4c04,{ 0xa1, 0x30, 0xea, 0xde, 0x52, 0xda, 0x5, 0xeb } };

int main(void)

{

HRESULT hr;

trace1("Client : Get an IUnknown pointer.");

IUnknown* pIUnknown = CreateInstance();

trace1("Client : Get interface IX.");

IX *pIX = NULL;

hr = pIUnknown->QueryInterface(IID_IX,(void**)&pIX);

if (SUCCEEDED(hr))

{

trace1("Clent : Succeeded getting IX.") ;

pIX->Fx();

}

trace1("Client : Get interface IY.");

IY *pIY = NULL;

hr = pIUnknown->QueryInterface(IID_IY,(void**)&pIY);

if (SUCCEEDED(hr))

{

trace1("Clent : Succeeded getting IY.") ;

pIY->Fy();

}

trace1("Client : Ask for an unsupported interface.");

IZ* pIZ = NULL;

hr = pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);

if (SUCCEEDED(hr))

{

trace1("Client: Succeeded in getting interface IZ.");

pIZ->Fz();

}

else

{

trace1("Client: Could not get interface IZ.");

}

trace1("Client: Get interface IY from interface IX.");

IY* pIYfromIX = NULL;

hr = pIX->QueryInterface(IID_IY,(void**)&pIYfromIX);

if (SUCCEEDED(hr))

{

trace1("Client: Succeeded getting IY.");

pIYfromIX->Fy();

}

trace1("Client: Get interface IUnknown from 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 pIUnknown;

return 0;

}
关于QueryInterface的实现规则

QueryInterface返回的总是同一IUnknown指针

若客户曾经获取过某个接口,那么它将总能获取此接口。

客户可以再次获取已经拥有的接口

客户可以返回到起始接口

若能够从某个接口获取某特定接口,那么可以从任意接口都可以获取此接口
1)同一IUnknown

BOOL SameComponents(IX* pIX,IY* pIY)

{

IUnknown* pI1=NULL;

IUnknown* pI2=NULL;

//Get IUnknown pointer from pIX.

pIX->QueryInterface(IID_IUnknown,(void**)*pI1);

//Get IUnknown pointer from pIY.

pIY->QueryInterface(IID_IUnknown,(void**)*pI2);

return pI1 == pI2;

}
2)客户可以获取曾经得到过的接口

若对于某个给定的接口,QueryInterface曾经成功过,那么对于同一组件的后续QueryInterface将总是成功的。若对于某个给定的接口,QueryInterface调用是失败的,那么后续的调用也将会失败。这一规则适用于组件的某个特定实例。当创建组件的一个新实例时,这条规则并不适用。

3)可以再次获取已经拥有的接口

若客户拥有一个IX接口,则可以通过它来查询IX接口指针,并且个定可以成功。

void f(IX* pIX)

{

IX* pIX2 = NULL;

// Query IX for IX.

HRESULT hr = pIX->QueryInterface(IID_IX,(void**)&pIX2);

assert(SUCCEEDED(hr)); // Query mst succeed.

}

4)客户可以从任何接口返回到起始接口

若客户拥有一个IX接口指针并成功地使用它来查询一个IY接口,那么它将可以使用此IY接口来查询一个IX接口。不论客户所拥有的接口是什么,它都可以返回起始时所用的接口。

void f(IX* pIX)

{

IX* pIX2 = NULL;

IX* pIY = NULL;

// Query IX for IX.

HRESULT hr = pIX->QueryInterface(IID_IY,(void**)&pIY);

if (SUCCEEDED(hr))

{

hr = pIY->QueryInterface(IID_IX,(void**)&pIX2);

// QueryInterface must succeed.

assert(SUCCEEDED(hr));

}

}

5)若能够从某接口获取某特定接口,则从任意接口都能够获取此接口

QueryInterface定义了组件

QueryInterface是COM最为重要的部分,这主要是因为一个组件实际上就是由QueryInterface定义了。组件所支持的接口集就是QueryInterface能够为之返回接口指针的那些接口。一个组件仅仅是由QueryInterface实现决定的。
接口集QuweryMultipleInterface

分布式COM(DCOM)定义了一个新接口IMultiQI。此接口有一个新的成员函数QueryMultipleInterface。使用此函数,客户可以通过一次调用而查询组件的多个接口。这主要是为了减少数据在网络上来回传输的次数,以提高程序的效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: