com QueryInterface「建议收藏」

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

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

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

       Interface IUnknown

       {

            virtual HRESULT __stdcall QueryInterfaceREFIID riid, void ** ppvObject) = 0;

            virtual ULONG __stdcall  AddRefvoid) = 0;

            virtual ULONG __stdcall  Releasevoid) = 0;

}

 

所有的COM接口都继承了IUnknown,每个接口的vtbl中的前三个函数都是QueryInterfaceAddRefRelease(如图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(IUnknownpI)

{

    // 定义一个接口指针

    IXpIX = 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「建议收藏」

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

HRESULT __stdcall CA::QueryInterface(const IIDiidvoid** 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(IXpIXIYpIY)

{

    IUnknownpI1 = NULL;

    IUnknownpI2 = 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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • vue ajax获取数据的时候,如何保证传递参数的安全或者说如何保护api的安全

    vue ajax获取数据的时候,如何保证传递参数的安全或者说如何保护api的安全

    2021年10月13日
    37
  • pycharm 修改镜像源_linux镜像源哪个好

    pycharm 修改镜像源_linux镜像源哪个好镜像网站是将一个完全相同的站点放到几个服务器,分别有自己的URL,在这些服务器上互为镜像网站。它和主站并没有太大差别,或者可算是为主站作的后备措施。有了镜像网站的好处是:如果不能对主站作正常访问(如某个服务器死掉或出了意外),但仍能通过其它服务器正常浏览。相对来说主站在速度等各方面比镜像站点略胜一筹。

    2022年8月25日
    11
  • BM3D 算法原理详细解析 按过程步骤讲解(附C++实现代码)[通俗易懂]

    BM3D 算法原理详细解析 按过程步骤讲解(附C++实现代码)[通俗易懂]BM3D是2007年提出的算法了,至今已经有一些年头了,但是仍然不妨碍它基本上还是最强的去噪算法。在初步了解了BM3D的算法后,会发现,BM3D有种堆叠怪的嫌疑。有很多不同的算法的影子在里面,比如Non-LocalMeans、Waveletshrinkage等。我感觉思想更像是暴力出奇迹吧。但是架不住效果好。就类似现在的神经网络,一层不行我就来两层。但是相比简单的堆叠,BM3D还是比其他的算法的细节保留上要好一些。好了,不废话了,下面直接来看BM3D的算法原理吧。BM3D算法原理:

    2022年5月27日
    34
  • 哥伦比亚大学留学申请_哥伦比亚大学巴纳德学院怎么样

    哥伦比亚大学留学申请_哥伦比亚大学巴纳德学院怎么样【转载原因】这是一篇讲述出国申请的文章。虽然我不准备出国,但还是被那朴实而激扬的文字所感染了。这确实是一篇励志的好文。其实不论在哪里,做什么,我们都需要朝着自己的目标去奋斗。真的,只有不懈地奋斗,才能活出更加精彩的人生。文章的作者确实很聪明,而且很坚强。我从心底里佩服她。每个人都有各自不同的路,就像文中提到的某些人,虽然一开始也不满现状,信心满满,但是最终还是向现实妥协了。每每看到一篇像这样…

    2025年7月20日
    3
  • ucosii操作系统内核源码学习第一篇

    ucosii操作系统内核源码学习第一篇待会就开始学习

    2022年5月6日
    37
  • jQuery简洁大方的登录页面模板

    jQuery+CSS网站登录模板本模板带验证码在线体验:http://hovertree.com/texiao/jquery/13.htmDemo2:http://hovertree.com/hvt

    2021年12月21日
    56

发表回复

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

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