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

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

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

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

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

(0)
blank

相关推荐

  • 彩色图和深度图转点云

    彩色图和深度图转点云环境:windows10、VS2013、opencv2.49、openNi、PCL1.8opencv环境搭建参考https://www.cnblogs.com/cuteshongshong/p/4057193.htmlhttps://blog.csdn.net/u013105549/article/details/50493069PCL1.8+openNi搭建参考https://blog.cs…

  • gridbagconstraints什么意思_gridlayout布局参数

    gridbagconstraints什么意思_gridlayout布局参数GridBagConstraints参数详解gridBagConstraints参数gridx=2;//X=2gridy=0;//Y=0gridwidth=1;//横占一个单元格gridheight=1;//列占一个单元格weightx=0.0;//当窗口放大时,长度不变weighty=0.0;//当窗口放大时,高度不变

  • 红黑树和平衡二叉树区别[通俗易懂]

    红黑树和平衡二叉树区别[通俗易懂]红黑树和平衡二叉树区别如下:1、红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。2、平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。…

    2022年10月20日
  • ByteBuffer的allocate和allocateDirect

    ByteBuffer的allocate和allocateDirect在Java中当我们要对数据进行更底层的操作时,一般是操作数据的字节(byte)形式,这时经常会用到ByteBuffer这样一个类。ByteBuffer提供了两种静态实例方式:Java代码publics

  • 网盘lua调用失败(dumb down)

    前阵子在弄一个dump程序的时候遇到这样一个问题.
    dump程序在被dump程序debug情况下一切正常,release版本下调用了minidumpwritedump函数进行dump,但是minidumpwritedump函数调用失败,GetLastError()获得到的是一个不正常的大数.
    在网络上久寻未果,遇到同样问题的人都没有提出最后的解决方案,也尝试过修改项目设置等一系列方法,没有解决.
     
    有一天想重新研究这个问题,看这个帖子http://app

  • Qt QListWidget详解

    Qt QListWidget详解1.QListWidget和QListViewQListWidget是继承QListView,QListView是基于Model的,需要自己来建模(如建立QStringListModel,QSqlTableModel等),保存数据,这样就大大降低了数据冗余,提高了程序的效率,但是需要我们对数据建模有一定的了解,而QListWidget是一个升级版本的QListView,它已经为我们建立了一个数据存储模型QListWidgetItem,操作方便,直接调用addItem即可添加Item项。2….

发表回复

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

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