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













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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • html中怎样使表格居中,HTML中怎么让表格居中[通俗易懂]

    html中怎样使表格居中,HTML中怎么让表格居中[通俗易懂]回答:IE6/7及IE8混杂模式中,text-align:center可以使块级元素也居中对齐。其他浏览器中,text-align:center仅作用于行内内容上。解决这个问题比较好的方式,就是为所有需要相对父容器居中对齐的块级元素设置“margin-left:Auto;margin-right:Auto”。但这个方式IE6/IE7/IE8的混杂模式中不支持,所以还要设置父容器的”text…

  • Docker 容器中运行 Docker 命令

    Docker 容器中运行 Docker 命令Docker容器中运行Docker命令在使用GitLab/Jenkins等CI软件的时候需要使用Docker命令来构建镜像,需要在容器中使用Docker命令;通过将宿主机的Docker共享给容器即可在启动容器时添加以下命令:–privileged\-v/var/run/docker.sock:/var/run/doc…

  • c++ stl源码剖析_stl源码 qt源码

    c++ stl源码剖析_stl源码 qt源码C++stl库手写前言序列式关联式容器适配器ListVector函数dequestringstackqueuebitset关联式容器setmultisetmultiset算法库仿函数前言stl版本abcd,四个版本,接口肯定是一样的代码复用性强,效率高,通用性高,vectordeque他有六个组件,空间配置器,容器,迭代器,算法,仿函数,容器适配器容器和算法中间,靠迭代器连接算法为了通用性,有辅助的东西,让算法通用,也就是使用仿函数仿函数就是一个对象容器通过适配器,可以相

    2022年10月15日
  • iTunes下载的固件在哪里_applemobiledevice安装不了

    iTunes下载的固件在哪里_applemobiledevice安装不了Keyword:iphone,itouch,ipad激活成功教程软件;installous和icabmobile 问题描述:今天使用Installous更新了iCabMobile浏览器后,发现Downloads里面的东西全都不见了,用ifile查看var/mobile/Documents/Installous目录下也没有Downloads文件夹了,遵循weiphone上的

  • Apache httpClient+Jackson学习笔记

    Apache httpClient+Jackson学习笔记

  • cms开源网站管理系统_javaweb开源商城

    cms开源网站管理系统_javaweb开源商城笔者整理了八款.Net优秀的开源CMS内容管理系统,推荐给广大的.net开发者。

发表回复

您的电子邮箱地址不会被公开。

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