第 10 章 分布式 com(dcom)

Post on 13-Jan-2016

194 Views

Category:

Documents

11 Downloads

Preview:

Click to see full reader

DESCRIPTION

第 10 章 分布式 COM(DCOM). DCOM 是 COM 的扩展,它可以支持不同计算机上组件对象与客户程序之间或者组件对象之间的相互通信,这些计算机可以在局域网内,也可以在广域网上,甚至通过 Internet 进行连接。对于客户程序而言,组件程序所处的位置是透明的,不必要编写任何处理远程调用的代码,因此, DCOM 也是 COM 的无缝扩展。. 10.1 DCOM 基本结构. 10.1.1 从 COM 转向 DCOM 本地进程外组件与客户程序运行在不同的进程空间,所以客户程序调用组件对象并不是直接进行的,而是用到了操作系统支持的一些跨进程通信方法。 - PowerPoint PPT Presentation

TRANSCRIPT

第 10 章 分布式 COM(DCOM) DCOM 是 COM 的扩展,它可以支持不同计算

机上组件对象与客户程序之间或者组件对象之间的相互通信,这些计算机可以在局域网内,也可以在广域网上,甚至通过 Internet 进行连接。对于客户程序而言,组件程序所处的位置是透明的,不必要编写任何处理远程调用的代码,因此, DCOM 也是 COM 的无缝扩展。

10.1 DCOM 基本结构 10.1.1 从 COM 转向 DCOM 本地进程外组件与客户程序运行在不同的进程空间,所以客户程序

调用组件对象并不是直接进行的,而是用到了操作系统支持的一些跨进程通信方法。

但对于客户和组件运行在不同机器上的情形, DCOM 只是简单只是简单地把本地跨进程通信用一个网络协议传输过程代替,客户程序和组件对象都感不到发生地过程,只是中间数据传递地路线更长一些。

当然,从 COM 到 DCOM ,并不仅意味着路线地更长,同时组件对象与客户程序协作运行地环境也发生了变化,从简单地单击环境转变到了网络环境。毕竟网络通信比单机系统环境下地跨进程通信要脆弱得多,所以为了保证协作过程地可靠性以及程序对异常事件地应变能力,客户和组件需要考虑更多地细节,而不是仅仅提供与应用有关地基本功能,比如,客户程序对调用返回错误地处理,调用被阻塞、被丢失地处理、以及异步调用地实现。

10.1.2 DCOM 对象地定位 客户程序可以通过给定地机器名和对象 CLSID 来创建

一个 DCOM 对象。可以调用 COM 库的基本创建函数(CoGetClassObject) 创建远程组件对象。

CoGetClassObject 函数的第三个参数 COSERVERUNFO 结构包含了远程机器名和安全信息。在这个结构中,pwszName 指定了远程机器的名字, pAuthInfo 指定了创建对象的激活安全信息,如果为 NULL ,则使用系统缺省的安全设置。

可以简单地把 pAuthInfo 参数设置为 NULL ,并在第二个参数中指定远程对象地标志,从此就有两种方法提供远程对象地机器名信息,或者在创建函数地参数中指定 COSERVERUNFO 结构,或者使用 DCOM配置工具指定异常机器名。

DCOM 对象地定位 COM 库的创建函数得到了远程对象地位置信息后,再

把对象创建地任务交给 SCM ,由 SCM 通过 RPC 与远程机器进行通信。 SCM( 程序名为 Rpcss.exe) 也是 COM 库的一部分,但它是一个单独的进程。 SCM负责创建新的 COM 对象,也负责建立组件对象与客户程序之间的连接。如果要创建远程对象,它会通过 RPC 调用远程机器上的 SCM ,由远程机器上的 SCM启动组件程序,并创建组件对象,然后返回到客户机器。

在此工程中,还要经过列集和散集的处理,包括创建代理对象和装载存根代码等,处理与本地一样。一旦组件对象创建完成之后,客户与组件之间的通信不再经过 SCM ,而是直接连接。

10.1.3 列集 DCOM 提供了一套复杂的列集和散集机制,它

建立在 RPC 的基础上,由于 RPC 被定义为 DCE(Distributed Computing Environment ) 标准的一部分,而 DCE RPC 定义了所有常用类型的数据表示方式,即网络数据表示法 (NDR,network data representation) 。为了使存根代理能够正确地对参数和返回结果进行列集和散集,它们应该使用一致地数据表示法 (NDR) ,以便在不同地操作系统环境下也能够进行远程调用。

10.1.4 对象 RPC DCOM 协议也被称为对象 RPC(ORPC,object remote procedure

call) ,它建立在 DCE RPC 协议地基础上,可用于各种基于组件的分布式系统。 ORPC 建立了一套面向对象的远程调用规范,指定了如何在网络上进行调用、对对象的引用如何表示和如何维护。

在 Internet 或 Intranet 网络环境下, ORPC仍使用标准的 RPC数据包,附加上专用于 DCOM 的一些信息-接口指针标识符 (IPID,interface point identifier) 、版本信息和扩展信息-作为调用和返回的附加参数进行传递,其中 IPID表示调用被处理的远程机器上特定对象的特定接口。 DCOM 客户程序必须周期性地“ pinging”远程机器上地对象,以便保证客户与对象一直处于连接状态。

在开发 DCOM 组件时并不需要直接在 ORPC层次上进行开发,因为组件程序和客户程序实际上只跟存根和代码对象进行通信。如果使用标准地列集法,则生成代理和存根完全和以前一样。

10.1.5 DCOM特性 (1) 可伸缩性:随着用户数目的增加、数据量的不断增

多,分布式应用系统的适应能力就反映了此系统的忧劣。使用 DCOM 建立起来的应用系统能很好地适应这种规模地变化。一方面, DCOM利用操作系统本身地可伸缩性-多 CPU等,另一方面, DCOM 提供了灵活地配置方案,运行不同的组件运行在不同地服务器上, DCOM 地位置透明性保证了这种变化可以不必修改组件源程序。

(2) 可配置性:使用 DCOM模型建立地分布式软件系统可以很方便地对系统进行重新配置,包含服务器变化、客户程序的自动安装等特性。 DCOMCNFG.EXE配置工具可使客户程序和组件程序在不改变代码的情况下适应不同的网络环境。

DCOM特性 (3) 安全性: DCOM 使用了Windows NT提供的可扩

展安全性框架,在非 NT 平台上实现的 DCOM 也包括了一个与 NT 兼容的安全提供器。

(4) 协议无关性: DCOM 并不要求专门的网络协议,所以使用 DCOM 建立的分布式应用系统对网络有很强的适应能力, DCOM 可以使用以下协议:TCP/IP、UDP、 IPX/SPX以及 NetBIOS 。

(5) 平台独立性: DCOM 把平台相关的二进制标准和平台无关的标准隔离开来,并且,由于 DCOM 建立在DCE RPC 的基础上,所以 DCOM 能很好地适应不同地系统平台。

10.2 对象激活 激活一个组件对象包括两种情形,或者是建立新的组件对象,或

者是建立已有组件对象与客户程序地连接。 DCOM 使用两种方式提供远程服务器名。直接设置和在代码中显

式指定远程服务器名。 10.2.1 创建 DCOM 组件 ( 一 ) 此种方法只要在客户机上的组件配置信息中设置好相应地远程服务

器名字,则客户程序不必考虑服务器的位置,它根本不用直接访问服务器名字信息。它只需要知道对象的 CLSID ,并调用 COM库函数, COM 库就会在组件配置信息中取到服务器名字,然后通过远程机器上的 SCM 创建组件对象。

在Windows 系统平台中,远程服务器名字 RemoteServerName 值被保存在系统注册表HKEY_CLASSES_ROOTS\APPID键下。其中的 <DNS name>为服务器名字。在每个 DCOM 组件的 CLSID入口项都有一个名字值指定了对应的 AppID ,从这一结构可以看出 AppID 可用于多个组件对象。

创建 DCOM 组件 ( 一 ) 客户机通过系统配置信息把组件对象的请求传

到远程服务器上,但是,服务器不能再利用 RemoteServer配置信息把创建请求传到另一台机器上。也就是 X调用在 Y上创建,机器 Y的调用在 Z上创建,那么 X对象创建请求将在机器 Y上得到响应,而不会被传到机器 Z上。

可以用 DCOMCNFG配置请求创建的远程机器名。如图 10.5。

10.2.2 创建 DCOM 组件( 二 )

用第一种方法并不是总能满足应用的需要,有些应用要求在程序运行工程中控制要连接的服务器,比如多人游戏程序、管理远程管理工具等。对于这样的应用, DCOM允许在创建函数中指定远程服务器名字。

(1) CoCreateInstanceEx创建一个组件对象,可以在参数中指定服务器信息。

(2) CoGetClassObject 返回一个指向类厂对象的接口指针,客户用该指针来创建一个或多个实例对象,可以在参数中指定服务器信息。

(3) CoGetInstanceFromFile 创建一个新的实例,并根据文件进行初始化。

(4) CoGetInstanceFromISorage 创建一个新的实例,并根据存储对象进行初始化。

创建 DCOM 组件 ( 二 ) COSERVERINFO 结构记录了服务器名字信息和激活安

全信息,一般使用缺省的激活安全信息,把 pAuthInfo成员设为 NULL 。 MULTI_QI 结构可保存多个接口指针,客户程序可根据需要一次取到多个接口指针。

在程序中指定服务器名字不仅可用于诸如游戏或远程管理,也可以利用这种特性实现分布式应用系统的动态负载平衡功能。 COM+ 已经实现了负载平衡功能。

10.2.3 远程创建进行会内组件 : 代理进程 (surrogate)

为了进程运行进程内组件,要求在远程机器上有代理进程 (surrogate process) 。除了可以远程启动进程内组件之外,代理进程还提供了下面的一些特性:

(1) 进程内组件程序中的严重错误只影响到代理进程,不会使客户进程崩溃

(2) 一个代理进程可以同时为多个客户通过服务 (3) 客户可以保护自己避免靠不住的组件程序代码,只访问组件程

序提供的服务 (4) 在代理进程中运行进程内服务可以使 DLL享有代理进程的安全

远程创建进行会内组件 : 代理进程 (surrogate) 如果一个进程内组件满足下列条件,则它将被装入代理进程: (1) 系统注册表中,在组件对象的 CLSID 关键字下必须要指定 AppI

D值,以及对应的 AppID 关键字。 (2) 客户程序在创建对象实例时,必须设置 CLSCTX_LOCAL_SERV

ER 标志 (3) 组件对象的 CLSID 关键字下不指定 LocalServer32 、 LocalServer 、 LocalService值。

(4) 组件对象的 CLSID 关键字包含 InProServer32子键 (5) 在 InProServer32子键中指定的 DLL文件必须存在 (6) 组件对象对应的 AppID键下指定 DLLSurrogate值 如果组件对象的 CLSID键下的 LocalServer32 、 LocalServer 或 L

ocalService值指示了 EXE 的存在,则 EXE 程序将被优先执行,COM 不再启动代理进程。

远程创建进行会内组件 : 代理进程 (surrogate) 为了激活代理进程, DLLSurrogate值必须被指定。

如果要使用系统提供的代理进程,那么把 DLLSurrogate 的值设置为空串或者设置为 NULL;如果要使用自定义代理进程,则把 DLLSurrogate 的值设置为代理进程的路径。为了激活。要在客户机中指定 RemoteServerName ,在服务机器上指定 DLLSurrogate 。

要一个组件程序的实例被装入,濒危所有的客户提供服务,而不管客户身份,则要在 AppID注册表关键字下用 RunAs值指定,如只为一个客户服务,就不要用RunAs值配置组件程序。

10.2.4 任何连接到指定的远程对象实例 在此可以利用名字对象连接到远程对象实例 客户程序调用 MkParseDisplayName 函数创建一个名字对象,然后调用名字对象的 BindToObject 函数执行绑定操作。如果要创建远程对象,那么必须利用 COM 对象的存储激活特性。

为了利用对象的存储激活特性,也就是说使 COM 对象在它保存数据的服务器上运行,必须在 COM 对象的 AppID 关键字下使用 ActivateAtStorage值,在 COM版本中,只有文件名字对象支持 ActivateAtStorage特性。

在使用名字对象执行绑定操作时也可以通过程序代码来重新设置远程服务器名。通过绑定环境的参数指定 BIND_OPTS2 结构参数。在程序中可以先取出绑定环境对象中的 BIND_OPTS2 参数,然后设置新的 COSERVERINFO 结构参数,再设置到绑定环境对象中。

10.3 连接管理 10.3.1 更好的控制远程对象的生存期 不断的调用 AddRef 和 Release会严重影响网络性能, DCOM优

化了远程对象的 AddRef 和 Release 调用。其中使用了 OXID(object exporter identifier ,对象管理标识符 ) ,每个支持 DCOM的机器上,都有一个被称为 OXID解析器 (OXID Resolver) 的服务,OXID 对象实现了 IRemUnknown 接口, DCOM 通过 RemAddRef 和 RemRelease 方法管理远程引用计数。

10.3.2 pinging机制 为了检测客户程序是否非正常终止, DCOM 提供了一种简单的方

法“ pinging”。在组件程序机器上,每一个被引出的对象 ( 对应一个引出的 OID) 都有一个“ pingPeriod”时间值和一个“ numPingsToTineout”计数,它们组合起来决定整个事件。通常称为“ ping 周期”。 DCOM 包含了优化的“ pinging”结构,ping信息由 OXID解析器发送和接收, OXID解析器决定哪些 OID 在同一台机器上,并产生一个 ping 集。

10.4 并发管理 10.4.1 线程模型 1. 同样进程内的客户程序任何得到对象接口指针 2. 套间线程中客户调用对象的工程 3. 套间线程的客户跨进程调用的工程 4. 从另一个进程进入套间线程的过程。

10.4.2 消息过虑器 消息过虑器是一个简单的 COM 对象,它

实现了 IMessageFilter 接口。

10.5 DCOM 安全模型 10.5.1 安全性策略 (1) 访问安全性和激发安全性 (2) 对象的安全身份 (3) 保护数据 (4) 鉴定级别 (5) 模式级别 匿名 (SecurityAnonymous) 身份鉴别 (SecurityIdentification) 模仿 (SecurityImpersonation) 委托 (SecurityDelegation)

10.5.2 安全性配置 在此可以使用 DCOMCNFG 程序的“默认属性”和“默认安全机制”。

DCOM 也允许应用程序调用 CoInitiallizeSecurity 初始化 DCOM 安全层以便建立进程的鉴定和模仿局部。

第 11 章 自动化 (Automation) 对象 虽然自动化技术建立在 COM 的基础上,但自动化要比 COM 应用广泛得多。一方面,自动化继承了 COM 的很多优点;另一方面,自动化技术简化了 COM 的一些底层细节。

自动化的核心是 IDispatch 接口,每一个自动化对象都必须实现 IDispatch 。

11.1 自动化对象基础 11.1.1 自动化产生与发展 自动化技术的发展与 VB和 VBA 有直接

的关系。 首先, VBA 以及发展成为大多数 Micros

oft 应用程序扩展的标准,在这些应用程序中,宏语言跳过自动化对象访问应用程序中的数据。

其次, VB开发工具的成功应用也推动了自动化对象的发展。

11.1.2 属性和方法 方法和属性是自动化对象的两个基本特性,方法是自动化对象所提供的功能服务;而属性是指自动化对象的数据特征。属性是一个值,它既可以被设置,也可以被获取。

一般来说,获取属性值不需要参数,但有一些属性可以具有索引值。属性的索引值有点像参数,它不一定非要整数,并且在索引中也可以有多个元素。

方法可以具有零个参数和多个参数,它们既可以设置也可以获取对象数据,最常见的是完成某些动作。

自动化的属性和方法都有符号化的名字,客户程序通过名字就可以访问到自动化对象的属性个方法。这比通过 vtable才能访问到 COM 对象的成员函数要方便。

11.3.1 类型库和 ODL 组件对象的类型信息是指它与外界进行交互的一些必要信息,包括组件的 CLSID 、它所支持接口的 IID 、接口的每个成员函数、成员函数的参数和返回值等等,类型信息中的数据类型也可以是自定义的数据类型。

类型信息是客户程序和组件之间进行通过的基础,不管通过什么样的方式,客户程序在访问组件对象之前必须得到基本的类型信息。

Microsoft 扩充了 IDL 形成 ODL(object description language ,对象描述语言 ) ,可以描述组件对象的类型信息。一般来说,一个组件对象的类型信息包括每个接口的类型信息和对象的类型信息,接口类型信息的描述方法与 IDL完全兼容。接口类型信息使用 interface 或 dispinterface 关键字描述,对象类型信息使用 coclass 关键字进行描述。经过 MIDL 或者 MkTyLib工具可编译得到类型库。通常一个组件程序中的对象放在一个 ODL文件中,并用 library关键字描述库信息,包括类型库的 ID(即 LIBID) 、类型库所使用的语言、版本等。在 ODL文件中也可以使用 importlib引入其它类型库信息。

类型库和 ODL 用 MIDL 实用工具编译 ODL文件可以得到类型库文件,其后缀为 TLB,也可以产生相应的 .h头文件。 VB或者 VBA 可以使用类型库来浏览组件对象的方法和属性,它也可以利用类型库增强对属性和方法的访问。

在实现自动化对象的 IDispatch 接口时,可以利用类型库向客户程序提供类型信息,这可以避免繁琐的类型处理。

11.1.4 IDispatch 接口 在接口 IDispatch就中,前两个成员函数 GetTypeInfo 和 GetTy

peInfoCount 用于处理对象的类型信息。在 GetTypeInfoCount成员函数中,如果对象提供了类型信息,则输出参数 pctinfo 的返回值为 1;否则为 0 。 GetTypeInfo成员函数返回对象的类型信息。

成员函数 GetIDsOfNames 用于返回方法或属性成员及其参数名字的 DISPID , IDispatch 用分发 ID管理属性和方法。

自动化接口的分发 ID 是一个整数, 0 或者负数有特殊含义。如表 11.1

成员函数 Invoke 是一个关键函数,客户程序必须通过 Invoke 函数才能访问属性和方法,也可以说此函数是自动化对象的命令翻译器。此函数的第一个参数 dispIdMember 给出了分发 ID ,它将根据此分发 ID执行有关的属性访问函数或者方法函数。

11.1.5 自动化兼容的数据类型 在自动化对象的方法和属性中,必须使用自动化兼容的数据类型。自动化对象使用的基本数据类型为 VARIANT结构类型,在 Invoke 函数的参数中, pDispParams 参数中表示方法或属性参数的类型为 VARIANTARG 结构, pVarResult 参数的类型为 VARIANT结构,其实两个的定义完全相同。

VARIANT结构中,大多数类型已经很熟悉,但有几个类型要做简单介绍

(1) 布尔型 VARIANT_BOOL 。实际上就是短整型, 1 为真, 0 为假(2) 货币类型 CY(3) 日期类型 DATE 。日期类型实际上是一个浮点数,整数部分表示自 1899 年 12月 30日以来的天数,小数部分为时间值。

自动化兼容的数据类型(4) Basic 字符串类型BSTR 。 BSTR 是一个双字节表示的字符串类型,以 0 结尾,但特殊的是,在字符串开始的前 4个字节指定了字符串的长度。如图 11.1 。

由于 BSTR 是使用了长度计数器,所以 BSTR 作为字符串类型,在进行各种处理时效率要比普通的做的长高的多。 OLE 提供了一组 API 函数用来处理 BSTR ,包括 SysAllocString、 SysAllocStringLen 、 SysFreeString、 SysReAllocString、 SysReAllocStringLen 以及 SysStringLen等。

(5) SAFEARRAY 类型。此类型实际上是一个结构类型,但它用于描述各种数组,由于 SAFEARRAY指定了数组每一维的边界信息,所以可以认为它是安全的。

OLE 也提供了一套 API 函数用来处理 SAFEARRAY结构,比如 SafeArrayAllocData 、 SafeArrayDestoryData等将近 20 个函数。

自动化兼容的数据类型 假如 Invoke 函数希望在成员调用中获得一个 BSTR 字符串

参数,但它得到的是 long或者 double ,那么 Invoke 函数在调用内部处理函数之前,必须先把它转化为 BSTR 。甚至得到的是一个 Idispatch 指针,也得把其转换为 BSTR 。

OLE 提供了两个函数可以完成类型转换功能: VariantChangeType 和 VariantChangeTypeEx,利用这两个函数可以把一个 VARIANT变量转换为另一个 VARIANT变量。还有 Variant <type> From <type>,比如 VarR4FromI2 , VarUI2FromDisp等。

Invoke 函数还可以使用另一个 API 函数 DispGetParam 来处理类型转换。

11.1.6 参数顺序、可选参数和命名参数 Invoke 函数的参数 pDispParam 。 pDispParam 参

数的数据类型为 DISPPARAMS 。关于此结构的三个问题如下:

1. 参数顺序:在 rgarg数组中,参数的顺序与客户程序中调用的参数左右顺序刚好相反。

2. 可选参数:在 ODL文件中,可以把方法的参数记为可选的 (optional) ,但是这样的参数在 DISPPARAMS结构中也会出现。为了判断一个参数是否为可选参数,Invoke 函数检查参数的 vt 域是否为 VT_ERROR ,如果 vt 域为 VT_ERROR 并且 scode 域为 DISP_E_ PARAMNOTFOUND ,则此参数为可选参数。

参数顺序、可选参数和命名参数

3. 命名参数:不管是可选参数还是按反序指定的参数,统称为定位参数 (positional argument) ,与之相对应的另一种参数为命名参数 (named argument) 。 DISPPARAMS 结构的 cNamedArgs成员指定了在 rgarg数组中有多少个命名参数。如果 cNamedArgs 为 0 ,则表示没有命名参数。

命名参数是指以名字指定的参数,命名参数可以不受次序约束。不过命名参数和定位参数不能相互交叉,而且 rgvarg数组的前 cNamedArgs 个参数为命名参数,后面的参数为定位参数;对应于客户程序调用的参数顺序,必须先指定定位参数,再紧跟着命名参数。

11.1.7 IDispatchEx接口 使用 IDispatch 接口对于一般的应用来说已经足够了,

但由于它所描述的类型信息是静态的,在运行工程中类型信息不会被改变,所以在编译时刻通过类型库就可以获得所有的类型信息。然而,在一些脚本语言中,需要造运行时刻动态提供类型信息,比如 VBScript 、JavaScript 或 DHTML 对象模型等要求一个更加灵活的接口。 IDispatchEx 正好这种要求。

11.2 自动化对象实现 11.2.1 类型库支持 接口 Idispatch成员函数 GetTypeInfoCount 和 GetTypeI

nfo 的主要目的是提供类型库支持,控制器可以在运行时刻调用 GetTypeInfoCount 函数判断对象是否提供接口类型信息,如果对象支持类型信息,则控制器程序可以调用 GetTypeInfo 函数获取类型信息。如果对象没有提供只需把输出参数 pctinfo 的返回值置为 0 。

提供类型库信息的步骤如下: 1. 利用 ODL语言编写一个 ODL文件, 2. 然后利用 MIDL 实用工具生成TLB文件。使用下面的命令可以产生类型库文件 ( 以及相应的 C/C++头文件 ):

MIDL /h <header>.H/tlb <library>.TLB <file>.ODL

自动化对象实现 有了 TLB文件之后,就可以实现 IDispatch 接口的 Ge

t-TypeInfoCount 和 GetTypeInfo 函数 GetTypeInfoCount 函数非常简单,只需要把输出函数 pctinfo 设置为 1既可。 GetTypeInfo 函数则需要调用 OLE 提供的 API 函数把 TLB文件装入到内存中,然后向控制器返回 ITypeInfo 接口指针。

11.2.2 Invoke 函数实现 根据实际情况,可以采用两种方法来实现 GetIDsOf-

Names 函数。 1. 由于自动化对象知道自己所有的方法和属性以及它们

的分发 ID ,因此可以利用这些知识提供 GetIDsOf-Names服务。可通过查表或者 switch语句实现从名字到分发 ID 的映射。如果对象的属性和数目较少,那么可以直接用 switch 或者 if语句实现。如果较多,那么可以把名字和分发 ID 信息放在一张表中, GetIDsOfNames 函数直接查表既可。

Invoke 函数实现 2. 利用自动化接口的类型信息对象实现 GetIDsOfNa

mes 函数。因为类型库本身已经包含了自动化对象接口的成员与分发 ID 之间的映射关系,所以 ITypeInfo接口提供这样的服务。

OLE 的 API 函数 DispGetIDsOfNames 简单地封装了 ITypeInfo 接口的 GetIDsOfNames成员函数用于提供这样的服务,

第一种方法适合于比较简单的情形, Invoke 函数负责所有的成员处理,包括参数提取等待,然后根据成员分发 ID 的不同盟执行不同的分支。

第二种方法也要用到类型库信息对象 ITypeInfo 接口指针。在执行 MIDL产生一个用 C/C++语言描述的 .h头文件,头文件不仅包含自动化接口的定义,也包含一个只与自动化成员有关的自定义接口。

11.2.4 多语种-本地化 在 Idispatch 接口的多个函数中的 LCID(locale identifier ,本地化标识,有时也写为 LocaleIE) 参数,它指示了控制器所使用的语言环境。 LCID 被定义为一个 32 位的整数,通常它对一个组件对象有以下一些影响:

(1) 字符串资源的表达、排序、比较等操作 (2) 日期、时间的表达以及其它一些操作 (3) 货币的格式 自动化对象的每一个方法和属性在不同的语言环境中可

以使用不同的名字,比如 MoveTo--移动到。 实现语言支持的合理做法是支持两种语言,一种是对象

应用的主要语言环境,另一种是可以适用于任何语言环境的语言-英语。

11.2.5 用 CreateStdDispatch函数实现自动化对象

OLE 提供了一个标准的 IDispatch 接口实现,可以通过 API 函数CreateStdDispatch 创建一个标准分发对象 (standard dispatch object) ,标准分发对象只实现一个 IDispatch 接口。

CreateStdDispatch 函数要求提供 Iunknown 接口指针以及一个指向对象自身的 pvThis 指针,更为重要的是一个指向类型信息对象的 ITypeInfo 接口指针 ptinfo 。

使用 CreateStdDispatch 函数实现自动化对象的用法比较简单,首先为自动化对象类增加一个初始化函数,以便类厂的创建函数调用。

因为自动化对象已经聚合了标准分发对象,所以它不必要实现 IDispatch 接口,而只需在 QueryInterface 函数中把标准分发对象的 IDispatch 接口暴露出去既可。

11.3 自动化对象应用 11.3.1 双接口 自动化控制器通过 IDispatch 接口可以调用自动化对象

的所有方法和属性,但由于这样调用是通过 Invoke 函数是间接进行的,并且在 Invoke 函数中要进行参数类型检查或者其它一些类型信息处理,因此效率要低一些。客户程序希望能直接通过 vtable 调用到成员函数代码。为了满足两种情形,自动化对象可以在实现 IDispacth 接口的基础上,把方法和属性函数也以接口成员函数的方法暴露出去,这样就形成了双接口 (dual interface) 。

11.3.2 迟绑定和早绑定 如果自动化控制器利用对象的类型库,在编程时刻对

参数类型和返回值进行检查,并直接用分发 ID 调用 Invoke 函数,这种调用方法就称为早绑定 (early binding)技术。

相应地,控制器也可以在运行时刻通过 IDispatch 接口的成员函数获得类型信息,当它访问一个属性或方法时,它调用 IDispatch::GetIDsOfNames ,以便根据符号化名字找到分发 ID ,这种称为迟绑定技术。

选择迟绑定或早绑定取决于控制器的运行环境,而调用效率并不成为判断的依据。正真限制速度的因素是 IDsipatch::Invoke 调用。

11.3.3 自动化集合对象 集合对象也是一个自动化对象,但它有一些特殊的要求。

(1) 首先,作为一组同类对象 ( 或数值 ) 的容器对象,它必须通过枚举这些成员的方法。

(2) 它必须支持 Add、 Remove 和 Item 方法以及 Count属性。

客户程序通过 Add方法增加成员;通过 Remove 方法删除成员;通过 Item 方法可以访问集合中的任意成员。

11.3.4 以 IDispatch 作为出接口

当 IDispatch 接口作为对象的出接口时,对象通过调用 Invoke 函数来激发事件。这时,位于客户程序或控制器一方的事件接收器实现 IDispatch 接口,它的 Invoke 函数负责处理各种事件;而自动化对象则变成了出接口的客户,它在调用 Invoke 函数时,必须设置正确的参数。然而,由于出接口是由对象调用的,所以它知道每个事件所对应的分发 ID 以及参数和返回值的类型,因此,事件接收器除了实现 Invoke 函数和 IUnknown 的三个函数之外,他不必再提供类型信息支持。从这个角度来讲,由 IDispatch 作为出接口要简单一些。

11.3.5 自动化控制器 如果要自己编写一个自动化对象控制器以便允许用户编写脚本代

码控制自动化对象,那么以下一些原则可供参考:(1) 对象创建机制-- COM命名机制(2) 对象析构机制--调用 Release 或者 CoFreeUnUsed - Librari

es ,或者 set obj=nothing(VB)(3) 如何连接的到一个已经在运行的自动化对象。通过名字对象或者

GetObject 可以根据路径名或者 ProgID 连接(4) 如何访问自动化对象的属性。 obj.property。也可以使用 obj::

property、 obj[property]等(5) 调用自动化方法。与属性非常类似,甚至可采取同样的语法规则。(6) 提供事件处理机制。(7) 对于实现多个自动化接口的对象,控制器应提供 QueryInterfac

e 函数的功能,允许用户的脚本代码从一个接口转向另一个接口。(8) 向用户提供 UI 形式的对象类型信息。

top related