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)


相关推荐

  • visifire 控件

    visifire 控件引言Silverlight对于图形图像处理方面,从1.0时代起就给予了很强大的支持,所以我们可以在Silverlight中实现非常棒的各种统计图表,然而现在有了一些开源的项目,使得这项工作更加的简单。本文我将介绍一个开源的项目visifire,使用它可以在Silverlight2中实现超酷的图表。简单图表首先我们需要下载Visifire项目Silverlight开发包,在建立完项…

  • 电赛校赛-三相逆变电源设计(模拟部分)「建议收藏」

    电赛校赛-三相逆变电源设计(模拟部分)「建议收藏」因为疫情问题,我们学校的这次电赛的校赛只能线上进行了,我是负责测量部分,所以其实压力相对小一点,为了统一评分,只能使用proteus8.6,我也是无奈,又捡起来了很多年不用的C51ORC52,其实还行吧,没考电压测量,在我之前仿真测试时候,测试了MSP430的ADC,但是在我们下板成功调试的demo板中的测频测幅程序,发现可能是进不去ADC的中断,莫名其妙。

  • 非线性方程(一)

    非线性方程(一)此为全书第一章,主matlab入门——通过学习各种插值法:反线性插值、牛顿法之类。1、diff>>diff('x^2')ans=-26-44>>dif

  • django mysqlclient_mac好用的ssh

    django mysqlclient_mac好用的sshmac系统安装mysqlclient时,会报错OSError:mysql_confignotfound解决办法在项目路径下输入以下内容PATH="$PATH":/usr

  • nginx接口转发_网关和nginx区别

    nginx接口转发_网关和nginx区别nginx转发给网关的时候会丢失很多信息,包括host信息解决办法在nginx的配置文件配置proxy_set_headerHost$host;proxy_passhttp://转发的ip地址还有一点需要注意网关转到具体的域名需要配置在网关的最后…

    2022年10月10日
  • 关于OleDbCommand中操作数据库的几种方法的区别「建议收藏」

    关于OleDbCommand中操作数据库的几种方法的区别「建议收藏」在vb.net中利用OleDb的OleDbCommand类操作数据库,有以下这些方法: ExecuteNoQuery()返回值类型integer,常用来执行增删改操作,返回操作影响的行数ExecuteReader()返回一个只读的数据集,常用来作查询操作ExecuteScalar()返回值类型Object,执行查询,并返回查询所返回的结果集中第一行的第一列,常用来作一

发表回复

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

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