公共语言运行时通过称为运行时可调用包装器(RCW)的代理公开 COM 对象。 尽管 RCW 似乎是 .NET 客户端的普通对象,但其主要函数是封送 .NET 客户端和 COM 对象之间的调用。
无论该对象上存在的引用数如何,运行时都为每个 COM 对象创建一个 RCW。 运行时针对每个对象的每个进程维护一个 RCW。 如果在某个应用程序域或单元创建一个 RCW,然后传递一个其他应用程序域或单元的引用,则将使用第一个对象的代理。 请注意,此代理是一个新的托管对象,与初始 RCW 不同;这意味着两个托管对象不相等,但确实表示相同的 COM 对象。 如下图所示,任意数量的托管客户端都可拥有一个对公开 INew
和 INewer
接口的 COM 对象的引用。
下图显示了通过运行时可调用包装器访问 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 接口,但调用 IUnknown 和 IDispatch 接口。 此外,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 |
---|---|
IConnectionPoint 和 IConnectionPointContainer | RCW 对向基于委托的事件公开连接点事件样式的对象执行转换。 |
IDispatchEx (仅限.NET Framework) | 如果类实现 IDispatchEx,RCW 将实现 IExpando。 IDispatchEx 接口是 IDispatch 接口的扩展,与 IDispatch 不同,它允许枚举、添加、删除和区分大小写的成员调用。 |
IEnumVARIANT | 使支持枚举的 COM 类型可被视为集合。 |