IMarshal 接口 (objidl.h)

使 COM 对象能够定义和管理其接口指针的封送处理。

继承

IMarshal 接口继承自 IUnknown 接口。 IMarshal 也有以下类型的成员:

方法

IMarshal 接口具有这些方法。

 
IMarshal::D isconnectObject

IMarshal::D isconnectObject 方法 (objidl.h) 在关闭之前释放与对象的所有连接。
IMarshal::GetMarshalSizeMax

检索封送处理期间所需的缓冲区的最大大小。
IMarshal::GetUnmarshalClass

检索未配置代码的 CLSID。
IMarshal::MarshalInterface

IMarshal::MarshalInterface 方法 (objidl.h) 封送接口指针。
IMarshal::ReleaseMarshalData

IMarshal::ReleaseMarshalData 方法 (objidl.h) 销毁封送数据包。
IMarshal::UnmarshalInterface

IMarshal::UnmarshalInterface 方法 (objidl.h) 取消封送接口指针。

注解

封送 处理是将数据打包成数据包以传输到其他进程或计算机的过程。 取消封送是在接收端恢复该数据的过程。 在任何给定的调用中,方法参数在一个方向上封送和取消封送,而返回值在其他方向封送和取消封送。

尽管封送处理适用于所有数据类型,但接口指针需要特殊处理。 根本问题是,在一个地址空间中运行的客户端代码如何正确地取消引用指向位于不同地址空间中的对象的接口的指针。 COM 解决方案适用于客户端应用程序,以便通过代理对象或代理与原始对象通信,代理位于客户端的进程中。 代理保存对原始对象上的接口的引用,并为客户端提供指向自身接口的指针。 当客户端对原始对象调用接口方法时,其调用实际上将转到代理。 因此,从客户端的角度来看,所有调用都是进程内调用。

在收到调用时,代理封送方法参数,并通过某些进程间通信方式(如 RPC)将它们传递到服务器进程中的代码,从而取消封送参数并将其传递到原始对象。 此相同的代码封送处理将值返回以传输回代理,代理将取消封送值并将其传递给客户端应用程序。

IMarshal 提供用于在客户端进程中创建、初始化和管理代理的方法;它不规定代理应如何与原始对象通信。 IMarshal 的 COM 默认实现使用 RPC。 自行实现此接口时,可以自由选择认为适合应用程序的任何进程间通信方法(共享内存、命名管道、窗口句柄、RPC),简言之,无论什么都有效。

IMarshal 默认实现

COM 使用自己的 IMarshal 接口内部实现来封送任何未提供其自己的实现的对象。 COM 通过查询 IMarshal 对象来做出此决定。 如果缺少接口,COM 默认为其内部实现。

IMarshal 的 COM 默认实现为每个对象使用泛型代理,并根据需要为对象上实现的每个接口创建单独的存根和代理。 此机制是必需的,因为 COM 无法提前知道给定对象可以实现哪些特定接口。 不使用 COM 默认封送处理的开发人员,而是选择编写自己的代理和封送处理例程,在编译时知道在其对象上找到的所有接口,因此确切地了解需要哪些封送代码。 COM 在为所有对象提供封送支持时必须在运行时执行此操作。

接口代理驻留在客户端进程中;接口存根驻留在服务器中。 每对共同处理接口的所有封送处理。 每个接口代理的工作是封送参数和未封送的返回值和输出参数,这些参数在后续调用其接口时来回传递。 每个接口存根的工作是取消封存函数参数并将其传递给原始对象,然后封送对象返回的返回值和输出参数。

代理和存根通过 RPC (远程过程调用) 通道进行通信,后者利用系统的 RPC 基础结构进行进程间通信。 RPC 通道实现单个接口 IRpcChannelBuffer,接口代理和存根都持有指针。 代理和存根调用 接口以获取封送数据包,将数据发送到其对应数据包,并在数据包完成后销毁数据包。 接口存根还包含指向原始对象的指针。

对于任何给定接口,代理和存根都作为同一类的实例实现,这些实例针对系统注册表中的每个接口在 标签 ProxyStubClsid32 下列出。 此条目将接口的 IID 映射到其代理和存根对象的 CLSID 。 当 COM 需要封送接口时,它会在系统注册表中查找以获取相应的 CLSID。 此 CLSID 标识的服务器实现接口代理和接口存根。

通常,此 CLSID 引用的类由工具自动生成,该工具的输入是给定接口的函数签名和语义的说明,以某些接口描述语言编写。 尽管强烈建议使用此类语言,但出于准确性考虑,建议使用此类语言,但不需要这样做。 代理和存根只是 RPC 基础结构使用的 COM 组件,因此,只要维护正确的外部协定,就可以以任何所需方式编写。 设计新接口的程序员负责确保所有接口代理和存根都同意其封送数据的表示形式。

创建接口代理时,始终聚合到更大的代理中,该代理将对象作为一个整体表示。 此对象代理还聚合 COM 泛型代理对象,该对象称为 代理管理器。 代理管理器实现两个接口: IUnknownIMarshal。 可以在对象上实现的所有其他接口通过单个接口代理的聚合在其对象代理中公开。 持有指向对象代理的指针的客户端“相信”它持有指向实际对象的指针。

客户端进程中需要一个将对象表示为一个整体的代理,以便客户端可以区分对在完全不同的对象上实现的相同接口的调用。 但是,在对象本身所在的服务器进程中不存在此类要求,因为所有接口存根仅与为其创建它们的对象通信。 无法进行其他连接。

与接口代理相比,接口存根不会聚合,因为对于某些外部客户端而言,它们不需要是较大整体的一部分。 连接后,会为接口存根提供指向服务器对象的指针,该服务器对象应将接收的方法调用转发到该服务器对象。 尽管从概念上引用存根管理器很有用,这意味着服务器端 RPC 基础结构中为给定对象的远程处理服务的任何代码段和状态,但没有直接要求代码和状态采用任何特定且指定良好的形式。

客户端首次请求指向特定对象上的接口的指针时,COM 在服务器进程中加载 IClassFactory 存根,并使用它将第一个指针封送回客户端。 在客户端进程中,COM 加载类工厂对象的泛型代理,并调用其 IMarshal 实现来取消映射第一个指针。 然后,COM 创建第一个接口代理,并为其提供指向 RPC 通道的指针。 最后,COM 返回指向客户端的 IClassFactory 指针,客户端使用该指针调用 IClassFactory::CreateInstance,并向其传递对接口的引用。

回到服务器进程中,COM 现在会创建 对象的新实例,以及所请求接口的存根。 此存根将接口指针封送回客户端进程,此时会为对象本身创建另一个对象代理。 同时创建的是请求接口的代理,该代理是返回到客户端的指针。 随着对对象上其他接口的后续调用,COM 将根据需要加载相应的接口存根和代理。

创建新的接口代理时,COM 会向其提供指向代理管理器实现 的 IUnknown 的指针,它将所有 QueryInterface 调用委托给该代理。 每个接口代理实现自己的两个接口:它表示的接口和 IRpcProxyBuffer。 接口代理将自己的接口直接公开给客户端,客户端可以通过在代理管理器上调用 QueryInterface 来获取其指针。 但是,只有 COM 可以调用 IRpcProxyBuffer,该 IRpcProxyBuffer 用于连接代理并断开与 RPC 通道的连接。 客户端无法查询接口代理以获取指向 IRpcProxyBuffer 接口的 指针。

在服务器端,每个接口存根实现 IRpcStubBuffer。 充当存根管理器的服务器代码调用 IRpcStubBuffer::Connect ,并将接口存根传递给其 对象的 IUnknown 指针。

当接口代理收到方法调用时,它会通过调用 IRpcChannelBuffer::GetBuffer 从其 RPC 通道获取封送数据包。 封送参数的过程会将数据复制到缓冲区中。 封送处理完成后,接口代理会调用 IRpcChannelBuffer::SendReceive 将封送的数据包发送到相应的接口存根。 当 IRpcChannelBuffer::SendReceive 返回时,参数封送到的缓冲区将被替换为包含从接口存根封送的返回值的新缓冲区。 接口代理取消对返回值进行封送,调用 IRpcChannelBuffer::FreeBuffer 以释放缓冲区,然后将返回值返回到方法的原始调用方。

IRpcChannelBuffer::SendReceive 的实现实际上将请求发送到服务器进程,并且知道如何识别服务器进程,并在该进程中标识应向其发送请求的对象。 通道实现还知道如何将请求转发到该过程中的相应存根管理器。 接口存根从提供的缓冲区中解编参数,在服务器对象上调用指示的方法,并将返回值封送回由调用 IRpcChannelBuffer::GetBuffer 分配的新缓冲区中。 然后,通道将返回数据包传输回接口代理,该代理仍在 IRpcChannelBuffer::SendReceive 中间,该代理返回到接口代理。

只要满足以下条件,接口代理的特定实例就可以用于为多个接口提供服务:

接口存根的单个实例也可以为多个接口提供服务,但前提是该接口集具有严格的单继承关系。 存在此限制是因为存根可以将方法调用定向到多个接口,前提是它事先知道哪些方法是在哪些接口上实现的。

在不同时间,代理和存根都需要分配或释放内存。 例如,接口代理需要分配内存,以便向调用方返回输出参数。 在这方面,接口代理和接口存根只是普通的 COM 组件,因为它们应使用标准任务分配器。 (请参阅 CoGetMalloc.)

要求

要求
最低受支持的客户端 Windows 2000 专业版 [桌面应用 |UWP 应用]
最低受支持的服务器 Windows 2000 Server [桌面应用 |UWP 应用]
目标平台 Windows
标头 objidl.h (包括 ObjIdl.h)

另请参阅

IStdMarshalInfo