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


相关推荐

  • DataGrid中DropDownList的动态绑定以及触发DropDownList事件

    DataGrid中DropDownList的动态绑定以及触发DropDownList事件今天在写DataGrid中DropDownList的绑定,客户需要根据值去改变价格信息,发现忘记如何写的了。虽然网上很多了,但自己还是做个笔记,以后查询方便1.ASPX页面1<asp:TemplateColumnHeaderText=”退还价格类型”>2<ItemTemplate>3<asp:DropDownListID=”ddlRe…

    2025年10月27日
    3
  • Java–LinkedList真的比ArrayList添加元素快?Open JDK JMH带你揭开真相「建议收藏」

    Java–LinkedList真的比ArrayList添加元素快?Open JDK JMH带你揭开真相「建议收藏」【学习背景】欢迎进来学习的小伙伴~不管你是学生,还是职场小白,还是入行1~3年的小伙伴,相信很多小伙伴在面试Java工作岗位时,发现LinkedList和ArrayList这个问题基本是必面的一道题,【面试场景】当面试官问到LinkedList和ArrayList的区别时,可能很多准备得不够充分的小伙伴第一反应的回答仅仅是这样的:LinkedList底层数据结构是链表,添加和删除元素效率比ArrayList高~ArrayList底层数据结构是数组,查询效率比LinkedList高~面试官:哦

    2022年7月11日
    21
  • json字符串转换成对象有哪几种方法_jsonstring转对象

    json字符串转换成对象有哪几种方法_jsonstring转对象1.将json字符串转化为json对象a.方案一:jquery自带的$.parseJSON函数&amp;lt;script&amp;gt;varjsonstr=&quot;{\&quot;id\&quot;:\&quot;1\&quot;,\&quot;name\&quot;:\&quot;jack\&quot;}&quot;;varobj=$.parseJSON(jsonstr);&a

    2022年9月1日
    5
  • 一加6手机图片中的文字如何识别?[通俗易懂]

    一加6手机图片中的文字如何识别?[通俗易懂]一加6手机很多人都想买吧?买了手机的朋友手机里就有很多照片,一加6手机图片中的文字如何识别?文字识别都是识别大量的文字,减轻办公人员负担的。1在手机上输入PDF阅读器,然后开始识别相册图片的文字2打开就是这样的界面,找到倒数第2个的小功能3选择拍照识别中的相册,接下来添加图…

    2022年5月1日
    440
  • 内积空间上的等积变换_内积和距离的关系

    内积空间上的等积变换_内积和距离的关系内积空间、三角不等式、模长、不变子空间、镜像变换

    2022年10月7日
    0
  • micro f1和macro f1_correct score

    micro f1和macro f1_correct score首先,明确一个概念,精确率(Precision)和召回率(Recall)并不是只有在目标检测中才有的,是所有任务涉及到分类的都有。而且P和R是针对于每一类来说的,每一个类别都有自己的准确率和召回率,计算每一个类别时,该样本即为正样本,其他样本统一为负样本来计算。首先,先说F1score,它其实是用来评价二元分类器的度量。F1是针对二元分类的,那对于多元分类器,有没有类似F1score的度量方法呢?那就是这里提到的micro-F1和macro-F1macro-F1…

    2022年10月10日
    3

发表回复

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

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