windows C++- Com技术简介(下)

服务控制管理器

服务控制管理器 (SCM) 会处理 COM 对象实例的客户端请求。 以下列表显示了事件序列:

  • 客户端会使用 COM 对象的 CLSID 调用 CoCreateInstance 等函数,以从 COM 库中请求指向 COM 对象的接口指针;
  • COM 库会查询 SCM,以查找与所请求的 CLSID 对应的服务器;
  • SCM 会找到服务器,并从服务器提供的类工厂请求创建 COM 对象;
  • 如果成功,COM 库将返回指向客户端的接口指针;

COM 系统将服务器对象连接到客户端后,客户端和对象将直接通信。 通过中间运行时调用不会增加开销。

将向主机系统注册 COM 服务器时,可以为要激活的服务器指定不同的方式。 以下列表显示了 SCM 可用于激活 COM 服务器的三种方法:

  • 进程内:SCM 返回包含对象服务器实现的 DLL 的文件路径。 COM 库会加载 DLL 并在其中查询其类工厂接口指针;
  • 本地:SCM 启动本地可执行文件,该可执行文件会在启动时注册类工厂,并且其接口指针可供系统和客户端使用;
  • 远程:本地 SCM 从正在远程计算机上运行的 SCM 获取类工厂接口指针;

当客户端请求 COM 对象时,COM 库会联系本地主机上的 SCM。 SCM 会找到可能为本地或远程的相应 COM 服务器,并且服务器将返回指向服务器的类工厂的接口指针。 当类工厂可用时,COM 库或客户端可以使用类工厂创建请求的对象。 

可重用性

COM 支持黑盒可重用性,这意味着不会将可重用组件的实现详细信息向客户端公开。 为了实现黑盒可重用性,COM 支持两种机制,通过这两种机制一个对象可以重用另一个对象。 这两种重用形式名为包含和聚合。 按照约定,要重用的对象名为内部对象,而在使用内部对象的对象名为外部对象。

在包含中,外部对象充当内部对象的客户端。 外部对象是内部对象的逻辑容器,并且当外部对象使用内部对象的服务时,外部对象会将实现委托给内部对象的接口。 这意味着,外部对象是在内部对象的服务中实现的。 外部对象可能不支持与内部对象相同的接口,并且外部对象可以使用内部对象的接口,帮助实现外部对象上不同接口的各个部分。

在聚合中,外部对象会公开来自内部对象的接口,就像在外部对象上实现接口一样。 当外部对象始终将其接口上的每次调用委托给内部对象中的相同接口时,这种方法就非常有用。 聚合是一种方便的方法,使外部对象能够避免额外的实现开销。

存储和流对象

COM 对象使用结构化存储将状态保存到文件,这是一种持久存储形式,支持使用文件系统语义导航文件内容。 以这种方式处理文件内容时,可启用进程之间的增量访问、事务和共享等功能。

COM 持久存储规范提供两种类型的存储元素:存储对象和流对象。 这些对象将由 COM 库实现,用户应用程序很少实现这些存储元素。 存储对象实现 IStorage 接口,而流对象实现 IStream 接口。

流对象包含数据,在概念上类似于文件系统中的单个文件。 每个流都具有访问权限和单个寻道指针。 通过 IStream 接口,可以读取、写入流基础数据,以及为这些数据进行寻道和执行其他操作。 流是使用文本字符串命名的。 它可以包含任何内部结构,因为它是一个平面字节流。 此外,IStream 接口中的函数类似于基于标准文件句柄的函数,例如 ANSI C 运行时库中的函数。

存储对象在概念上类似于文件系统中的目录。 每个存储都可以包含任意数量的子存储对象和任意数量的流。 每个存储都有自己的访问权限。 通过 IStorage 接口,可以执行枚举、移动、复制、重命名、创建和删除元素等操作。 存储对象不存储应用程序定义的数据,但会隐式存储包含的元素(存储和流)的名称。

根据主机平台上的 COM 规范实现存储和流对象时,可在进程之间共享这些对象。 这使进程内或进程外正在运行的对象能够对其文件存储进行同等的增量访问。 由于单独将 COM 加载到每个进程中,因此它使用操作系统支持的共享内存机制,传达打开的元素的状态及其在进程之间的访问模式。

结构化文件中的每个存储和流对象都有一个名称来标识。 该名称是遵循特定约定的字符串。  会将该名称传递给 IStorage 函数,以指定要在其上运行的存储中元素。 根存储对象的名称与基础文件系统中的文件名相同,并且这些名称必须遵循文件系统的约定和限制。 传递给命名文件的存储相关函数的字符串将传递到文件系统,而无需解释或更改。

存储对象中包含的元素的名称可通过实现相关特定存储对象来进行管理。 存储对象的所有实现都必须支持长度为 32 个字符的元素名称,并且某些实现可能支持更长的名称。 名称以保留大小写的形式存储,但比较时不区分大小写。 定义存储元素名称的应用程序必须选择适用于任一情况的名称。

可以使用 COM 实现的函数和接口访问结构化存储文件中的每一个元素。 这意味着,其他应用程序可以使用提供类似目录的服务的 IStorage 接口函数进行导航,籍此浏览文件。 此外,其他应用程序可以使用文件数据,而无需运行写入文件的应用程序。 当 COM 应用程序访问另一个应用程序的结构化存储文件时,标准 Windows 访问权限适用,并且应用程序必须具有足够的特权。

COM 对象可以读取和将自身写入持久存储。 客户端会查询 COM 对象上的其中持久性相关接口,具体取决于操作上下文。 COM 对象可以实现以下接口的任意组合:

  • IPersistStorage:COM 对象会读取并将其持久状态写入存储对象。 客户端通过此接口为对象提供 IStorage 指针。 这是唯一包含增量访问语义的持久性接口;
  • IPersistStream:COM 对象会读取并将其持久状态写入流对象。 客户端通过此接口为对象提供 IStream 指针;
  • IPersistFile:COM 对象直接读取并将其持久状态写入基础系统上的文件。 除非通过这些接口访问基础文件,否则此接口不涉及 IStorage 或 IStream,但 IPersistFile 接口没有存储和流的语义。 客户端会为对象提供文件名,并调用 Save 或 Load 函数;
数据传输

结构化存储为 COM 对象和进程之间的数据交换提供了基础,此数据交换称为统一数据传输。 在 OLE 2 中实现 COM 之前,Windows 上的数据传输通过传输协议(例如剪贴板和拖放协议)指定。 每个传输协议都有自己的函数集,这些函数将协议绑定到查询,并且需要特定的代码来处理每个不同的协议和交换过程。 统一数据传输表示使用 IDataObject 接口进行的所有数据传输,该接口会将通用数据交换操作与传输协议分开。

IDataObject 接口会封装有关数据、查询和枚举以及检测对象中数据更改的通知的标准 get 和 set 操作。 统一数据传输支持数据格式的丰富描述,以及使用不同的存储介质进行数据传输。

在统一数据传输期间,所有协议都会交换指向 IDataObject 接口的指针。 服务器是数据源并实现一个数据对象,该对象在任何数据交换协议中都可用。 客户端从任何协议接收 IDataObject 指针时,都会使用数据并从数据对象请求数据。 指针交换完成后,双方通过 IDataObject 接口以统一的方式处理数据交换。

COM 定义了两个支持统一数据传输的数据结构。 FORMATETC 结构表示通用剪贴板格式,而 STGMEDIUM 结构将传输介质表示为内存句柄。

客户端会创建 FORMATETC 结构,以指示它从数据源请求的数据类型,而数据源使用它来描述提供的格式。 客户端会请求数据源的 IEnumFORMATETC 接口,以在数据源中查询可用格式。 

客户端会创建 STGMEDIUM 结构并将其传递给 GetData 方法,而数据对象会采用提供的 STGMEDIUM 结构返回数据。

STGMEDIUM 结构使客户端和数据源都能够选择最高效的交换介质。 例如,如果要交换的数据非常大,则数据源可以指示将基于磁盘的介质作为其首选格式,而不是主内存。 这种灵活性会使得数据交换非常高效,就像将指针传递给 IStorage 或 IStream 那样快。 

当数据更改时,数据源的客户端可能需要通知。 COM 使用建议接收器对象处理数据更改通知,该对象将实现 IAdviseSink 接口。 建议接收器对象和 IAdviseSink 接口由客户端实现,客户端会将 IAdviseSink 指针传递给数据源。 当数据源检测到基础数据更改时,它会调用 IAdviseSink 方法来通知客户端。

远程处理

COM 支持远程和分布式计算。 接口远程处理使成员函数能够返回指向位于不同进程或不同主计算机的 COM 对象的接口指针。 执行接口远程处理的基础结构对客户端和对象服务器都是透明的。 客户端和服务器都不需要彼此的部署详细信息,即可通过远程接口进行通信。 客户端调用同一接口上的成员函数,以与本地主机或远程计算机上的进程内和进程外 COM 对象通信。 客户端无法区分同一接口上的本地和远程调用。

若要与 COM 对象通信,客户端始终将调用进程内实现。 如果 COM 对象是进程内对象,则调用是直接调用。 如果 COM 对象是进程外或远程对象,COM 提供了一个代理实现,该实现将使用远程过程调用 (RPC) 协议将调用转发到对象。

COM 对象始终通过进程内实现从客户端接收调用。 如果调用方是进程内调用方,则调用是直接调用。 如果调用方是进程外或远程调用方,COM 会提供一个存根实现,用于从客户端进程中的代理接收远程过程调用。

封送处理是打包要从代理传输到存根的调用堆栈的过程。 取消封送是在接收端发生的解包。 返回值将被封送处理,并在从存根到代理时取消封送。 这种通信也称为通过线路发送调用。

每种不同的数据类型都有封送处理规则。 接口指针还具有封送处理协议,该协议封装在 CoMarshalInterface 函数中。 在大多数情况下,由系统提供的标准接口封送处理已足够,但 COM 对象可能会根据需要实现自定义接口封送处理,以控制其远程对象代理的创建。

安全性

COM 提供两种形式的应用程序安全性。 一种是激活安全性,这种安全性会指定如何创建新对象、客户端如何连接到新对象和现有对象,以及如何保护某些公共服务(如类表和正在运行的对象表)。 另一种是调用安全性,这种安全性会指定安全如何在客户端与 COM 对象之间的已建立连接中运行。

服务控制管理器 (SCM) 会自动应用激活安全性。 当 SCM 收到检索 COM 对象的请求时,它会根据注册表中存储的安全信息检查请求。

SCM 实现通常提供注册表驱动的配置,用于管理部署的类和主机上的特定用户帐户。 

应用程序会自动应用或强制实施调用安全性。 如果应用程序提供设置信息,COM 将执行必要的检查,以保护应用程序。

自动机制会检查进程的安全性,但不会检查单个对象或方法的安全性。 如果应用程序需要更精细的安全性,COM 会提供应用程序可用于执行自己的安全检查的函数。

可以将自动机制和自定义机制结合使用,因此应用程序可能会要求 COM 执行自动安全检查,然后执行自己的安全检查。

COM 调用安全性服务分为以下几个类别:

  • 客户端和服务器都调用的常规函数,支持初始化自动安全性机制和注册自动身份验证服务。 常规调用安全性 API 是 CoInitializeSecurity 和 CoQueryAuthenticationServices 函数。
  • 客户端代理上的接口,使客户端能够控制单个接口调用的安全性。 IClientSecurity 接口以及 CoQueryProxyBlanket、CoSetProxyBlanket 和 CoCopyProxy 函数在远程对象上提供调用安全性。
  • 服务器端函数和调用上下文接口,使服务器能够检索有关调用的安全信息并模拟调用方。 IServerSecurity接口以及 CoGetCallContext、CoImpersonateClient 和 CoRevertToSelf 函数提供服务器端调用安全性。

通常,客户端会查询 IClientSecurity 接口的 COM 对象,该接口由远程处理层在本地实现。 客户端使用此接口来控制 COM 对象上各个接口代理的安全性,然后再对其中一个接口进行调用。

当调用到达服务器时,服务器可能会调用 CoGetCallContext 函数,以检索 IServerSecurity 接口,这样服务器就可以检查客户端的身份验证,并在必要时模拟客户端。 IServerSecurity 对象在调用期间有效。

调用 CoInitializeSecurity 函数以初始化安全层,并将指定的值设置为安全默认值。 如果进程未调用 CoInitializeSecurity,则 COM 会在首次封送或取消封送接口时自动调用它,从而注册系统默认安全性。 CoInitializeSecurity 函数允许客户端为进程建立默认调用安全性,从而避免在单个代理上使用 IClientSecurity。 CoInitializeSecurity 函数使服务器能够为进程注册自动身份验证服务。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值