接口查询:
在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是IUnknown.
头文件包含在Win32 SDK的unknwn.h头文件中。 引用如下:
interface IUnknown
{
virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv)=0;
virtual ULONG __stdcall AddRef()=0;
virtual ULONG __stdcall Release()=0;
}
1)关于IUnknown
所有的COM接口都继承了IUnknown,每个接口的vtbl中的前三个函数都是QueryInterface、AddRef和Release。如下图:
//
2)IUnknown指针的获取
能过CreateInstance的函数,它可以建立一个组件返回一个IUnkown指针
IUnkown* CreateInstance();
在创建组件时,客户可以使用CreateInstance而不必再使用new操作符。
3)关于QueryInterface
IUnknown中包含一个名为QueryInterface的成员函数,客户可以通过 此函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface将返回一个指向此接口的指针;否则返回值将是一个错误代码。
virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv)=0;
返回S_OK或E_NOINTERFACE。客户不应将QueryInterface 的返回值直接同这两个值进行比较,而应使用SUCCEEDED宏或FAILED宏。
4)QueryInterface的使用
假定客户有一个指向IUnknown指针pI,为知道相应的组件是否支持某个特定的接口,可以调用QueryInterface,并传给它一个接口标识符。若QueryInterface成功返回,那么就可以使用它返回的指针了。代码如下:
void foo(IUnknown* pI)
{
// Define a pointer for the interface
IX* pIX = NULL;
// Ask for interface IX.
HRESULT hr = pI->QueryInterface(IDD.IX,(void**)&pIX);
// check return value.
if (SUCCEEDED(hr))
{
// Use Interface
pIX->Fx1();
}
}
5)QueryInterface的实现
interface IX:IUnknown {
/*....*/
}
interface IY:IUnknown {
/*....*/
}
class CA:public IX,public IY
{
/*....*/
}
类CA及其接口的继承关系如下:
//
非虚拟继承:
注意IUnknown并不是虚拟基类。IX和IY并不能按虚拟方式继承IUnknown,这是由于会导致与COM不兼容的vtbl。若IX和IY按虚拟方式继承IUnknown,那么 IX和IY的vtbl中的头三个函数指向的将不是IUnknown的三个成员函数。
HRESULT __stdcall CA::QueryInterface(const IID& iid,void ** ppv)
{
if (iid==IID_IUnknown)
{
// The client wants the IUnknown interface.
*ppv = static_cast<IX*>(this);
}
else if (iid==IDD.IX)
{
// The client wants the IX interface.
*ppv = static_cast<IY*>(this);
}
else if (iid==IID.IY)
{
// The client wants the IY interface.
*ppv = static_cast<IY*>(this);
}
else
{
// We don't support the interface the client
// wants. Be sure to set the resulting pointer to NULL
*ppv = NULL;
return E_NOINTERFACE;
}
static_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
6)关于类型转换
将this指针转换成一个IX指针所得到的地址与将其转换成一个IY指针所得到的地址是不同的。如:
static_cast<IX*>(this) != static_cast<IY*>(this)
static_cast<void*>(this) != static_cast<IY*>(this)
同下:
(IX*)this != (IY*)this
(void*)this != (IY*)this
多重继承及类型转换
通常将一种类型的指针转换成另外一种类型并不会改变它的值。但为了支持多重继承,在某些情况下,c++必须改变类指针的值。许多c++程序员并不清楚多重继承的此种负面效果。
例如:
class CA:public IX,public IY{
/*...*/
}
void foo(IX* pIX);
void bar(IY* pIY);
int main(void)
{
CA* pA = new CA;
foo(pA);
bar(pA);
delete pA;
return 0;
}
foo需要一个指向合法的IX虚拟函数表的指针,而bar则需要一个指向IY虚拟函数表的指针。当然IX和IY的虚拟函数表中的内容是不一样的。因此在将一个IX vtbl传给bar时,此函数将不能正常工作。因此编译器将同一个指针传给foo和bar是不可能的,它必须对CA的指针进行修改以便它指向一个合适的vtbl指针。内存结构如下:
//
7)一个完整的例子
#include <iostream>
#include <objbase.h>
using namespace std;
void trace1(const char* pMsg){
cout<<pMsg<<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;
};
extern const IID IID_IX;
extern const IID IID_IY;
extern const IID IID_IZ;
class CA:public IX,public IY
{
virtual HRESULT __stdcall QueryInterface(const IID&iid,void** ppv);
virtual ULONG __stdcall AddRef(){
return 0;
}
virtual ULONG __stdcall Fx(){
cout<<"Fx"<<endl;
}
virtual void __stdcall Fy(){
cout<<"Fy"<<endl;
}
}
HRESULT __stdcall CA::QueryInterface(const IID &iid,void** ppv)
{
if (iid==IID_IUnknown)
{
trace1("QueryInterface: Return pointer to IUnknown");
*ppv = static_cast<IX*>(this);
}
else if (iid==IID_IX)
{
trace1("QueryInterface: Return pointer to IX");
*ppv = static_cast<IX*>(this);
}
else if (iid==IID_IY)
{
trace1("QueryInterface: Return pointer to IY");
*ppv = static_cast<IY*>(this);
}
else
{
trace1("QueryInterface: Interface not supported.");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
IUnknown* CreateInstance()
{
IUnknown* pI = static_cast<IX*>(new CA);
pI->AddRef();
return pI;
}
// {0F36BEC7-4D94-4096-AEF9-B37B20FD3196}
static const IID IID_IX =
{ 0xf36bec7, 0x4d94, 0x4096,{ 0xae, 0xf9, 0xb3, 0x7b, 0x20, 0xfd, 0x31, 0x96 } };
// {509A308E-D4FA-4850-B2CF-513B0CC76F5F}
static const IID IID_IY =
{ 0x509a308e, 0xd4fa, 0x4850,{ 0xb2, 0xcf, 0x51, 0x3b, 0xc, 0xc7, 0x6f, 0x5f } };
// {D5D2F831-0CDF-4C04-A130-EADE52DA05EB}
static const IID IID_IZ =
{ 0xd5d2f831, 0xcdf, 0x4c04,{ 0xa1, 0x30, 0xea, 0xde, 0x52, 0xda, 0x5, 0xeb } };
int main(void)
{
HRESULT hr;
trace1("Client : Get an IUnknown pointer.");
IUnknown* pIUnknown = CreateInstance();
trace1("Client : Get interface IX.");
IX *pIX = NULL;
hr = pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
if (SUCCEEDED(hr))
{
trace1("Clent : Succeeded getting IX.") ;
pIX->Fx();
}
trace1("Client : Get interface IY.");
IY *pIY = NULL;
hr = pIUnknown->QueryInterface(IID_IY,(void**)&pIY);
if (SUCCEEDED(hr))
{
trace1("Clent : Succeeded getting IY.") ;
pIY->Fy();
}
trace1("Client : Ask for an unsupported interface.");
IZ* pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);
if (SUCCEEDED(hr))
{
trace1("Client: Succeeded in getting interface IZ.");
pIZ->Fz();
}
else
{
trace1("Client: Could not get interface IZ.");
}
trace1("Client: Get interface IY from interface IX.");
IY* pIYfromIX = NULL;
hr = pIX->QueryInterface(IID_IY,(void**)&pIYfromIX);
if (SUCCEEDED(hr))
{
trace1("Client: Succeeded getting IY.");
pIYfromIX->Fy();
}
trace1("Client: Get interface IUnknown from IY.");
IUnknown* pIUnknownFromIY = NULL;
hr = pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownFromIY);
if (SUCCEEDED(hr))
{
cout<<"Are the IUnknown pointers equal? ";
if (pIUnknownFromIY==pIUnknown)
{
cout<<"Yes,pIUnknownFromIY == pIUnknown."<<endl;
}
else
{
cout<<"No,pIUnknownFromIY != pIUnknown."<<endl;
}
}
delete pIUnknown;
return 0;
}
关于QueryInterface的实现规则
QueryInterface返回的总是同一IUnknown指针
若客户曾经获取过某个接口,那么它将总能获取此接口。
客户可以再次获取已经拥有的接口
客户可以返回到起始接口
若能够从某个接口获取某特定接口,那么可以从任意接口都可以获取此接口
1)同一IUnknown
BOOL SameComponents(IX* pIX,IY* pIY)
{
IUnknown* pI1=NULL;
IUnknown* pI2=NULL;
//Get IUnknown pointer from pIX.
pIX->QueryInterface(IID_IUnknown,(void**)*pI1);
//Get IUnknown pointer from pIY.
pIY->QueryInterface(IID_IUnknown,(void**)*pI2);
return pI1 == pI2;
}
2)客户可以获取曾经得到过的接口
若对于某个给定的接口,QueryInterface曾经成功过,那么对于同一组件的后续QueryInterface将总是成功的。若对于某个给定的接口,QueryInterface调用是失败的,那么后续的调用也将会失败。这一规则适用于组件的某个特定实例。当创建组件的一个新实例时,这条规则并不适用。
3)可以再次获取已经拥有的接口
若客户拥有一个IX接口,则可以通过它来查询IX接口指针,并且个定可以成功。
void f(IX* pIX)
{
IX* pIX2 = NULL;
// Query IX for IX.
HRESULT hr = pIX->QueryInterface(IID_IX,(void**)&pIX2);
assert(SUCCEEDED(hr)); // Query mst succeed.
}
4)客户可以从任何接口返回到起始接口
若客户拥有一个IX接口指针并成功地使用它来查询一个IY接口,那么它将可以使用此IY接口来查询一个IX接口。不论客户所拥有的接口是什么,它都可以返回起始时所用的接口。
void f(IX* pIX)
{
IX* pIX2 = NULL;
IX* pIY = NULL;
// Query IX for IX.
HRESULT hr = pIX->QueryInterface(IID_IY,(void**)&pIY);
if (SUCCEEDED(hr))
{
hr = pIY->QueryInterface(IID_IX,(void**)&pIX2);
// QueryInterface must succeed.
assert(SUCCEEDED(hr));
}
}
5)若能够从某接口获取某特定接口,则从任意接口都能够获取此接口
QueryInterface定义了组件
QueryInterface是COM最为重要的部分,这主要是因为一个组件实际上就是由QueryInterface定义了。组件所支持的接口集就是QueryInterface能够为之返回接口指针的那些接口。一个组件仅仅是由QueryInterface实现决定的。
接口集QuweryMultipleInterface
分布式COM(DCOM)定义了一个新接口IMultiQI。此接口有一个新的成员函数QueryMultipleInterface。使用此函数,客户可以通过一次调用而查询组件的多个接口。这主要是为了减少数据在网络上来回传输的次数,以提高程序的效率。

COM技术内幕--QueryInterface函数相关推荐

  1. 《COM技术内幕》笔记(1)

    <COM技术内幕>笔记(1) <COM技术内幕>笔记(1) 第1章 组件 1.COM,即组件对象模型,是关于如何建立组件以及如何通过组件建构应用程序的一个规范. 2.组件的优点 ...

  2. 读书笔记之MySQL技术内幕

    前言 本文内容基本摘抄自<MySQL技术内幕 InnoDB存储引擎>,以供复习之用,没有多少参考价值.想要更详细了解请参考原书. 第一章.MySQL体系结构和存储引擎 数据库是物理操作系统 ...

  3. linux内核如何修改lowmem,技术内幕:Android对Linux内核的增强 Low Memory Killer

    6 09 2013 技术内幕:Android对Linux内核的增强 Low Memory Killer Low Memory Killer(低内存管理) 对于PC来说,内存是 至关重要.如果某个程序发 ...

  4. 2008技术内幕:T-SQL语言基础 联接查询摘记

    续 2008技术内幕:T-SQL语言基础 单表查询摘记 第三章 联接查询 Microsoft SQL Server 2008 支持四种表运算符 join(ANSI标准).apply(T-SQL扩展). ...

  5. IUnknown接口QueryInterface函数介绍

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

  6. mysql技术内幕sampdb_MySql技术内幕之MySQL入门(1)

    MySql技术内幕之MySQL入门(1) 安装 检查系统中是否已经安装了MySQL sudo netstat -tap | grep mysql 若没有显示已安装结果,则没有安装.否则表示已经安装. ...

  7. mysql桦仔_Microsoft SQL Server 2005技术内幕:T-SQL查询笔记

    Microsoft SQL Server 2005技术内幕:T-SQL查询笔记 目录 f f f f f f f f 第二章 物理查询处理 分析,代数化,查询优化 f f f f f. 分析--> ...

  8. 从Paxos到ZooKeeper-四、ZooKeeper技术内幕

    本文将从系统模型.序列化与协议.客户端工作原理.会话.服务端工作原理以及数据存储等方面来揭示ZooKeeper的技术内幕. 一.系统模型 1.1 数据模型 ZooKeeper的视图结构使用了其特有的& ...

  9. jQuery技术内幕:深入解析jQuery架构设计与实现原理1

    jQuery技术内幕:深入解析jQuery架构设计与实现原理 高 云 著 图书在版编目(CIP)数据 jQuery技术内幕:深入解析jQuery架构设计与实现原理 / 高云著. -北京:机械工业出版社 ...

最新文章

  1. 九度oj 题目1374:所有员工年龄排序
  2. Leetcode | 513. Find Bottom Left Tree Value
  3. java udp 同一个端口实现收发_Java网络编程之UDP协议
  4. 火柴 UVa11375
  5. Spring Cloud笔记-Maven构建父子项目
  6. linux make乱码,linux乱码
  7. getLong not implemented for class oracle.jdbc.driver.T4CRowidAccessor
  8. Spring中注入List,Set,Map,Properties的xml文件配置方法
  9. 人脸对齐(三)--AAM算法
  10. kd树的构造和搜索(超详细)
  11. 微信小程序 开发第三方自定义组件
  12. Linux(Ubuntu)之top命令
  13. 与世界对话丨预康可瘦品牌发布暨全国招商会隆重举行
  14. 破解Android app的过程记录
  15. 《淮南师范学院学报》(双月刊)投稿须知
  16. 时空同步图卷积网络:时空网络数据预测的新框架
  17. PaddlePaddle21天深度学习训练营学习心得
  18. IT人士必去的10个网站
  19. react使用富文本编辑器
  20. 已婚的女性突然开始拼命努力挣钱是为了什么?

热门文章

  1. Hive 元数据服务 MetaStore
  2. 记录一个连接,走遍美国视频下载
  3. windows添加开机启动项
  4. 服务器监控-grafana,influxdb,prometheus
  5. RuntimeError: Trying to resize storage that is not resizable
  6. 木偶然-2013.7.28
  7. 海外获取用户:你不知道的25个空手套白狼的方法
  8. 电脑桌面图标都变成lnk后缀的解决办法
  9. 重启linux时无法连接网络的解决方法
  10. 【图像处理】-019 补色