您的位置:首页 > 产品设计 > UI/UE

COM编程中的接口查询QueryInterface的实现原理

2009-02-14 16:24 218 查看
我们都知道,COM组件编程中,QueryInterface实现的接口之间的查询,通过这个接口,我们可以获取该组件中其他的接口。但是,QueryInterface实现的原理是什么呢,首先,我们看一下基本的COM实现。一般来说,COM是 通过多继承实现多个接口,如下图



而对应的QueryInterface实现如下


HRESULT 实现类::QueryInterface( const IID& iid,void **ppv)




...{


if( iid==IID_IUnknown)


*ppv=(接口一*) this;


else


if( iid== IID_接口一)


*ppv=(j接口一*) this




//其他以此类推


}





在查询IUnknown接口的时候,如下面查询


IUnknwon *Unknown;


实现类.QueryInterface(IID_IUnknown,void(**)Unknown);


//其他以此类推

再看以下代码:

LPDIRECTDRAW lpdd = NULL;
if( FAILED(lpdd->QueryInterface(IID_IDirectDraw7, (LPVOID *) &lpdd)))
{
//error handle and return
}


代码很简单,就是调用了QueryInterface方法去获得DirectDraw7的接口指针。

QueryInterface通过传入的IID参数来判断要查询哪个接口.

因为多个接口的实现是通过多继承实现的。所以,实现类的的内存结构如下图中的this指针数据,

注意,QueryInterface中查询的时候,是将实现类的this指针强制转换成要查询的接口的类型,这个时候在内存中发生的操作是将this的内存块切割成要查询的接口的内存大下,然后将这块内存填入传入的void指针。这就获取了要查询的接口。因为实现类的内存中是包含了要查询的接口的内存结构,因此,上面的操作是可实现的。整个过程如下图



这就是接口查询的实现原理。

至于组件的另外一种基于模板的实现,例如ATL,就比较复杂,

我昨天实现第一个COM对象的时候override的QueryInterface方法:

HRESULT __stdcall ComObject::QueryInterface(const IID &iid, void ** iface)
{
if(iid == IID_IX)
*iface = (InterfaceX*) this;
else
if (iid == IID_IY)
*iface = (InterfaceY*) this;
else
*iface = 0;
return E_NOINTERFACE;
((IUnknown*) (*iface))->AddRef();
return (S_OK);
}

我觉得别扭是因为第二个参数少了个dereference操作符,尽管LPVOID就是void *,但是通过使用typedef关键字将其定义为LPVOID,确实让代码可读性更高了。这是我想到第一个问题;
接着就是一个感觉很低级的问题了:QueryInterface为什么要使用指针的指针作为参数呢?直接用指针不行吗?如果仅仅是为了在函数体中能够改变参数,使用指针就足够了。那是因为什么呢?我们从如下形式的QueryInterface方法来看:

HRESULT QueryInterface( REFIID iid, void ** ppvObject);
HRESULT QueryInterface( REFIID riid,LPVOID *ppvObj);

第一,ppvObject这个参数的含义是接口指针的地址(Address of a pointer to fill with the interface

pointer)。从这个角度而言,第二种函数形式就更为容易理解了,由于"*"为解引用操作符,作用于ppvObject,得到的类型正好就是一个接口指针了(与LPVOID相对应);
第二,QueryInterface如果执行成功,就会将相应的接口指针赋给参数*ppvObject,如果仅仅是传递接口指针本身,而非接口指针的地址,就无法在函数体中对其进行赋值了。大家看以下代码:

class X
{
public:
virtual void sayHi();
};

class Y:public X
{
public:
void sayHi();
};

(Test.cpp)

#include <iostream>
#include "Test.hpp"

using namespace std;

void X::sayHi()
{
cout << "Hi, My name is X." << endl;
}

void Y::sayHi()
{
cout << "Hi, My name is Y." << endl;
}

void change(X *x)
{
x = new Y;
}

void main()
{
X* a = new X;
a->sayHi();
change(a);
a->sayHi();
}

大家会觉得输出是什么呢?我们所期待的Y给大家的问候语并没有出现,就剩下X在唱独角戏。不管如何折腾,Y就是不会出现。如果使用了指针的指针,我们就可以得到体现多态的结果了。
PS:其实这是一个很简单的问题,但是通过摸索让自己对双重指针有了进一步的认识,以前就知道双重指针的定义就是指针的指针,至于它为什么会存在,一点想法都没有。在瞎胡闹的过程中,同时还强化了一个概念,就是运算符的优先级,类型转化的优先级要比"->"和"."要低。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: