com QueryInterface「建议收藏」

com QueryInterface「建议收藏」客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown。它在UNKNWN.H头文件定义 :如下

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

客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown。它在UNKNWN.H头文件定义 :如下

       Interface IUnknown

       {

            virtual HRESULT __stdcall QueryInterface( REFIID riid, void ** ppvObject) = 0;

            virtual ULONG __stdcall  AddRef( void) = 0;

            virtual ULONG __stdcall  Release( void) = 0;

}

 

所有的COM接口都继承了IUnknown,每个接口的vtbl中的前三个函数都是QueryInterface、AddRef、Release(如图3-1)。这样所有COM接口都可以被当成IUnknown接口来处理。

com QueryInterface「建议收藏」

由于所有的接口都是从IUnknown继承的,因此所有的接口都支持QueryInterface,所以组件的任何一个接口都可以被客户用来获取它所支持的其他接口。

 

QueryInterface

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

 

QueryInterface 有两个参数,和一个HRESULT返回值

HRESULT __stdcall QueryInterface( REFIID riid, void ** ppvObject);

 

第一个参数:接口标识符(IID)

第二个参数:存放所请求接口指针的地址。

返回值:查询成功返回S_OK,如果不成功则返回相应错误码。

 

QueryInterface的使用

void foo(IUnknown* pI)

{

    // 定义一个接口指针

    IX* pIX = NULL;

 

    // 查询接口IX

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

    if (SUCCEEDED(hr))

    {

       // 通过接口调用函数

       pIX->Fx();

    }

}

 

QueryInterface实现

 

根据某个给定的IID返回指向相应接口的指针。若组件支持客户指定的接口,那么应返回S_OK以及相应的指针。若不支持返回测返回E_NoINTERFACE并将相应的指针返回值置成NULL。

QueryInterface的实现要求可以将一种类型映射成另外一种类型的结构。如:if else 、数组、哈希表或者是树来实现,但是case语句是无法用的。因为接口标识符是一个结构而不是一个数。

 

com QueryInterface「建议收藏」

注意:IX和IY不能按虚拟方式继承IUnknown。否则IX和IY的vtbl中的头三个函数指向的将不是IUnknown的三个成员函数。

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

{  

    if (iid == IID_IUnknown)

    {

       trace(“QueryInterface: Return pointer to IUnknown.”);

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

    }

    else if (iid == IID_IX)

    {

       trace(“QueryInterface: Return pointer to IX.”);

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

    }

    else if (iid == IID_IY)

    {

       trace(“QueryInterface: Return pointer to IY.”);

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

    }

    else

    {
     

       trace(“QueryInterface: Interface not supported.”);

       *ppv = NULL;

       return E_NOINTERFACE;

    }

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

    return S_OK;

}

 

 

IUnknown

接口标准化,在COM中有两方面的内容:一是接口基本功能的标准化,二是接口内存结构的标准化。为了保证组件接口在基本功能上的标准化,COM预定义了一个基本接口IUnknow。(在文件UNKNWN.H中定义)

Class   Iunknown

{

    Public:

        Virtual  HRESULT _stdcall  QueryInterface(const IID& iid, void **ppv)=0;

        Virtual  HRESULT _stdcal   AddRef( )=0;

        Virtual  HRESULT _stdcal   Release( )=0;

};

显然,Iunknown接口具有3个纯虚函数。COM 要求组件的所有接口必须继承自IUnknown接口,这样就保证组件的所有接口都能提供这3个服务(函数)。

 

一个完整的例子

(vs2008)代码下载:http://www.box.net/shared/m4yr9z73zu

复制代码
  
  
  
#include < iostream > using namespace std; #include < objbase.h > 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 ; }; // Forward references for GUIDs extern const IID IID_IX; extern const IID IID_IY; extern const IID IID_IZ; // // 实现接口 IX,IY(这里表示一个组件) // 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;} // Interface IY implementation virtual void __stdcall Fy() { cout << " 这里是Fy函数 " << endl;} }; HRESULT __stdcall CA::QueryInterface( const IID & iid, void ** ppv) { if (iid == IID_IUnknown) { trace( " QueryInterface: Return pointer to IUnknown. " ); * ppv = static_cast < IX *> ( this ); } else if (iid == IID_IX) { trace( " QueryInterface: Return pointer to IX. " ); * ppv = static_cast < IX *> ( this ); } else if (iid == IID_IY) { trace( " QueryInterface: Return pointer to IY. " ); * ppv = static_cast < IY *> ( this ); } else { trace( " QueryInterface: Interface not supported. " ); * ppv = NULL; return E_NOINTERFACE; } reinterpret_cast < IUnknown *> ( * ppv) -> AddRef(); // 加计数 return S_OK; } // // 创建类CA,并返回一个指向IUnknown的指针 // IUnknown * CreateInstance() { IUnknown * pI = static_cast < IX *> ( new CA); pI -> AddRef(); return pI ; } // // 下面是各接口的IID // // {32bb8320-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IX = { 0x32bb8320 , 0xb41b , 0x11cf , { 0xa6 , 0xbb , 0x0 , 0x80 , 0xc7 , 0xb2 , 0xd6 , 0x82 }}; // {32bb8321-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IY = { 0x32bb8321 , 0xb41b , 0x11cf , { 0xa6 , 0xbb , 0x0 , 0x80 , 0xc7 , 0xb2 , 0xd6 , 0x82 }}; // {32bb8322-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IZ = { 0x32bb8322 , 0xb41b , 0x11cf , { 0xa6 , 0xbb , 0x0 , 0x80 , 0xc7 , 0xb2 , 0xd6 , 0x82 }}; // // 主函数(这里代表客户) // int main() { HRESULT hr; trace( " Client:获取 IUnknown指针. " ); IUnknown * pIUnknown = CreateInstance(); trace( " Client:获取接口IX. " ); IX * pIX = NULL; hr = pIUnknown -> QueryInterface(IID_IX, ( void ** ) & pIX); if (SUCCEEDED(hr)) { trace( " Client:获取接口IX成功. " ); pIX -> Fx(); // 使用 IX. } trace( " Client:获取接口IY. " ); IY * pIY = NULL; hr = pIUnknown -> QueryInterface(IID_IY, ( void ** ) & pIY); if (SUCCEEDED(hr)) { trace( " Client: Succeeded getting IY. " ); pIY -> Fy(); // 使用 IY. } trace( " Client:是否支持接口IZ. " ); IZ * pIZ = NULL; hr = pIUnknown -> QueryInterface(IID_IZ, ( void ** ) & pIZ); if (SUCCEEDED(hr)) { trace( " Client:获取接口IZ成功. " ); pIZ -> Fz(); } else { trace( " Client:获取接口IZ失败,不支持接口IZ. " ); } trace( " Client:用接口IX查询接口IY. " ); IY * pIYfromIX = NULL; hr = pIX -> QueryInterface(IID_IY, ( void ** ) & pIYfromIX); if (SUCCEEDED(hr)) { trace( " Client:获取接口IY成功. " ); pIYfromIX -> Fy(); } trace( " Client:用接口IY查询接口IUnknown. " ); IUnknown * pIUnknownFromIY = NULL; hr = pIY -> QueryInterface(IID_IUnknown, ( void ** ) & pIUnknownFromIY); if (SUCCEEDED(hr)) { cout << " IUnknown指针是否相等? " ; if (pIUnknownFromIY == pIUnknown) { cout << " Yes, pIUnknownFromIY == pIUnknown. " << endl; } else { cout << " No, pIUnknownFromIY != pIUnknown. " << endl; } } // Delete the component. delete pIUnknown; return 0 ; }
复制代码

 

QueryInterface的实现规则

com QueryInterface「建议收藏」 QueryInterface返回的总是同一IUnknown指针。

com QueryInterface「建议收藏」 若客户曾经获取过某个接口,那么将总能获取此接口。如果曾经不能,则将总是不能。

com QueryInterface「建议收藏」 客户可以再次获取已经拥有的接口。

com QueryInterface「建议收藏」 客户可以返回到接口。

com QueryInterface「建议收藏」 若能够从某个接口获取某特定接口,那么可以从任意接口都将可以获取此接口。

 

同一IUnknown指针:

组件的实例只有一个IUnknown接口。因为查询组件实例的IUnknown接口时,不论通过哪个接口,所得到的均将是同一指针值。所以可以通过两个接口的IUnknown,然后比较他们的值。看看是否相同来判断两个接口是否在同一个组件里。

/*

    判断两个接口是否在同一个组件里

*/

BOOL SameComponents(IX* pIX, IY* pIY)

{

    IUnknown* pI1 = NULL;

    IUnknown* pI2 = NULL;

 

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

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

    return pI1 == pI2;

}

 

QueryInterface定义了组件

一个组件实际上就是由QueryInterface定义的。组件所支持的接口集就是QueryInterface能够为之返回接口指针的那些接口。这一点是由QueryInterface的实现决定的,而不是由实现组伯的C++类决定的。实现组件的类的继承层次关系也不能决定组件。

 

新版本组件的处理

COM接口是不会发性变化的,当组件发布一个接口并补某个使用后,此接口将决不会发生任何变化。如果我们想改动它。只能通过增加新的接口。

 

何时需要建立一个新版本

当改了下列条件中任何一个时,就应该给新接口指定新的ID.

com QueryInterface「建议收藏」 接口中函数的数目。

com QueryInterface「建议收藏」 接口中函数的顺序。

com QueryInterface「建议收藏」 某个函数的参数。

com QueryInterface「建议收藏」 某个函数参数的顺序。

com QueryInterface「建议收藏」 某个函数参数的类型。

com QueryInterface「建议收藏」 函数可能的返回值。

com QueryInterface「建议收藏」 函数返回值的类型。

com QueryInterface「建议收藏」 函数参数的含义。

com QueryInterface「建议收藏」 接口中函数的含义。

总之,只要是所做的修改会影响客户的正常运行,都应该为接口指定新的ID

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

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

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

相关推荐

  • Android UI Design

    Android UI Design

    2021年9月6日
  • openGL研究钞四 : 关于颜色, 尺寸, 虚线, 多边形逆转, 空洞, 使用位图

    openGL研究钞四 : 关于颜色, 尺寸, 虚线, 多边形逆转, 空洞, 使用位图

    2022年1月14日
  • 随机梯度下降算法原理 知乎_梯度下降算法的正确步骤

    随机梯度下降算法原理 知乎_梯度下降算法的正确步骤目录1.算法目标2.算法描述3.算法推导4.注意1.算法目标逐渐逼近损失函数loss的极小值,简单抽象为求函数的极小值。2.算法描述每次取一个增量,使得,每次向函数值更小的地方前进一小步,多次迭代就能做到逐渐逼近函数的极小值。3.算法推导展开得到公式。其中H为海森矩阵,暂且不考虑。为使成立,只需要保证。即,当时,,如此即可保证每次更新在逐渐逼近函数的极小值。其中为学习率是一个较小的正数。每次更新时做操作,求得的最小值。4.注意上..

    2022年9月4日
  • Murmur下载_highwayhash

    Murmur下载_highwayhashpackageorg.videolan.vlc;/**Murmurhash2.0.**Themurmurhashisarelativefasthashfunctionfrom*http://murmurhash.googlepages.com/forplatformswithefficient*multiplication.**Thisisare-i…

    2022年10月19日
  • js传参数到另一个页面_jquery json字符串转换json对象

    js传参数到另一个页面_jquery json字符串转换json对象我是MVC3和Razor的新手,一旦从AJAX发布返回数据,就需要绑定/加载WebGrid的帮助。任何帮助将不胜感激(项目截止日期很快临近);)我的情况是这样的:我有两个级联的下拉列表。第一个列表包含数据库中的区域。选择区域后,它将在第二个下拉列表中填充一系列设施。选择设施后,我需要使用建筑物列表填充WebGrid。我的级联下拉菜单正常工作Index.cshtml:@usingThisContr…

    2022年10月6日
  • SpringBoot的认识,SpringBoot与Spring关系[通俗易懂]

    SpringBoot的认识,SpringBoot与Spring关系[通俗易懂]一、概念1、SpringSpring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。2、SpringMVCSpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。SpringMVC是一种web层mvc框架,用于替代servlet(处理|响应请求,获取表单参数,表单校验等。S

    2022年5月27日

发表回复

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

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