COM技术内幕–QueryInterface函数「建议收藏」

COM技术内幕–QueryInterface函数「建议收藏」接口查询:在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown.头文件包含在Win32SDK的unknwn.h头文件中。引用如下:interfaceIUnknown{virtualHRESULT__stdcallQueryInterface(constIID&iid,void**ppv)=0;virtual

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

接口查询:
在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是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。如下图:
//
COM技术内幕--QueryInterface函数「建议收藏」

COM技术内幕--QueryInterface函数「建议收藏」
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及其接口的继承关系如下:
//
COM技术内幕--QueryInterface函数「建议收藏」

COM技术内幕--QueryInterface函数「建议收藏」
非虚拟继承:
注意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指针。内存结构如下:
//
COM技术内幕--QueryInterface函数「建议收藏」

COM技术内幕--QueryInterface函数「建议收藏」
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。使用此函数,客户可以通过一次调用而查询组件的多个接口。这主要是为了减少数据在网络上来回传输的次数,以提高程序的效率。













版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 一篇文章读懂企业如何升级到云安全体系

    一篇文章读懂企业如何升级到云安全体系

    2022年3月6日
    46
  • 【NLP】之 结巴分词

    【NLP】之 结巴分词1.结巴分词简介结巴分词是当前效果较好的一种中文分词器,支持中文简体、中文繁体分词,同时还支持自定义词库。结巴分词支持三种分词模式:精确模式、全模式和搜索引擎模式。精确模式是试图将句子最精确的进行切分,适合用于文本分析; 全模式的原理是把句子中全部可以成词的词语全部扫描出来,它的分词速度快,缺点是无法识别歧义词句; 搜索引擎模式是在精确模式的基础上进一步处理的,它对较长的词语再进…

    2022年6月17日
    28
  • 灰色关联度矩阵模型及其MATLAB实现[通俗易懂]

    灰色关联度矩阵模型及其MATLAB实现[通俗易懂]灰色关联度矩阵是灰色系统另一个非常重要的领域,通常用于分析向量与向量之间或矩阵与矩阵之间的关联度,其实用性非常强。

    2022年5月14日
    42
  • c语言头文件instr,C/C++常用函数合集「建议收藏」

    目录C标准库头文件1.memset()2.memcpy()3.strcpy()4.strncpy()5.strcat()6.getline()7.strlen()头文件1.tolower()/toupper()2.isalpha(),isdigit(),isprint()头文件1.pow()2.floor()3.ceil()4.atan()STL头文件1.min(),max()函数2.lower_…

    2022年4月8日
    97
  • 分子模拟软件amber_容天AMBER优化的GPU解决方案

    分子模拟软件amber_容天AMBER优化的GPU解决方案AMBER认证的GPU系统AMBER认证GPU系统提供商容天更快地运行MD仿真容天与AMBER的主要开发商合作开发了交钥匙解决方案,为GPU加速的生物分子模拟提供增值系统。经过验证的系统,每个用户的CPU,GPU,内存和存储具有适当的平衡。从工作站到超级计算机的高度可扩展系统,具有3年保修和支持。容天AMBER优化的GPU加速系统更快地完成MD仿真并不需要花费很多。我们的AMBERG…

    2022年5月9日
    108
  • date和localdatetime转换_localDate

    date和localdatetime转换_localDateLocalDateTime是jdk8的新增的类,还有LocalDate,LocalTime;我们可能用到类里面的一些方法,例如传入的时间和当前时间做比较,就需要将Date转为LocalDate或其他两个,Date转换为LocalDateDatedate=newDate();LocalDatelocalDate=date.toInstant().atZone(ZoneId.systemDefault()) //设置当前系统时区.toLocalDat

    2022年10月4日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号