前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >QueryInterface详解 COM

QueryInterface详解 COM

作者头像
全栈程序员站长
发布2022-09-07 10:15:52
9790
发布2022-09-07 10:15:52
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

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;

}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/147857.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档