com技术内幕--读书笔记(3)

第三章

本章讨论了客户如何向组件询问它所支持的接口,组件如何回答,以及这种请求应答方式的结果。


客户同组件交互都是通过接口完成的。在客户程序查询组件的其他接口时,也是通过接口完成的,因此每一个COM组件必须实现一个共同的接口,供客户程序和组件通信,

这个接口就是IUnknown。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;
}

所有的COM接口都必须继承并实现IUnknown接口,每个COM接口的前三个函数都是QueryInterface,AddRef,Release,如果不是的话,它就不是一个COM接口。因为接口

都是在COM组件的内部实现的,因此客户程序可以使用任何一个接口的QueryInterface函数来获取组件所支持的其它接口(例如可通过IX接口获取同一个组件实现的IY接口)。


IX接口的内存图





QueryInterface的第一个参数是接口标识符IID,第二个参数用来返回所查询的接口指针,HRESULT返回值使用SUCCEEDED或FAILED宏验证是否成功。


在本章,作者在客户端使用了IUnknown *CreateInstance()函数来获取创建并获取组件的IUnkown接口指针。这并不是建立COM组件的真正方法,第6,7章会介绍到。本章的例子分为三个部分,组件的接口定义,组件的实现,main函数客户部分。


//IUnkown.cpp
//use:cl IUnkown.cpp UUID.lib
//
#include <iostream>
#include <string>
#include <objbase.h>
using namespace std;

void trace(string msg)
{
	cout<<msg<<endl;
}

//Interfaces
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 ;
//Component
class CA:public IX, public IY
{
	virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
	virtual ULONG   __stdcall AddRef() 
	{
		return 0;
	}
	virtual ULONG   __stdcall Release()
	{
		return 0;
	}

	virtual void __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)
	{
		trace("Return pointer to IUnkown");
		*ppv = static_cast<IX*>(this);
	}
	else if(iid == IID_IX)
	{
		trace("pointer to IX");
		*ppv = static_cast<IX*>(this);
	}
	else if(iid == IID_IY)
	{
		trace("return pointer to IY");
		*ppv = static_cast<IY*>(this);
	}
	else
	{
		trace("Interface not supported");
		*ppv = NULL;
		return E_NOINTERFACE;
	}
	reinterpret_cast<IUnknown*>(*ppv)->AddRef();
	return S_OK;
}

//Create function
IUnknown *CreateInstance()
{
	IUnknown *pI = static_cast<IX*>(new CA);
	pI->AddRef();
	return pI;
}

// IIDs
// {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}} ;


///
//Client
///
int main(void)
{
	HRESULT hr;
	trace("Client: Get Iunkown pointer");
	IUnknown *pIunknown = CreateInstance();

	trace("Client: Get interface IX");
	IX *pIx = NULL;
	hr = pIunknown->QueryInterface(IID_IX, (void**)&pIx);
	if(SUCCEEDED(hr))
	{
		trace("Client:Succeeded get IX");
		pIx->Fx();
	}

	trace("Client: Get interface IY");
	IY *pIy = NULL;
	hr = pIunknown->QueryInterface(IID_IY, (void**)&pIy);
	if(SUCCEEDED(hr))
	{
		trace("Client: Succeeded get IY");
		pIy->Fy();
	}

	trace("Client: Ask for an unsupported interface");
	IZ *pIz = NULL;
	hr = pIunknown->QueryInterface(IID_IZ, (void**)&pIz);
	if(SUCCEEDED(hr))
	{
		trace("Client: Succeeded get IZ");
		pIz->Fz();
	}
	else
	{
		trace("Client: Could not get interface IZ");
	}

	trace("Client: Get interface IUnkown from IY");
	IY *pIyFromIx = NULL;
	hr = pIx->QueryInterface(IID_IY, (void**)&pIyFromIx);
	if(SUCCEEDED(hr))
	{
		trace("Client: Succeeded get Iy");
		pIyFromIx->Fy();
	}

	IUnknown *pIunknownFromIy = NULL;
	hr = pIy->QueryInterface(IID_IUnknown, (void**)&pIunknownFromIy);
	if(SUCCEEDED(hr))
	{
		if(pIunknownFromIy == pIunknown)
		{
			cout<<"pIupIunkownFromIy == pIunkown"<<endl;
		}
		else
		{
			cout<<"pIupIunkownFromIy != pIunkown"<<endl;
		}
	}
	delete pIunknown;

	return 0;
}

运行结果:




本章有几个需要注意的问题:

1.class CA: public IX, public IY

这里不能用虚拟多重继承的方式,因为这样会破坏接口的内存结构。同时static_cast<IUnknown*>(this)是不确定的,因为IX和IY接口都是从IUnknown继承得到的。

CA的内存结构如下:


这里面涉及到一个C++类的内存布局,vtbl指针在类的内存中是排在最前面的。从图中也可以发现,IX接口的IUnknown跟继承自IY接口的IUnknown的地址是不一样的,static_cast<IUnknow*>(this)是不明确的。但是它们存放QueryInterface,AddRef,Release函数地址是相同的

2.返回接口集的问题

对客户来说,它只需要关心它需要的组件接口,返回组件所支持的所有接口并没有必要。但是QueryInterface一个一个的查询,比较耗费时间,为此,可以使用一个组件类别来标识一个接口集,将在第6章中详细讨论。

3.接口升级的问题

一个IID对应一个接口,如果需要添加或者更改老接口中的函数,需要重新定义接口,并指定一个新的IID,而不是在老接口中添加函数,新接口可以继承自老接口。新接口的命名最好是老接口名字后加上数字,例如IFly,新接口就是IFly2。

4.QueryInterface有几条规则,需要看一下。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值