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

来源:互联网 发布:青岛工资知乎 编辑:程序博客网 时间:2024/06/07 17:34

第三章

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


客户同组件交互都是通过接口完成的。在客户程序查询组件的其他接口时,也是通过接口完成的,因此每一个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;}//Interfacesinterface 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 ;//Componentclass 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 functionIUnknown *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有几条规则,需要看一下。


原创粉丝点击