IUnKNown接口——QueryInterface函数

IUnKNown接口——QueryInterface函数COM笔记-QueryInterface函数fangyukuan2010.6.1客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown。它在UNKNWN.H头文件定义:如下       Interface IUnknown       {            virtual HRESULT _

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

fangyukuan

2010.6.1

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

       Interface IUnknown

       {

            virtual HRESULT __stdcall QueryInterfaceREFIID riidvoid ** ppvObject) = 0;

            virtual ULONG __stdcall  AddRefvoid) = 0;

            virtual ULONG __stdcall  Releasevoid) = 0;

}

 

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

IUnKNown接口——QueryInterface函数

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

 

QueryInterface

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

 

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

HRESULT __stdcall QueryInterfaceREFIID riidvoid ** 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语句是无法用的。因为接口标识符是一个结构而不是一个数。

 

IUnKNown接口——QueryInterface函数

注意:IX和IY不能按虚拟方式继承IUnknown。否则IX和IY的vtbl中的头三个函数指向的将不是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的实现规则

IUnKNown接口——QueryInterface函数 QueryInterface返回的总是同一IUnknown指针。

IUnKNown接口——QueryInterface函数 若客户曾经获取过某个接口,那么将总能获取此接口。如果曾经不能,则将总是不能。

IUnKNown接口——QueryInterface函数 客户可以再次获取已经拥有的接口。

IUnKNown接口——QueryInterface函数 客户可以返回到接口。

IUnKNown接口——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.

IUnKNown接口——QueryInterface函数 接口中函数的数目。

IUnKNown接口——QueryInterface函数 接口中函数的顺序。

IUnKNown接口——QueryInterface函数 某个函数的参数。

IUnKNown接口——QueryInterface函数 某个函数参数的顺序。

IUnKNown接口——QueryInterface函数 某个函数参数的类型。

IUnKNown接口——QueryInterface函数 函数可能的返回值。

IUnKNown接口——QueryInterface函数 函数返回值的类型。

IUnKNown接口——QueryInterface函数 函数参数的含义。

IUnKNown接口——QueryInterface函数 接口中函数的含义。

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

本文地址:http://www.cnblogs.com/fangyukuan/archive/2010/06/02/1750377.html 

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

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

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


相关推荐

  • Logout_logoutAll

    Logout_logoutAll现在到注销。早些时候我们离开这个函数一片空白,现在是时候来填补它。在UsersController:注销()添加以下:$this->Session->setFlash(‘Good-Bye’);$this->redirect($this->Auth->logout());这个设置一个会话flash信息和日志使用身份验证用户的注销方法。身份验证的注销方法基本上删除身份验证会话密钥并返回一个可

    2022年10月21日
    0
  • Pytorch_hook机制的理解及利用register_forward_hook(hook)中间层输出[通俗易懂]

    Pytorch_hook机制的理解及利用register_forward_hook(hook)中间层输出[通俗易懂]参考文献:【1】梯度计算问题含公式:参考链接1.【2】pytorch改动和.data和.detch()问题:https://blog.csdn.net/dss_dssssd/article/details/83818181【3】hook技术介绍:https://www.cnblogs.com/hellcat/p/8512090.html【4】hook应用->中间层的输出:https://blog.csdn.net/qq_40303258/article/details/10688431

    2022年10月26日
    0
  • WebStorm强大的调试JavaScript功能

    WebStorm强大的调试JavaScript功能一、JavaScript的调试目前火狐和Chrome都具备调试JavaScript的功能,而且还是相当的强大。如果纯粹是用浏览器来进行js调试的话,我比较喜欢用火狐。火狐可以安装各种插件,真的是非常适合开发者。不过今天的主角并不是火狐,也不是Chrome,而是号称最智能的JavaScriptIDE:WebStorm。WebStorm是jetbrains公司旗下一款JavaScript开发

    2022年10月23日
    0
  • mt4交易系统源码_如何将源码加载到mt4里面

    mt4交易系统源码_如何将源码加载到mt4里面1、打开编辑器:第二步,新建一个指标或者eaqml4文件.第三步创建一个ea文件:点击下一步:命名,aaa,点击下一步:全部不打勾,点击下一步:全部不打勾,点击完成:然后全部选中,删除代码:然后选中源码,复制到aaa里面,然后点击编写:就可以在ea里面找到你复制的ea了。指标的源码跟ea的一样,只需要建立一个指标文件,然后复制进去就可以了。如果觉得文章对你有帮助,可以关注公众号,谢谢您…

    2022年5月30日
    87
  • plsql直接连接远程数据库_plsql远程连接oracle

    plsql直接连接远程数据库_plsql远程连接oracle前言每次安装Oracle以后,都会出现使用plsql连接不上的问题!多次重启电脑、重装系统的磨人经历之后,终于做出这么一篇文章,希望能帮助广大技术人员减少一些时间,顺利进行连接。注:也可以用plsql连接远程数据库(只要有oracle的network\admin\tnsnames.ora就行)。首先下载64位oracle以及32位轻量级客户端(注意版本的对应,我用的是11g的oracl……

    2022年10月20日
    0
  • JMESPath_正则表达式语法

    JMESPath_正则表达式语法前言JMESPath是JSON的查询语言。您可以从JSON文档中提取和转换元素官方文档:https://jmespath.org/tutorial.html基本表达式JMESPath用的最多的

    2022年7月31日
    2

发表回复

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

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