COM技术内幕--QueryInterface函数

COM 专栏收录该内容
23 篇文章 0 订阅
接口查询:
在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是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。使用此函数,客户可以通过一次调用而查询组件的多个接口。这主要是为了减少数据在网络上来回传输的次数,以提高程序的效率。













  • 2
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值