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

Interface IUnknown

{

virtual HRESULT __stdcall QueryInterface( REFIID riid, void ** ppvObject) = 0;

virtual ULONG __stdcall  AddRef( void) = 0;

virtual ULONG __stdcall  Release( void) = 0;

}

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

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

QueryInterface

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

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

HRESULT __stdcall QueryInterface( REFIID riid, void ** ppvObject);

第一个参数:接口标识符(IID)

第二个参数:存放所请求接口指针的地址。

返回值:查询成功返回S_OK,如果不成功则返回相应错误码。

QueryInterface的使用

void foo(IUnknown* pI)

{

// 定义一个接口指针

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

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

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;

}

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的实现规则

 QueryInterface返回的总是同一IUnknown指针。

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

 客户可以再次获取已经拥有的接口。

 客户可以返回到接口。

 若能够从某个接口获取某特定接口,那么可以从任意接口都将可以获取此接口。

同一IUnknown指针:

组件的实例只有一个IUnknown接口。因为查询组件实例的IUnknown接口时,不论通过哪个接口,所得到的均将是同一指针值。所以可以通过两个接口的IUnknown,然后比较他们的值。看看是否相同来判断两个接口是否在同一个组件里。

/*

判断两个接口是否在同一个组件里

*/

BOOL SameComponents(IX* pIX, IY* pIY)

{

IUnknown* pI1 = NULL;

IUnknown* pI2 = NULL;

pIX->QueryInterface(IID_IUnknown, (void**)&pI1);

pIY->QueryInterface(IID_IUnknown, (void**)&pI2);

return pI1 == pI2;

}

QueryInterface定义了组件

一个组件实际上就是由QueryInterface定义的。组件所支持的接口集就是QueryInterface能够为之返回接口指针的那些接口。这一点是由QueryInterface的实现决定的,而不是由实现组伯的C++类决定的。实现组件的类的继承层次关系也不能决定组件。

新版本组件的处理

COM接口是不会发性变化的,当组件发布一个接口并补某个使用后,此接口将决不会发生任何变化。如果我们想改动它。只能通过增加新的接口。

何时需要建立一个新版本

当改了下列条件中任何一个时,就应该给新接口指定新的ID.

 接口中函数的数目。

 接口中函数的顺序。

 某个函数的参数。

 某个函数参数的顺序。

 某个函数参数的类型。

 函数可能的返回值。

 函数返回值的类型。

 函数参数的含义。

 接口中函数的含义。

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

com QueryInterface相关推荐

  1. JS和C#访问遇到QueryInterface调用出错

    在原来的WinForm里,我们只要在窗体类的头部添加属性[System.Runtime.InteropServices.ComVisibleAttribute(true)],然后 webBrowser ...

  2. C++ COM编程之QueryInterface函数(二)

    前言 在COM编程--认识组件中也总结了,COM是一个说明如何建立可动态互变组件的规范,它提供了为保证能够互操作,客户和组件应遵循的一些标准.而在实现和使用QueryInterface时,就需要去遵守 ...

  3. IUnknown接口QueryInterface函数介绍

    一.COM组件的目标: COM组件的一个主要优势是:便于升级. 要实现这个优势需要满足一下两个条件: 1.运行时从客户程序动态加载和卸载,采用DLL技术可以实现. 2.为了更好的突出DLL的优势,还需 ...

  4. 使用SQLDMO中“接口SQLDMO.Namelist 的 QueryInterface 失败”异常的解决方法

    SQLDMO(SQL Distributed Management Objects,SQL分布式管理对象),它封装 Microsoft SQL Server 数据库中的对象.它允许我们通过COM对象, ...

  5. com学习笔记(2)基本的com接口-QueryInterface的实现

    QueryInterface   接上篇 一.COM接口的开始IUnknown com起于接口,又归于接口. com之所以是com,是因为其继承了一个名为IUnknown接口. IUnknown接口是 ...

  6. IUnknow IDispatch IInspectable QueryInterface

    IUnknow IDispatch IInspectable QueryInterface IUnknow接口是个伟大的创造! IUnknow的AddRef和Release实现对象的引用计数管理, 引 ...

  7. ArcGIS Engine 开发 (三)COM技术中的QueryInterface(接口查询)的实现原理和IUnknown接口

    IUnknown IUnknown 接口是组件对象模型(COM)中的基础接口.COM规格书中规定COM对象至少要实现此一接口,而且其他所有的COM接口都需要派生自IUnknown接口. IUnknow ...

  8. C++ 编程之QueryInterface函数(一)

    前言 组件对外公布的是接口:一个组件可以实现多个接口,也就是说可以对外公布多个接口,之前也总结过了,你很少会100%的去完全了解一个组件的所有接口,就像你去学习编程一样,你几乎不可能去成为编程中的全才 ...

  9. c#中的接口查询(QueryInterface)

    接口查询(QueryInterface) 一个类可以有多个接口,声明了接口变量并且指向一个对象的时候,这个变量只能使用该接口内的方法和属性,而不能访问其他接口中的方法和属性.但接口查询很方便的让我们在 ...

  10. error C2227: “-QueryInterface”的左侧必须指向类/结构/联合

    问题:ado编程 编译时发现QueryInterface"的左边必须指向类/结构/联合/泛型类型 错误: e:\Program Files\Microsoft Visual Studio . ...

最新文章

  1. 认识HTML5的WebSocket 认识HTML5的WebSocket
  2. R使用glm构建logistic回归模型
  3. USB有时adb shell连不上设备
  4. POJ-2777-CountColor(线段树,位运算)
  5. python比赛评分计算_python3:(可输入评委人数和参赛人数)模拟决赛现场最终成绩计算过程...
  6. 跨页面访问ViewState
  7. mysql+int+类型如何模糊搜索_mysql全文模糊搜索MATCH AGAINST方法示例
  8. windows无法打开添加打印机_打印机常见故障机及处理方法
  9. 计算机语言 指令,计算机BASIC语言    指令
  10. MySQL Front的作者到底何许人也,这款好用的sql可视化软件背后有什么故事。。
  11. 2016 hitb-facebook-ctf capture-mexico-tls RSA-CRT-Attack
  12. linux主机如何安装杀毒软件,Linux 杀毒软件ClamAV安装部署
  13. 单片机毕业设计 stm32车牌识别系统
  14. 测试开发人员与开发人员_我是真正的开发人员还是优秀的Googler?
  15. MacOS启动台(launchpad)缺少应用软件图标
  16. Tipask,Tipask建站,Tipask插件
  17. 品葡萄酒的11个常见问题
  18. vue2 provide和inject的使用
  19. 北京各大医院专长(转)
  20. MySQL 快速入门之DATE_FORMAT() 函数详解

热门文章

  1. 阿里视频云黄海宇:解析世界杯超大规模直播场景下的码率控制
  2. c语言网络编程断点续传,网络编程实战之FTP的文件断点续传
  3. AMD三代锐龙箭在弦上:如此家族堪称豪华
  4. 摄影构图_摄影中的构图是什么?
  5. 建筑业供应链具有哪些特点?
  6. thymeleaf frame 局部刷新_明日方舟公开招募标签强制刷新 黑赫拉格加入
  7. linux crontab文件,crontab用法与实例
  8. net framework是什么意思?net framework怎么修复?
  9. (ง •_•)ง[Python3 OpenCV4]4.颜色转换
  10. mysql全文索引 版本_MySQL 全文索引实现简单版搜索引擎