C++ COM编程之QueryInterface函数(一)

所属分类: 软件编程 / C 语言 阅读数: 149
收藏 0 赞 0 分享

前言

组件对外公布的是接口;一个组件可以实现多个接口,也就是说可以对外公布多个接口,之前也总结过了,你很少会100%的去完全了解一个组件的所有接口,就像你去学习编程一样,你几乎不可能去成为编程中的全才。那么,既然我们不能去完全的了解一个组件提供的所有接口,那么我们在实际开发中,如何去判断一个组件是否提供对应的接口呢?看文档?是的,是个好主意,在文档的海洋,找到一个知识点,真的很难,浪费时间和精力;其实,组件本身就提供对自己查询的一个接口,让客户去询问组件,问它是否支持某个接口,在经过多次的这种询问之后,客户对于组件的认识将越来越清晰;而我这篇文章和下一篇文章就是对这种询问机制进行详细的剖析和总结。

关于IUnknown

上面说到组件本身提供一个对自己查询的接口,那么这个接口是什么呢?这就是大名鼎鼎的IUnknown接口了,IUnknown接口在Windows SDK的unknwn.h中定义,它的定义如下:

复制代码 代码如下:

interface IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_  void **ppvObject) = 0;
    virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
    virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
};

这里的STDMETHODCALLTYPE表示调用方式,也就是windows API的__stdcall方式。可以看到,在IUnknown中定义了一个名为QueryInterface的函数。客户可以调用QueryInterface来判断组件是否支持某个特定的接口,而对于剩下的AddRef和Release两个接口操作,我会在之后的文章中进行总结。

所有的COM接口都需要继承IUnknown接口;因此,如果某个客户拥有一个IUnknown接口的指针,它并不需要知道它所拥有的接口指针到底是指向什么类型的,而只需要知道此接口可以用来查询其它接口就行了。

由于所有的COM接口都首先继承了IUnknown,再根据对之前的文章COM编程——接口的背后 的理解,我们可以知道每个接口的vtbl中的前三个函数都是QueryInterface,AddRef和Release。这就使得所有的COM接口都可以被当成IUnknown接口来处理。如果某个接口的vtbl中的前三个函数不是这三个,那么它将不是一个COM接口。由于所有的接口都是从IUnknown继承而来的,因此所有的接口都支持QueryInterface。所以,组件的任何一个接口都可以被客户用来获取它所支持的其他接口。由于所有的接口指针同时也将是IUnknown指针,客户并不需要单独维护一个代表组件的指针,它所关心的将仅仅是接口的指针。

既然,我们可以只用QueryInterface去询问组件是否支持某个接口,但是,这一切都是基于获得了IUnknown接口指针之后,才能进行的操作,那么如何获得一个指向组件的IUnknown接口指针呢?我们可以实现一个CreateInstance函数,它建立一个组件并返回一个IUnknown指针;对于客户来说,可以调用CreateInstance获得IUnknown指针,而不用使用new操作符了。在系统的总结了COM的所有基础知识之后,我再说说系统提供的一个创建组件实例的API函数。

关于QueryInterface

IUnknown中包含一个名为QueryInterface的成员函数,客户可以通过此函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface将返回一个指向此接口的指针;否则返回值将是一个错误代码;然后客户可以接着查询其它接口。

从QueryInterface函数的声明中可以看出,QueryInterface有两个参数,第一个参数标识客户所需的接口,这个参数是一个接口标识符(IID)结构,在之后的文章中,我会总结有关IID的知识的;第二个参数用来存放所请求的接口的地址。QueryInterface返回的是一个HRESULT值,它是一个具有特定结构的32位值,之后我也会进行总结的;对于返回的HRESULT值,在实际开发中,需要使用SUCCEEDED宏或FAILED宏去进行判断HRESULT值是表示成功还是失败。

QueryInterface的简单实现

总结了QueryInterface的简单实现,说白了,就是简单工厂模式的实现;上面也说了,就是根据客户提供的IID接口标识符,然后获得对应的接口的指针,返回对应的接口的指针;如果组件支持客户指定的接口,那么应返回S_OK以及相应的指针;若不支持,返回值应是E_NOINTERFACE,并将相应的指针返回值置成NULL。下面通过一个简单的例子来说明QueryInterface的简单实现:

比如有上述的一个结构;接口IX和IY都继承自IUnknown接口,组件CA实现了IX和IY接口,那么QueryInterface的实现应该像下面这样:

复制代码 代码如下:

HRESULT __stdcall CA::QueryInterface(const IID &iid, void **ppv)
{
     if (iid == IID_IUnknown)
     {
          *ppv = static_cast<IX *>(this);
     }
     else if (iid == IID_IX)
     {
          *ppv = static_cast<IX *>(this);
     }
     else if (iid == IID_IY)
     {
          *ppv = static_cast<IY *>(this);
     }
     else
     {
          *ppv = NULL;
          return E_NOINTERFACE;
     }
     static_cast<IUnknown *>(*ppv)->AddRef();
     return S_OK;
}

QueryInterface的简单使用

当我获得了一个IUnknown指针以后,就可以调用对应的QueryInterface进行查询了,如下:

复制代码 代码如下:

void Fod(IUnknown *pI)
{
     IX *pIX = NULL;
     // Ask for interface IX
     HRESULT hr = pI->QueryInterface(IID_IX, (void **)&pIX);
     // Check the return value
     if (SUCCEEDED(hr))
     {
          // Use the interface
          pIX->Fx();
     }   
}

完整的例子

上面说了那么多了,现在提供一个完整的例子,将上面的各种理论知识都在实际代码中进行了实践,让各位能更好的理解QueryInterface。(下载)。

总结

QueryInterface理解起来比较简单,但是,它的理论知识还是必须要去掌握的,理论是一切的基础,没有理论作为支撑,任何实际的操作都不会那么可靠和可信,所以,这篇文章总结的偏于理论多一些。由于QueryInterface部分的内容比较多,使用一篇文章无法总结的齐全,所以,之后我还会继续总结关于QueryInterface的第二部分。

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多