Transcript
Page 1: Component Object Model  COM 组件对象模型

Component Object Model Component Object Model

COMCOM

组件对象模型组件对象模型

Page 2: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 3: Component Object Model  COM 组件对象模型

应用程序应用程序• 一般由单个二进制文件组成• 编译后至重新编译和发行生成新下一版本

前,一般不会变化• 操作系统、硬件和客户需求的改变必须等

到整个应用程序被重新编译后才能认可

• 解决方案– 将单个的应用程序分隔成多个独立部分-组件

Page 4: Component Object Model  COM 组件对象模型

显而易见的优点显而易见的优点• 随着技术的不断发展用新的组件取代已有的组件• 程序随着新组件不断取代旧的组件而趋于完善• 用已有的组件建立全新的应用

组件 A 组件 B

组件 C 组件 D

组件 E

组件应用程序

组件 A 组件 B

组件 C 新改进后的组件 D

组件 E

改进后的组件应用程序

Page 5: Component Object Model  COM 组件对象模型

比较比较• 传统的做法

– 应用程序分割成文件、模块或类– 编译并链接成一个铁板一块般的应用程序

• 组件建立的应用程序– 微型应用程序:每个部分都已经编译、链接好

可以使用的– 可以在运行时同其它组件连接。– 需要修改和改进时,只需将某个构成组件用新

的版本替换

Page 6: Component Object Model  COM 组件对象模型

COMCOM

• Component Object Model

• 组件对象模型,是关于如何建立组件以及如何通过组件构建应用程序的一个规范。

• 开发 COM 的目标是为了使应用程序更易于定制、更为灵活。– OLE1 DDE– OLE2 COM

Page 7: Component Object Model  COM 组件对象模型

使用组件的优点使用组件的优点• 应用程序定制• 组件库• 分布式组件

Page 8: Component Object Model  COM 组件对象模型

对组件的需求对组件的需求• 组件的优点直接来源于可以动态地将它们

插入或卸出。• 所有组件必须满足两个条件:

– 组件必须动态连接• 目标是为在应用程序运行的过程中替换组件

– 组件必须封装其内部实现细节• 保持同样的连接方式

• 客户通过接口同其它组件进行连接

Page 9: Component Object Model  COM 组件对象模型

封装对组件的限制封装对组件的限制• 组件必须将其实现所用的编程语言封装起

来• 组件必须以二进制的形式发布• 组件必须可以在不妨碍已有用户的情况下

被升级• 组件在网络上的位置可以被透明地重新分

Page 10: Component Object Model  COM 组件对象模型

限制的考虑限制的考虑• 语言的无关性

– 内部: Object C , C++ , EspressoBeans– 市场: VB only ,对手任何语言编写组件

• 版本– 向后兼容的能力– 改变某个组件的功能以使之适应新应用程序的

需要、并同时支持老的应用程序

Page 11: Component Object Model  COM 组件对象模型

COMCOM 组件组件• COM 规范就是一套为组件架构设置标准的

文档• COM 组件是…

– COM 组件以动态链接库或可执行文件的形式发布的可执行代码组成。

– COM 组件满足下面的限制条件• COM 组件是完全与语言无关的• COM 组件可以以二进制的形式发布• COM 组件在不妨碍已有用户的情况下被升级• COM 组件在网络上的位置可以被透明地重新分布

Page 12: Component Object Model  COM 组件对象模型

COMCOM 组件组件• COM 组件不是…

– COM 不是计算机语言– COM 与 DLL相比或相提并论不合适– COM 不是一个 API 函数集合

• COM 库– COM具有一个被称为 COM 库的 API

• 提供组件管理服务• 保证对所有组件大多数重要的操作都可以按照相同

的方式完成• 节省开发和实现时间,支持分布式或网络化

Page 13: Component Object Model  COM 组件对象模型

COMCOM 综述综述• 提供了一个所有组件都应遵守的标准• 允许使用组件的多个不同版本,对用户几乎透明• 使得可以按相同的方式来处理类似的组件• 定义了一个与语言无关的框架• 支持对远程组件的透明链接

• COM强制开发人员必须将客户和组件严格地隔离开

Page 14: Component Object Model  COM 组件对象模型

接口的作用接口的作用• 在 COM 中接口就是一切• 对客户来说,一个组件就是一个接口集• 客户只能通过接口才能与 COM 组件交互

Page 15: Component Object Model  COM 组件对象模型

接口对接口对 COMCOM 程序的重要性程序的重要性• 可复用应用程序结构

A C

B D

E

接口A-C

接口B-D

接口 A-B

接口 B-E

接口 C-D

接口 D-E

Page 16: Component Object Model  COM 组件对象模型

接口的简单实现接口的简单实现class IX{public:

virtual void Fx1()=0;virtual void Fx2()=0;

};class IY{public:

virtual void Fy1()=0;virtual void Fy2()=0;

};class CA: public IX,public IY{

virtual void Fx1(){cout<<“Fx1”<<endl;}virtual void Fx2(){cout<<“Fx2”<<endl;}virtual void Fy1(){cout<<“Fy1”<<endl;}virtual void Fy2(){cout<<“Fy2”<<endl;}

};

Page 17: Component Object Model  COM 组件对象模型

说明说明• 对纯虚函数的继承被称为接口继承

–派生类所继承的只是基类对函数的描述–抽象类没有提供任何可供继承的实现细节

• 注意!– COM 是与语言无关的,对于接口,它有一个二

进制的标准–表示 COM 接口的内存块必须具有一定的结构– 必须继承 IUnknown 接口

Page 18: Component Object Model  COM 组件对象模型

约定约定# define interface struct

#include <objbase.h>interface IX{

virtual void _stdcall Fx1()=0;virtual void _stdcall Fx2()=0;

}interface IY{

virtual void _stdcall Fy1()=0;virtual void _stdcall Fy2()=0;

}

IXIX

IYIY

组件图形化表示

Page 19: Component Object Model  COM 组件对象模型

模拟实现模拟实现interface IX{…} ;interface IY{…} ;class CA:public IX,public IY{…};main{

CA *pA=new CA;IX* pIX=pA;pIX->Fx1();PIX->Fx2();

IY* pIY=pA;pIY->Fy1();pIY->Fy2();delete pA;return 0;

}

Page 20: Component Object Model  COM 组件对象模型

代码分析代码分析• 非接口通信

– 指向 CA 的指针– 使用了 new 和 delete控制组件的生命周期 C++

• 实现细节– 类并非组件– 接口并非总是继承的– 多重接口及多重继承 系统-组件-接口-函数– 命名冲突

• COM 接口是一个二进制标准• 客户同接口的连接不是通过其名称或其成员函数的名称完成的,而是

通过在内存块中的位置完成的。• 解决冲突的方法是不使用多重继承,可以合用指向实现的指针

Page 21: Component Object Model  COM 组件对象模型

接口理论接口理论• 接口的不变性

– 一旦公布了一个接口,那么它将永远保持不变– 升级时一般不修改已有接口,而是加入新接口

• 多态–按照同一种方式处理不同的对象

• 接口表示的行为越多,它的特定性越强,被其它组件复用的可能性越小

Page 22: Component Object Model  COM 组件对象模型

虚拟函数表虚拟函数表

vtbl指针vtbl指针&Fx2

&Fx3

&Fx4

&Fx1pIX

interface IX{

virtual void _stdcall Fx1()=0;

virtual void _stdcall Fx2()=0;

virtual void _stdcall Fx3()=0;

virtual void _stdcall Fx4()=0;

}虚拟函数表

IX

Page 23: Component Object Model  COM 组件对象模型

虚拟函数表与接口虚拟函数表与接口• 定义纯抽象基类就定义了相应的内存结构• 只有派生类实现抽象基类时才会分配

• COM 接口内存结构与 C++ 编译器为抽象基类生成的内存结构相同-巧合– IX即是一个接口– IX也是一个抽象基类

Page 24: Component Object Model  COM 组件对象模型

实例数据实例数据class CA: public IX{

virtual void _stdcall Fx1(){cout<<“Fx1”<<endl;}virtual void _stdcall Fx2(){cout<<m_Fx2<<endl;}virtual void _stdcall Fx3(){cout<< m_Fx3 <<endl;}virtual void _stdcall Fx4(){cout<< m_Fx4 <<endl;}

CA(double d):m_Fx2(d*d), m_Fx3(d*d*d) , m_Fx4(d*d*d*d) { }

double m_Fx2;double m_Fx3;double m_Fx4;

};

Page 25: Component Object Model  COM 组件对象模型

pApA

vtblvtbl 指针及实例数据指针及实例数据

vtbl指针

&Fx2

&Fx3

&Fx4

&Fx1

虚拟函数表

IX

&m_Fx2

&m_Fx3

&m_Fx4

Fx2

Fx3

Fx4

Fx1

客户

Page 26: Component Object Model  COM 组件对象模型

共享共享 vtblvtbl 实例数据实例数据

int main ()

{

CA* pA1=new CA(1.5);

CA* pA2=new CA(2.75);

}

Page 27: Component Object Model  COM 组件对象模型

pA1

pA2

pA1

pA2

多重实例多重实例vtbl指针

&Fx2

&Fx3

&Fx4

&Fx1

虚拟函数表&m_Fx2

&m_Fx3

&m_Fx4

Fx2

Fx3

Fx4

Fx1

客户

vtbl指针

&m_Fx2

&m_Fx3

&m_Fx4

Page 28: Component Object Model  COM 组件对象模型

不同类实例不同类实例class CB: public IX{

virtual void _stdcall Fx1(){cout<<“CB::Fx1”<<endl;}virtual void _stdcall Fx2(){cout<<“CB:: Fx2”<<endl;}virtual void _stdcall Fx3(){cout<< “CB:: Fx3”<<endl;}virtual void _stdcall Fx4(){cout<< “CB:: Fx4”<<endl;}

}

void foo(IX * pIX){pIX->Fx1();pIX->Fx2();

}

int main(){CA* pA=new CA(1.77);CB* pB=new CB;IX* pIX=pA;foo(pIX);

pIX=pB;foo(pIX);

};

Page 29: Component Object Model  COM 组件对象模型

pA1

pA2

pA1

pA2

多重实例多重实例vtbl指针

&Fx2

&Fx3

&Fx4

&Fx1

虚拟函数表

&m_Fx2

&m_Fx3

&m_Fx4

Fx2

Fx3

Fx4

Fx1客户

vtbl指针

&m_Fx2

&m_Fx3

&m_Fx4

&Fx2

&Fx3

&Fx4

&Fx1

虚拟函数表

Fx2

Fx3

Fx4

Fx1

Page 30: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 31: Component Object Model  COM 组件对象模型

接口查询接口查询• 客户同组件的交互都是通过接口完成的• 客户查询组件的其它接口时也要通过接口• IUnknown UNKNWN.Hinterface IUnknown{

virtual HRESULT _stdcall QueryInterface (const IID& iid, void** ppv)=0;

virtual ULONG _stdcall AddRef()=0;virtual ULONG _stdcall Release()=0;

};

Page 32: Component Object Model  COM 组件对象模型

IUnknownIUnknown

• 所有的 COM 接口都要继承 IUnknown• 所有 COM 接口都可以当作 IUnknown 接口处理• 组件的任何一个接口都可以用来获取它所支持的

其它接口。

pIXpIX vtbl指针vtbl指针

AddRef

Release

Fx

QueryInterface

虚拟函数表

IX

AddRef

Release

Fx

QueryInterface

客户 CA

Page 33: Component Object Model  COM 组件对象模型

获取获取 IUnknownIUnknown 指针指针• 自定义函数 (非标准 )

IUnknown* CreateInstance(){

IUnknown* pI=static_cast<IX*>(new CA);pI->AddRef();return pI;

}

– 标准方式HRESULT _stdcall CoCreateInstance(…);

Page 34: Component Object Model  COM 组件对象模型

QueryInterfaceQueryInterface

• 查询某个组件是否支持某个特定的接口virtual HRESULT _stdcall QueryInterface

(const IID& iid, void** ppv);

• IID :接口标识符{0x32bb8320,0xb41b,0x11cf,{0xa6,0xbb,0x0,0x80,0xc7,xb2,0xd6,0x82}}

• ppv :请求的接口指针地址• HRESULT : 32 位结构

– S_OK– E_NOINTERFACE– 宏 SUCCEEDED 和 FAILED处理结果

Page 35: Component Object Model  COM 组件对象模型

使用使用 QueryInterfaceQueryInterface

void foo(IUnknown pI){

IX* pIX=NULL;

HRESULT hr=pI->QueryInterface(IID_IX,(void**) &pIX);

if (SUCCEEDED(hr)){

pIX->Fx();}

}

Page 36: Component Object Model  COM 组件对象模型

实现类及其接口的继承关系实现类及其接口的继承关系

interface IX: IUnknown {/*…*/ };

interface IY: IUnknown {/*…*/ };

class CA: public IX, public IY {/*…*/ };

IXIX IYIY

IUnknownIUnknown IUnknownIUnknown

CACA

Page 37: Component Object Model  COM 组件对象模型

实现实现 QueriInterfaceQueriInterface

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();reurn S_OK;

}

Page 38: Component Object Model  COM 组件对象模型

类型转换类型转换• 将 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

• 不使用如下转换*ppv= static_cast<IUnknown*>(this);

Page 39: Component Object Model  COM 组件对象模型

多重继承的内存结构多重继承的内存结构

IX vtbl指针

CA 实例数据 AddRef

Release

Fx

QueryInterface

AddRef

Release

Fy

QueryInterface

IY vtbl指针

虚拟函数表CA::this

(IX*)CA::this(IY*)CA::this

ΔIY

IX

IY

CA

void foo(IX* pIX);void bar(IY* pIY)int main(){

CA* pA=new CA;foo(pA);bar(pA);delete pA;return 0;

}

Page 40: Component Object Model  COM 组件对象模型

QueryInterfaceQueryInterface 的实现规则的实现规则• 1.QueryInterface返回的总是同一 IUnknown指针

• 2.若客户曾经获取过某个接口,那么它将总能获取此接口

• 3. 客户可以再次获取已经拥有的接口• 4. 客户可以返回到起始接口• 5.若能够从某个接口获取某特定接口,那么从任意接口都将可以获取此接口

Page 41: Component Object Model  COM 组件对象模型

同一同一 IUnknownIUnknown

• 组件的实例只有一个 IUnknown 接口• 为确定两个接口是否指向同一组件,可以通过这两个接口查询 IUnknown 接口,比较返回值

BOOL SameComponent(IX* pIX, IY* pIY){IUnknown* pI1=NULL;IUnknown* pI2=NULL;

pIX->QueryInterface(IID_IUnknown,(void**) &pI1);pIY->QueryInterface(IID_IUnknown,(void**) &pI2);

return pI1==pI2;}

Page 42: Component Object Model  COM 组件对象模型

可以获取曾经得到的接口可以获取曾经得到的接口• 如果 QueryInterface曾经成功过,那么同

一组件的后续 QueryInterface 将总是成功• 如果 QueryInterface失败,那么后续调用

将失败

• 保证接口集不会发生变化

Page 43: Component Object Model  COM 组件对象模型

可以再次获取已经拥有的接口可以再次获取已经拥有的接口• 拥有 IX 接口,查询 IX 接口• 所有接口都继承了 IUnknown ,许多函数需要一个 IUnknown指针

作为参数

void f(IUnknown* pI){IX* pIX=NULL;HRESULT hr=pIX->QueryInterface(IID_IX, (void**) &pIX);…

}void main(){

IX* pIX=GetIX();f(pIX);

}

Page 44: Component Object Model  COM 组件对象模型

可以从任何接口返回起始接口可以从任何接口返回起始接口void f(IX* pIX){

HRESULT hr;IX* pIX2=NULL;IY* pIY=NULL;hr=pIX->QueryInterface(IID_IY, (void**) &pIY);if (SUCCEEDED(hr)){

hr=pIX->QueryInterface(IID_IX, (void**) &pIX2);}…

}

Page 45: Component Object Model  COM 组件对象模型

若能够获取某特定接口,那么从任若能够获取某特定接口,那么从任意接口都将可以获取此接口意接口都将可以获取此接口

• 若可以通过接口 IX得到 IY , IY得到 IZ ,那么通过 IX也将得到 IZ

• 防止顺序依赖

• 以上规则的目标是为了是 QI 使用更简单、更富逻辑性、一致性以及确定性

Page 46: Component Object Model  COM 组件对象模型

QueryInterfaceQueryInterface 定义了组件定义了组件• 组件所支持的接口集就是 QueryInterface

能够为之返回接口指针的那些接口• 客户了解组件所支持接口的唯一方法是进

行查询• DCOM 中提供 IMultiQI

Page 47: Component Object Model  COM 组件对象模型

新版本组件的处理新版本组件的处理• COM 中接口不会变化,具有唯一接口标识符( II

D)• 可以建立新接口,并指定新的 IID• 无缝处理多个版本

– QI收到老 IID查询时返回老接口– 收到新 IID 时返回新接口

• 客户方面– 已有的客户运行不受影响– 新客户可以自行决定使用老的或新的接口

• 接口的标识同其版本是完全绑在一块的

Page 48: Component Object Model  COM 组件对象模型

新老版本组件组合新老版本组件组合

pIFlypIFly

pIFly

pIFlyFast

pIFly

pIFlyFast

IFlyIFly

IFlyFastIFlyFast

FastBronco

IFlyIFly

Bronco

Page 49: Component Object Model  COM 组件对象模型

何时建立新版本何时建立新版本• 接口中函数的数目• 接口中函数的顺序• 某个函数的参数• 某个函数参数的顺序• 某个函数产生的类型• 函数可能的返回值• 函数返回值的类型• 函数参数的含义• 接口中函数的含义

– 只要修改会导致已有客户的正常运行

Page 50: Component Object Model  COM 组件对象模型

版本约定版本约定• 新版本命名方式约定

– 在老版本后加一个数字 , IFly2

• 隐含合约– 仅保证函数名称及参数不变不足以保证修改组件不会

妨碍客户正常运行– 客户也许按照一定的方式或次序使用接口中函数– 所有的接口都有隐含合约– 避免隐含合约

• 使得接口不论成员函数如何被调用都能工作• 强制客户按一定的方式来使用此接口并在文档中声明这一点

Page 51: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 52: Component Object Model  COM 组件对象模型

生命周期控制生命周期控制• 客户不应直接控制组件的生命周期

– 使用完一个接口而仍要使用另一个接口时,不能释放组件

– 很难知道两个接口指针是否指向同一个对象– 程序越来越复杂时,决定何时释放组件是极为复杂的

• COM采用的方法– 通知组件何时需要使用它的某个接口以及何时使用完,

不是直接将接口删除– 组件的释放可以由组件在客户使用完其各个接口后自己完成

• 一般能够精确地知道何时开始使用一个接口• 大多数情况下可以精确地知道何时将用完此接口• 使用完接口后给组件发出指示比使用完整个接口更有意义

Page 53: Component Object Model  COM 组件对象模型

引用计数规则引用计数规则• COM 组件维护一个称作引用计数的数值• 基本规则

–返回接口指针的函数,在返回之前调用 AddRef 。

• 包括 QueryInterface 和 CreateInstance• 客户从这样的函数得到接口后,无须调用 AddRef

– 使用完接口之后调用 Release 。– 在赋值之后调用 AddRef 。

Page 54: Component Object Model  COM 组件对象模型

引用计数例引用计数例IUnknown* pIUnknown=CreateInstance();IX* pIX=NULL;HRESULT hr=

pIUnknown->QueryInterface(IID_IX, (void**)&pIX);

If (SUCCEEDED(hr)){

pIX->Fx();pIX->Release();

}

pIUnknown->Release();

IUnknown* pIUnknown=CreateInstance();IX* pIX=NULL;

HRESULT hr=pIUnknown->QueryInterface(IID_IX, (void**)&pIX);

pIUnknown->Release();

If (SUCCEEDED(hr)){

pIX->Fx();pIX->Release();

}

Page 55: Component Object Model  COM 组件对象模型

引用规则例引用规则例IUnknown* pIUnknown=CreateInstance();IX* pIX=NULL;HRESULT hr=

pIUnknown->QueryInterface(IID_IX, (void**)&pIX);pIUnknown->Release();If (SUCCEEDED(hr)){

pIX->Fx();IX* pIX2=pIX;pIX2->AddRef();pIX2->Fx();pIX2->Release();pIX->Release();

}

Page 56: Component Object Model  COM 组件对象模型

引用计数接口引用计数接口

• 只要能够使客户相信组件将记录每个接口本身维护引用计数值,实现方法无关紧要– 每个接口分别维护一个引用计数– 整个组件维护单个引用计数

IXIX

IYIY

组件

引用计数

IUnknownIUnknownIX

引用计数IX

引用计数

IY引用计数IY

引用计数

组件

IUnknown引用计数IUnknown

引用计数客户

Page 57: Component Object Model  COM 组件对象模型

• 客户要注意组件可能会为每个接口维护一个引用计数

IUnknown* pIUnknown=CreateInstance();IX* pIX=NULL;HRESULT hr=

pIUnknown->QueryInterface(IID_IX, (void**)&pIX);pIX->Fx();pIX* pIX2=pIX;pIUnknown->AddRef(); //Should be pIX->AddRef();pIX2->Fx();pIX2->Release();pIUnknown->Release(); //Should be pIX->Release();pIUnknown->Release();

Page 58: Component Object Model  COM 组件对象模型

每个接口一个引用计数每个接口一个引用计数• 原因

– 使程序调试更为方便– 支持资源的按需获取

• 在单独的组件中实现需要大量资源的接口

Page 59: Component Object Model  COM 组件对象模型

AddRefAddRef 和和 ReleaseRelease 实现实现ULONG _stdcall AddRef(){

return ++m_cRef; //InterlockedIncrement(&m_cRef);}

ULONG _stdcall Release(){if (--m_cRef==0){ //InterlockedDecrement(&m_cRef);

delete this;return 0;

}return m_cRef;

}返回值没有意义,客户不应将此值当成精确的引用计数

Page 60: Component Object Model  COM 组件对象模型

引用计数的优化引用计数的优化IUnknown* pIUnknown=CreateInstance();IX* pIX=NULL;HRESULT hr=

pIUnknown->QueryInterface(IID_IX, (void**)&pIX);pIUnknown->Release();

If (SUCCEEDED(hr)){pIX* pIX2=pIX;pIX2->AddRef();pIX->Fx();pIX2->Fx();pIX2->Release();pIX->Release();

}

Page 61: Component Object Model  COM 组件对象模型

生命周期嵌套生命周期嵌套组件 IUnknown pIX pIX2操作

CreateInstance

QueryInterface

pIUnknown->Release()

pIX2->AddRef()

pIX2=pIX

pIX2-

>Release(

)

pIX->Release()

Page 62: Component Object Model  COM 组件对象模型

一些情况一些情况• 函数中,无需对存在于局部变量的接口指针进行

引用计数– 包含在调用者的生命周期中void foo(pIX* pIX2){

pIX2->Fx();}

• 全面变量间进行指针赋值时,需要对该指针进行引用计数– 全局变量可以在任何地方释放

Page 63: Component Object Model  COM 组件对象模型

引用计数规则引用计数规则• 输出参数规则

– 任何在输出参数中或作为返回值返回一个新的接口指针的函数必须对此接口指针调用 AddRef

• 输入参数规则– 对传入函数的接口指针,无需调用 AddRef 和 Release

• 输入-输出参数规则– 对传递进来的接口指针必须给它赋另外一个接口指针前调用 Release– 在函数返回前,还必须对输出参数中保存的接口指针调用 AddRef

• 局部变量规则– 在函数的生命周期内,无需调用 AddRef 和 Release

• 全局变量规则– 在将其传递给另外一个函数之前,必须调用其 AddRef

• 不能确定时的规则– 都应调用 AddRef 和 Release

Page 64: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 65: Component Object Model  COM 组件对象模型

组件的创建组件的创建• 一个组件实际上不是 DLL

• 组件实际上应看成是 DLL 中所实现的接口集

• 客户在可以获取某个组件接口指针前,必须将 DLL 装载到进程空间中并创建此组件

Page 66: Component Object Model  COM 组件对象模型

从从 DLLDLL 中输出函数中输出函数extern “C”IUnknown* CreateInstance(){

IUnknown* pI=(IUnknown*)(void**)new CApI->AddRef();return pI;

}

Page 67: Component Object Model  COM 组件对象模型

模块定义文件模块定义文件CMPNT1.DEF

;

;Compnt1 module-definition file.

;

LIBRARY compnt1.dll

DESCRIPTION ‘(c) 2001-2005 cs’

EXPORTS

CreateInstance@1 PRIVATE

Page 68: Component Object Model  COM 组件对象模型

DLLDLL 装载装载typedef IUnknown* (*CREATEFUNCPTR)();IUnknown* CallCreateInstance(char* name){

HINSTANCE hComponent=::LoadLibrary(name);if (hComponent==NULL){

cout<<“CallCreatInstance:Cannot load.”<<endl;return NULL;}CREATEFUNCPTR CreateInstance

=(CREATEFUNCPTR)::GetProcAddress(hComponent, “CreateInstance”);

if (CreateInstance==NULL){cout<<“Can not find CreateInstance function.”<<endl;return NULL;

}return CreateInstance();

}

COM 库函数 CoCreateInstance

Page 69: Component Object Model  COM 组件对象模型

进程空间进程空间

DLL3 映象内存

进程 2

应用程序映象内存

DLL1 映象内存

DLL2 映象内存

进程 1

应用程序映象内存

DLL2 映象内存

DLL4 映象内存

未映象区域 未映象区域

4G

B

4G

B

Page 70: Component Object Model  COM 组件对象模型

客户和组件文件构成客户和组件文件构成

CLIENT1.CPP

CREATE.HCREATE.CPP

IFACE.HGUIDS.CPP

CMPNT1.CPP

CMPNT1.DEF

客户文件 共享文件 组件文件

Page 71: Component Object Model  COM 组件对象模型

GUID-Globally Unique IdentifierGUID-Globally Unique Identifier

• IID 实际是 128 比特( 16字节)的 GUID结构

• GUID最初由 OSF 定义的( UUID)

Page 72: Component Object Model  COM 组件对象模型

COMCOM 库函数库函数• HRESULT CoInitialize(void* reserved);

• void CoUninitialize();

Page 73: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 74: Component Object Model  COM 组件对象模型

CoCreateInstanceCoCreateInstance

HRESULT _stdcall CoCreateInstance(

const CLSID& clsid,

IUnknown* pIUnknownOuter,

DWORD dwClsContext,

const IID& iid,

void** ppv);

Page 75: Component Object Model  COM 组件对象模型

CoCreateInstanceCoCreateInstance 例例IX* pIX=NULL;HRESULT hr=::CoCreateInstance(CLSID_Compnt1,

NULL,CLSCTX_INPROC_SERVER,IID_IX,(void**)&pIX);

If (SUCCEEDED(hr)){pIX->Fx();pIX->Release();

}

Page 76: Component Object Model  COM 组件对象模型

类上下文类上下文• CLSCTX_INPROC_SERVER

• CLSCTX_INPROC_HANDLER

• CLSCTX_LOCAL_SERVER

• CLSCTX_REMOTE_SERVER

Page 77: Component Object Model  COM 组件对象模型

类厂类厂• 没有给客户提供一种能够控制组件创建过程的方

法• 在建立好一个组件之后,无法控制将组件装载到

内存中何处或检查客户是否有权限。

• 客户可以通过类厂组件创建其它组件• 类厂组件的唯一功能就是创建其它的组件• 创建组件的标准接口是 IClassFactory

– 用 CoCreateInstance创建的组件实际上是通过 IClassFactory创建的。

Page 78: Component Object Model  COM 组件对象模型

CoGetClassObjectCoGetClassObject

• 创建某个 CLSID相应的类厂

HRESULT _stdcall CoGetClassObject(const CLSID& clsid,DWORD dwClsContext,COSERVERINFO* pServerInfo, //for DCO

Mconst IID& iid,void** ppv

);

Page 79: Component Object Model  COM 组件对象模型

IClassFactoryIClassFactory

Interface IClassFactory: IUnknown{

HRESULT _stdcall CreateInstance(IUnknown* pUnknownOuter,const IID& iid,void** ppv);

HRESULT _stdcall LockServer(BOOL bLock);};

Page 80: Component Object Model  COM 组件对象模型

CreateInstanceCreateInstance 实现实现HRESULT _stdcall CFactory::CreateInstance(

IUnknown* pUnknownOuter,

const IID& iid,

void** ppv){

CA* pA=new CA;

if (pA==NULL){

return E_OUTOFMEMORY;

}

HRESULT hr=pA->QueryInterface(iid,ppv);

pA->Release();

return hr;

}

Page 81: Component Object Model  COM 组件对象模型

实现方式实现方式HRESULT CoCreateInstance(

const CLSID& clsid, IUnknown* pUnknownOuter,

DWORD dwClsContext, const IID& iid, void** ppv)

{*ppv=NULL;IClassFactory* pIFactory=NULL;HRESULT hr=CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void**)&pIFactory);if (SUCCEEDED(hr)){

hr=pIFactory->CreateInstance(pUnknownOuter, idd, ppv);pIFactroy->Release();}return hr;

}

Page 82: Component Object Model  COM 组件对象模型

使用使用 CoGetClassObjectCoGetClassObject

• 使用不同于 IClassFactory 的某个接口来创建组件

• 创建同一个组件的多个实例• 客户对组件的创建过程进行更多的控制

Page 83: Component Object Model  COM 组件对象模型

类厂的特性类厂的特性• 类厂的一个实例将只能创建同某个 CLSID相应的组件

• 与某个特定的 CLSID相应的类厂将是由实现组件的开发人员实现的

• 类厂可以知道并确实具有它所创建的组件的一些特殊知识

Page 84: Component Object Model  COM 组件对象模型

DLLGetClassObjectDLLGetClassObject

STDAPI DLLGetClassObject(

const CLSID& clsid,

const IID& iid,

void** ppv);

Page 85: Component Object Model  COM 组件对象模型

创建类厂创建类厂STDAPI DLLGetClassObject( const CLSID& clsid,

const IID& iid,void** ppv)

{if (clsid!=CLSID_Compnt1){

return CLASS_E_CLASSNOTAVAILABLE;}CFactory* pFactory=new CFactory;if (pFactory==NULL){

return E_OUTOFMEMORY;}HRESULT hr=pFactory->QueryInterface(iid,ppv);pFactory->Release();return hr;

}

Page 86: Component Object Model  COM 组件对象模型

组件创建过程组件创建过程

IClassFactoryIClassFactory

IXIX

调用CoGetClassObject

客户

pIClassFactory

pIX

CoGetClassObjectCoGetClassObjectDLLGetClassObjectDLLGetClassObject

1COM 库 DLL

2

创建类厂 3返回 IClassFactory4

5调用IClassFactory::CreateInstance

创建组件

67返回 IX

8调用 IX::Fx

Page 87: Component Object Model  COM 组件对象模型

DLLMainDLLMain

BOOL APIENTRY DLLMain ( HANDLE hModule,

DWORD dwReason,

void** lpReserved)

{

if (dwReason==DLL_PROCESS_ATTACH){

g_hModule=hModule;

}

return TRUE;

}

Page 88: Component Object Model  COM 组件对象模型

一个一个 DLLDLL 中多个组件中多个组件

类厂 1类厂 1

组件 1组件 1

DLLGetClassObjectDLLGetClassObject调用CoCreateInstance调用CoCreateInstance

客户 DLL

类厂 n类厂 n

组件 n组件 n

Page 89: Component Object Model  COM 组件对象模型

类厂实现的复用类厂实现的复用

类厂类厂

组件 1

CreateFunction_1

组件 1

CreateFunction_1

DLLGetClassObjectDLLGetClassObject调用CoCreateInstance调用CoCreateInstance

客户 DLL

CLSID_1CLSID_1

组件 n

CreateFunction_n

组件 n

CreateFunction_n

&CreateFunction_1&CreateFunction_1

CLSID_2CLSID_2 &CreateFunction_2&CreateFunction_2

CLSID_2CLSID_2 &CreateFunction_2&CreateFunction_2

Page 90: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 91: Component Object Model  COM 组件对象模型

COMCOM 的继承特性的继承特性• COM 不支持实现继承• COM 支持接口继承

• 理由 :– 实现继承使得一个对象的实现同另外一个对象

的实现紧密地关联起来• 基类修改后 ,派生类无法正常运行而必须修改• 无法得到源代码

Page 92: Component Object Model  COM 组件对象模型

包容简介包容简介• 包容也是在接口级完成的

– 外部组件包含指向内部组件接口的指针– 外部组件将调用转发给内部组件 , 并可增加一些代码

IYIY

外部组件

IXIX

IZIZ

IYIY

外部组件

IXIX

IYIY

内部组件 内部组件

Page 93: Component Object Model  COM 组件对象模型

聚合简介聚合简介• 外部组件将直接把内部组件的接口指针返回给客户– 外部组件无需重新实现并转发接口中的所有函数

• 客户不应知道是在与两个不同的组件交互• 无法改造内部组件

IXIX

IYIY

外部组件

内部组件

Page 94: Component Object Model  COM 组件对象模型

包容的实现包容的实现class CA: public IX, public IY{

public://IUnknown interface …QI,AddRef,Releasevirtual void _stdcall Fx(){cout<<“Fx”<<endl;}virtual void _stdcall Fy() {m_pIY->Fy();}CA();~CA();HRESULT Init();

private:long m_cRef;IY* m_pIY;

};

Page 95: Component Object Model  COM 组件对象模型

包容的实现包容的实现CA::CA():m_cRef(1),m_pIY(NULL){

InterlockedIncrement(&g_cComponents);

}

CA:: ~CA(){InterlockedDecrement(&g_cComponents);if (m_pIY!=NULL){

m_pIY->Release();}

}

Page 96: Component Object Model  COM 组件对象模型

包容的实现包容的实现HRESULT CA::Init(){

HRESULT hr=::CoCreateInstance( CLSID_Compnt2, NULL,CLSCTX_INPROC_SERVER, IID_IY, (void**)&m_pIY);

if (FAILED(hr)){return E_FAIL;

}else{return S_OK;

}}

Page 97: Component Object Model  COM 组件对象模型

包容的实现包容的实现HRESULT _stdcall CFactory::CreateInstance( IUnknown* pUnknownOuter, const II

D& iid, void** ppv){if (pUnknownOuter!=NULL){

return CLASS_E_NOAGGREGATION;}CA* pA=new CA;if (pA==NULL){

return E_OUTOFMEMORY;}HRESULT hr=pA->Init();if (FAILED(hr)){

pA->Release();return hr;

}hr=pA->QueryInterface(iid,ppv);pA->Release();return hr;

}

Page 98: Component Object Model  COM 组件对象模型

接口扩展接口扩展interface IAirplane:IUnknown{

void Takeoff();void Fly();void Land();

}

Interface IFloatPlane:IAirplane{void LandingSurface(UINT iSurfaceType);void Float();void sink();

}

Page 99: Component Object Model  COM 组件对象模型

接口扩展接口扩展Void CMyFloatPlane::Fly(){

m_pIAirplane->Fly();}

void CMyFloatPlane::Land(){if (m_iLandingSurface==WATER){

WaterLanding();}else{

m_pIAirplane->Land();}

}

Page 100: Component Object Model  COM 组件对象模型

聚合的实现聚合的实现• 客户向外部组件请求接口 IY

• 外部组件不实现 IY 接口 , 向内部组件请求查询此 IY 接口

• 将查询到的 IY 接口指针返回给客户• 客户直接使用此指针调用内部组件所实现

的 IY 成员函数

Page 101: Component Object Model  COM 组件对象模型

聚合聚合 -- 类声明类声明

class CA: public IX{public :

//Add IUnknown interface declaration herevirtual void _stdcall Fx(){cout<<“Fx”<<endl;}CA();~CA();HRESULT Init();

private:long m_cRef;IUnknown* m_pInnerUnknown;

};

Page 102: Component Object Model  COM 组件对象模型

聚合聚合 --QueryInterfaceQueryInterface 实现实现

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){

return m_pInnerUnknown->QueryInterface(iid,ppv);} else{

*ppv=NULL;return E_NOINTERFACE;

}reinterpret_cast<IUnknown *>(*ppv)->AddRef();return S_OK;

}

Page 103: Component Object Model  COM 组件对象模型

不正确的不正确的 IUnknownIUnknown

• 聚合的目标是使客户确信内部组件所实现的某个接口是由外部组件实现的

• 客户查询属于内部组件的接口时 , 所获得的组件功能视图与它查询外部组件的接口是不同的– 如果将内部组件按照通常方式实现的接口指针

传递给客户 , 客户将会得到分裂的视图• 违反“若能够从某个接口获取某特定接口,则从任意接口都能获取此接口”

Page 104: Component Object Model  COM 组件对象模型

组件的分裂视图组件的分裂视图

QueryInterfaceQueryInterface

IX

外部组件的IUnknown 实现外部组件的

IUnknown 实现AddRefAddRef

ReleaseRelease

FxFx

QueryInterfaceQueryInterfaceI

Y内部组件的

IUnknown 实现内部组件的

IUnknown 实现AddRefAddRef

ReleaseRelease

FyFy

外部组件

内部组件

Page 105: Component Object Model  COM 组件对象模型

解决方案解决方案• 问题的根源在于

– 内部组件实现了自己的 IUnknown 接口– 客户独立于聚合组件的实现,客户得到两个不同的 IUn

known 接口• 原则:不同的接口当且仅当它们返回相同的 IUnk

nown 接口指针时被认为是同一个组件。• 解决:应将内部组件的 IUnknown 接口向客户隐藏,而只给用户提供一个 IUnknown 接口– 内部组件上的接口必须使用外部组件所实现的 IUnkno

wn 接口– 外部组件的接口称作外部未知接口或者控制未知接口

Page 106: Component Object Model  COM 组件对象模型

外部未知接口外部未知接口HRESULT _stdcall CoCreateInstance(

const CLSID& clsid,IUnknown* pIUnknownOuter,DWORD dwClsContext,const IID& iid,void** ppv);

HRESULT _stdcall CreateInstance(IUnknown* pUnknownOuter,const IID& iid,void** ppv);

Page 107: Component Object Model  COM 组件对象模型

代理和非代理未知接口代理和非代理未知接口• 为支持聚合,内部组件实际将实现两个 IUnknown 接口• 非代理未知接口

– 将按照通常的方式实现内部组件的 IUnknown 接口• 代理未知接口

– 把 IUnknown 成员函数调用转发给外部未知接口或非代理未知接口

• 如果内部组件没有被聚合,代理未知接口将这些调用转发给非代理未知接口

• 如果内部组件被聚合了,代理未知接口将这些调用转发给由外部组件实现的外部未知接口

• 使用关系– 聚合组件的客户调用代理未知接口– 外部组件通过非代理未知接口来操作内部组件

Page 108: Component Object Model  COM 组件对象模型

未聚合组件未聚合组件

QueryInterfaceQueryInterface

IY 代理 IUnknown实现

代理 IUnknown实现

AddRefAddRef

ReleaseRelease

FyFy 非代理 IUnknown实现

非代理 IUnknown实现

Page 109: Component Object Model  COM 组件对象模型

聚合组件聚合组件

QueryInterfaceQueryInterface

IX

外部 IUnknown 实现外部 IUnknown 实现AddRefAddRef

ReleaseRelease

FxFx

非代理 IUnknown 实现非代理 IUnknown 实现

QueryInterfaceQueryInterface

IY

AddRefAddRef

ReleaseRelease

FyFy

代理 IUnknown 实现代理 IUnknown 实现

控制内部组件的代码

Page 110: Component Object Model  COM 组件对象模型

非代理未知接口声明非代理未知接口声明struct INondelegatingUnknown{

virtual HRESULT _stdcall NondelegatingQueryInterface(const IID&,

void**)=0;virtual ULONG _stdcall NondelegatingAddRef()=0;virtual ULONG _stdcall NondelegatingRelease()=0;

}

Page 111: Component Object Model  COM 组件对象模型

非代理未知接口实现非代理未知接口实现HRESULT _stdcallCB::NondelegatingQueryInterface(const IID& iid, void** ppv){

if (iid==IID_IUnknown){ *ppv=static_cast<INondelegatingUnknown*>(this);} else if (iid==IID_IY){

*ppv=static_cast<IY*>(this);} else {

*ppv=NULL;return E_NOINTERFACE;

}reinterpret_cast<IUnknown*>(*ppv)->AddRef();return S_OK;

}

Page 112: Component Object Model  COM 组件对象模型

代理未知接口实现代理未知接口实现class CB: public IY, public INondelegatingUnknown{

public:virtual HRESULT _stdcall QueryInterface(const IID& iid, void** ppv){

return m_pUnknownOuter->QueryInterface(iid,ppv);}virtual ULONG _stdcall AddRef(){

return m_pUnknownOuter->AddRef();}Virtual ULONG _stdcall Release(){

return m_pUnknownOuter->Release();}// Add NondelegatingUnknown interface declaration herevirtual void _stdcall Fy(){cout<<“Fy”<<endl;}CB(IUnknown* m_pUnknownOuter);~CB();

private:long m_cRef;IUnknown* m_pUnknownOuter;

};

Page 113: Component Object Model  COM 组件对象模型

外部组件的外部组件的 InitInit 函数函数HRESULT _stdcall CA::Init(){

IUnknown* pUnknownOuter=this;HRESULT hr=CoCreateInstance( CLSID_Compnt2,

pUnknownOuter, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUnknownInner);

if(FAILED(hr)){return E_FAIL;

}hr= m_pUnknownInner->Queryinterface(IID_IY, (void**)&m_pIY);if(FAILED(hr)){

m_pUnknownInner-> Release();return E_FAIL;}pUnknownOuter->Release(); return S_OK;

}

Page 114: Component Object Model  COM 组件对象模型

内部组件的内部组件的 IClassFactory::CreateInstaIClassFactory::CreateInstancence

HRESULT _stdcallCFactory::CreateInstance( IUnknown* pUnknownOuter,

const IID& iid, void** ppv){

if ((pUnknownOuter!=NULL)&&(iid!=IID_IUnknown)){return CLASS_E_NOAGGREGATION;

}CB* pB=new CB(pUnknownOuter);if (pB==NULL){

return E_OUTOFMEMORY;}HRESULT hr=pB->NondelegatingQueryInterface(iid,ppv);pB->NodelegatingRelease();return hr;

}

Page 115: Component Object Model  COM 组件对象模型

内部组件构造函数内部组件构造函数CB::CB(IUnknown* pUnknownOuter):m_cRef(1){

::InterlockedIncrement(&g_cComponents);if (pUnknownOuter==NULL){

m_pUnknownOuter=reinterpret_cast<IUnknown*> (static_cast<INondelegatingUnknown*>(this));} else {

m_pUnknownOuter=pUnknownOuter;}

}

Page 116: Component Object Model  COM 组件对象模型

外部组件指向内部组件指针外部组件指向内部组件指针else if (iid==IID_IY){

return m_pUnknownInner->QueryInterface(iid,ppv);

}

或者else if (iid==IID_IY){

*ppv=m_pIY;

}

Page 117: Component Object Model  COM 组件对象模型

释放内部组件释放内部组件• 首先要保证组件不会试图再次将自己释放• 其次需要对外部组件调用 AddRef

– 对内部组件的 Release调用将会导致对外部组件的 Release调用。

• 最后释放外部组件

Page 118: Component Object Model  COM 组件对象模型

盲目聚合盲目聚合

else if (iid==IID_IX){

*ppv=static_cast<IX*>(this);

} else {

return m_pInner->QueryInterface(iid,ppv);

}

• 大多数情况不应该使用盲目聚合– 内外组件的接口可能不兼容

Page 119: Component Object Model  COM 组件对象模型

元接口元接口• 元接口:最不可能同组件中已有接口出现冲突的接口

• 用于操作组件本身

ISetColorsISetColorsIToolInfoIToolInfo

IMorphIMorph

内部组件

IColorsIColors

外部组件变换工具

变换算法

Page 120: Component Object Model  COM 组件对象模型

现实中的聚合和包含现实中的聚合和包含

• 为提供内部状态信息,内部组件可以加上新的接口,提供状态信息

IXIX

IYIY

内部组件

IZIZ

外部组件

外部组件的实现

IInternalStateIInternalState

Page 121: Component Object Model  COM 组件对象模型

接口实现接口实现

IXIXIX

实现IX

实现

IYIY

IZIZ

独立实现的组件

IY实现IY

实现

IZ实现IZ

实现

IXIX

IYIY

IZIZ

共享了某些内容的组件

IX,IY,IZ实现

IX,IY,IZ实现

Page 122: Component Object Model  COM 组件对象模型

虚拟函数模拟虚拟函数模拟• 基类可以在完成某个操作前、中、后调用某个虚拟函数给派生类提供一个定制操作的机会

IXIX

组件

客户调用 IX

客户

ICustomizeICustomize组件调用 ICustomize,给客户提供定制 IX行为的机会

ICustomizeICustomize

缺省定制组件客户的 ICustomize 实现可以包含或聚合缺省定制组件

Page 123: Component Object Model  COM 组件对象模型

概览概览 组件和接口 QueryInterface 函数 引用计数 动态链接 类厂 组件复用:包容与聚合 服务器

Page 124: Component Object Model  COM 组件对象模型

• 每个 EXE 文件都将在不同的进程中运行• 每个进程都有其自己的进程空间• DLL 将被映射到链接它们的 EXE 进程空间

不同的进程不同的进程

0x0000ABBA0x0000ABBApFoopFoo

pFoopFoo

0x0000ABBA0x0000ABBA

0x0000ABBA0x0000ABBA 0x0BAD0ADD0x0BAD0ADD

0x000012340x00001234

进程 1

进程 2

进程 1地址空间

进程 2地址空间

物理内存

Page 125: Component Object Model  COM 组件对象模型

进程内接口访问进程内接口访问• DLL 被称作是进程中服务器• EXE 被称作是进程外服务器(本地服务器)

• 接口实际上是一个函数指针数组• 为调用接口中的函数,客户必须能够访问同

接口相关的内存• 组件是 DLL ,与客户处于相同的地址空间

Page 126: Component Object Model  COM 组件对象模型

跨进程边界接口跨进程边界接口• 一个进程需要能够调用另外一个进程中的

函数• 一个进程需要能够将数据传递给另外一个

进程• 客户无需关心它所访问的服务器是进程内

服务器还是进程外服务器

Page 127: Component Object Model  COM 组件对象模型

本地过程调用(本地过程调用( LPCLPC ))• LPC 是基于 RPC 的用于单机上进程间通信

的 MS 专有技术• LPC 由操作系统实现

– 操作系统知道同每一个进程逻辑地址空间相对应的物理地址,操作系统可以调用任意进程中的任意函数

组件客户

EXE EXE进程边界

LPC

Page 128: Component Object Model  COM 组件对象模型

MarshalMarshal

• 将函数调用的参数从一个进程的地址空间中传到另外一个进程的地址空间中

• 若两个进程的都在同一台机器上,调整的过程相当直接:只需将参数数据从一个地址空间复制到另一个的地址空间

• 若在不同的机器上,考虑不同机器在数据表示方面的不同,必须将参数数据转换成标准格式– 实现 IMarshal 接口,在 COM创建组件的过程中查询– 在调用函数前后使用 IMarshal 成员函数调整或反调整

Page 129: Component Object Model  COM 组件对象模型

Proxy\Stub DLLProxy\Stub DLL

• 客户应该可以按照相同的方式与进程中、本地和远程组件进行通信。

• 代理:同另外一个组件行为相同的组件,DLL 形式,需要访问用户进程空间并进行数据调整

• 桩:对客户传来的数据进行反调整,对回传给客户的数据进行调整

Page 130: Component Object Model  COM 组件对象模型

COMCOM 使用的结构使用的结构

组件客户

EXE EXE

进程边界

LPC

代理对参数进行调整

桩对参数进行反调整

DLL DLL

Page 131: Component Object Model  COM 组件对象模型

MIDLMIDL 简介简介import “unknown.idl”;[

object,uuid(32bb8323-b41b-11cf-a6bb-0080c7b2d682),helpstring(“IX Interface”),pointer_default(unique)

]Interface IX:IUnknown{

HRESULT FxStringIn([in,string]wchar_t* szIn);HRESULT FxStringOut([out,string]wchar_t** szOut);HRESULT FyArrayIn( [in] long sizeIn,

[in size_is(sizeIn)] long arrayIn[])}

Page 132: Component Object Model  COM 组件对象模型

Pointer_defaultPointer_default

• ref -将指针当成是引用对待。此时表示此指针将总是指向一个合法的地址,并可反引用。调用前后指向同一地址,不能为空。 不能为它们指定别名。

• unique -此类指针可以为空,并且在函数内可以修改它们的值。

• prt -指定相应的指针就是一个 C指针,可以有别名、可以为空,其值可以被修改。

Page 133: Component Object Model  COM 组件对象模型

输入输出参数输入输出参数• in -仅仅需要将此参数从客户传递给组件• out -参数仅用来从组件向客户传回有关数据。

• 对于输出参数, MIDL 要求必须是一个指针。

HRESULT foo([in] int x, [int,out] int* y, [out] int* z);

Page 134: Component Object Model  COM 组件对象模型

IDLIDL 中的字符串中的字符串• 对数据进行调整时必须知道此块数据大小• 在函数中加上一个 string 修饰符, MIDL知道相应的参数为一个字符串,通过查找末尾空格决定其长度

• 标准约定: Unicode字符即 wchar_t 。

Page 135: Component Object Model  COM 组件对象模型

IDLIDL 中的结构中的结构

typedef struct{

double x;

double y;

double z;

} Point3d;

HRESULT FzStructOut( [out] Point3d* pt);

Page 136: Component Object Model  COM 组件对象模型

COM/DCOMCOM/DCOM 系统结构系统结构

Page 137: Component Object Model  COM 组件对象模型

DCOMDCOM 部件部件


Top Related