运行时可调用包装

公共语言运行时通过称为运行时可调用包装器(RCW)的代理公开 COM 对象。 尽管 RCW 似乎是 .NET 客户端的普通对象,但其主要函数是封送 .NET 客户端和 COM 对象之间的调用。

无论该对象上存在的引用数如何,运行时都为每个 COM 对象创建一个 RCW。 运行时针对每个对象的每个进程维护一个 RCW。 如果在某个应用程序域或单元创建一个 RCW,然后传递一个其他应用程序域或单元的引用,则将使用第一个对象的代理。 请注意,此代理是一个新的托管对象,与初始 RCW 不同;这意味着两个托管对象不相等,但确实表示相同的 COM 对象。 如下图所示,任意数量的托管客户端都可拥有一个对公开 INewINewer 接口的 COM 对象的引用。

下图显示了通过运行时可调用包装器访问 COM 对象的过程:

通过 RCW 访问 COM 对象的过程。

使用派生自类型库的元数据,运行时将同时创建要调用的 COM 对象和该对象的包装器。 每个 RCW 在它包装的 COM 对象上维护接口指针的缓存,并在不再需要 RCW 时在 COM 对象上释放其引用。 运行时在 RCW 上执行垃圾回收。

在其他活动中,RCW 代表包装的对象封送托管代码和非托管代码间的数据。 具体而言,只要客户端和服务器之间传递的数据具有不同的表示形式,RCW 就为方法参数和方法返回值提供封送。

标准包装强制执行内置的封送处理规则。 例如,当 .NET 客户端将类型 string 作为参数的一部分传递给非托管对象时,包装器会将类型 string 转换为类型 BSTR 。 如果 COM 对象向其托管调用方返回 BSTR,则调用方会收到 string。 客户端和服务器都发送和接收他们熟悉的数据。 其他类型的不需要转换。 例如,标准包装器将始终在托管和非托管代码之间传递一个 4 字节整数,而无需转换类型。

封送处理所选接口

运行时可调用包装器(RCW)的主要目标是隐藏托管和非托管编程模型之间的差异。 若要创建无缝转换,RCW 会使用选定的 COM 接口,而不将其公开给 .NET 客户端,如下图所示。

下图显示了 COM 接口和运行时可调用包装器:

带有接口的运行时可调用包装器的屏幕截图。

当创建为早期绑定对象时,RCW 为特定类型。 它实现 COM 对象实现的接口,并从对象的接口公开方法、属性和事件。 在图中,RCW 暴露 INew 接口,但调用 IUnknownIDispatch 接口。 此外,RCW 会将 INew 接口的所有成员公开给 .NET 客户端。

RCW 使用下表列出的接口,这些接口由其包装的对象公开。

接口 DESCRIPTION
IDispatch 用于通过反射后期绑定到 COM 对象。
IErrorInfo 提供错误的详细说明、错误的源、帮助文件、帮助上下文以及定义错误的接口的 GUID(对于 .NET 类,始终为 GUID_NULL)。
IProvideClassInfo 如果包装的 COM 对象实现 IProvideClassInfo,RCW 将从此接口中提取类型信息,以提供更好的类型标识。
IUnknown 针对对象标识、类型强制和生存期管理:

- 对象标识
运行时通过比较每个对象的 IUnknown 接口的值来区分 COM 对象。
- 类型强制转换
RCW 可识别 QueryInterface 方法执行的动态类型发现。
生命周期管理
使用 QueryInterface 方法,RCW 获取并保存对非托管对象的引用,直到运行时对包装器执行垃圾回收(释放非托管对象)。

RCW 选择性地使用下表中列出的接口,这些接口由其包装的对象公开。

接口 DESCRIPTION
IConnectionPointIConnectionPointContainer RCW 对向基于委托的事件公开连接点事件样式的对象执行转换。
IDispatchEx (仅限.NET Framework) 如果类实现 IDispatchEx,RCW 将实现 IExpandoIDispatchEx 接口是 IDispatch 接口的扩展,与 IDispatch 不同,它允许枚举、添加、删除和区分大小写的成员调用。
IEnumVARIANT 使支持枚举的 COM 类型可被视为集合。

另请参阅