互操作封送处理控制调用期间,如何在方法自变量中传递数据,以及如何在托管内存和非托管内存之间返回值。 互操作封送处理是由公共语言运行时的封送处理服务执行的运行时活动。
大多数数据类型在托管和非托管内存中都有常见的表示形式。 互操作封送处理程序为你处理这些类型。 其他类型在托管内存中可能不明确或者根本不在其中表示。
不明确的类型可能具有多种映射到单个托管类型的非托管表示形式,或者可能缺少类型信息(如数组的大小)。 对于不明确的类型,封送器提供默认表示形式和替代表示形式,其中存在多个表示形式。 可以向封送处理程序提供有关它如何封送不明确类型的显式指令。
平台调用和 COM 互操作模型
公共语言运行时提供两种机制,用于与非托管代码进行互作:
- 平台调用,使托管代码能够调用从非托管库导出的函数。
- COM 互作,使托管代码能够通过接口与组件对象模型 (COM) 对象进行交互。
平台调用和 COM 互操作都根据需要使用互操作封送处理在调用方和被调用方之间准确地来回移动方法参数。 如下图所示,平台调用方法调用从托管代码流向非托管代码,除非涉及 回调函数,否则不会反向流动。 即使平台调用调用只能从托管代码流向非托管代码,但数据也可以作为输入或输出参数向两个方向流动。 COM 互操作方法调用可以在任一方向流动。
在最低级别,这两种机制都使用同一种互操作封送处理服务;不过,某些数据类型则仅受 COM 互操作或平台调用支持。 有关详细信息,请参阅默认封送处理行为。
封送处理和 COM 单元
互操作封送处理程序在公共语言运行时堆和非托管堆之间封送数据。 每当调用方和被调用方无法操作数据的同一个实例时就会发生封送处理。 互操作封送处理程序使调用方和被调用方都能够看上去像是在操作同一数据,即使它们都有自己的数据副本。
COM 也有一个在 COM 单元或不同的 COM 进程之间封送数据的封送处理程序。 在同一个 COM apartment 中的托管代码和非托管代码之间进行调用时,互操作封送器是唯一涉及的封送器。 在调用不同的 COM 单元或不同进程中的托管代码和非托管代码时,互操作封送器和 COM 封送器两者都参与其中。
COM 客户端和托管服务器
具有由 Regasm.exe(程序集注册工具) 注册的类型库的导出的托管服务器有一个设置为 ThreadingModel
的 Both
注册表项。 此值指示可以在单线程单元(STA)或多线程单元(MTA)中激活服务器。 服务器对象在与其调用方相同的单元中创建,如下表所示:
COM 客户端 | .NET 服务器 | 封送处理要求 |
---|---|---|
STA |
Both 变为 STA。 |
相同单元封送处理。 |
纽约大都会交通局 (MTA) |
Both 变为 MTA。 |
相同单元封送处理。 |
由于客户端和服务器位于同一单元中,因此互操作封送处理服务将自动处理所有数据封送。 下图显示了在同一个 COM 样式的单元内的托管和非托管堆之间进行的互操作封送处理服务。
如果计划导出托管服务器,请注意,COM 客户端确定服务器的单元。 MTA 中初始化的 COM 客户端调用的托管服务器必须确保线程安全。
托管客户端和 COM 服务器
托管客户端公寓的默认设置为 MTA;然而,.NET 客户端的应用程序类型可以改变默认设置。 例如,Visual Basic 客户端单元设置为 STA。 可以使用 System.STAThreadAttribute、System.MTAThreadAttribute属性、Thread.ApartmentState属性或Page.AspCompatMode属性来检查和更改托管客户端的公寓设置。
组件的作者设置 COM 服务器的线程相关性。 下表显示 .NET 客户端和 COM 服务器的单元设置的组合。 同时还显示产生的针对这些组合的封送处理要求。
.NET 客户端 | COM 服务器 | 封送处理要求 |
---|---|---|
MTA (默认值) | 纽约大都会交通局 (MTA) STA |
互操作封送处理。 互操作封送处理和 COM 封送处理。 |
STA | 纽约大都会交通局 (MTA) STA |
互操作封送处理和 COM 封送处理。 互操作封送处理。 |
当托管客户端和非托管服务器位于同一单元中时,互操作封送处理服务处理所有数据封送。 但是,当客户端和服务器在不同的单元中初始化时,还需要 COM 封送处理。 下图显示跨单元调用的元素:
对于跨单元封送处理,可以执行下列操作:
接受跨单元封送处理的系统开销,它只在存在许多跨边界调用时才值得注意。 若要使调用能够成功跨过单元边界,必须注册 COM 组件的类型库。
通过将客户端线程设置为 STA 或 MTA 来更改主线程。 例如,如果你的 C# 客户端调用多个 STA COM 组件,可以通过将主线程设置为 STA 来避免跨线程封送处理。
注释
C# 客户端的线程设置为 STA 后,对 MTA COM 组件的调用将需要跨单元封送。
有关显式选择单元模型的说明, 请参阅 托管和非托管线程处理。
封送远程调用
与跨单元封送处理一样,只要对象驻留在不同的进程中,托管代码和非托管代码之间的每个调用就都涉及 COM 封送处理。 例如:
- 在远程主机上调用托管服务器的 COM 客户端使用分布式 COM (DCOM)。
- 在远程主机上调用 COM 服务器的托管客户端使用 DCOM。
下图显示互操作封送处理和 COM 封送处理如何跨进程和主机边界提供信道:
保留身份
公共语言运行时保留托管引用和非托管引用的标识。 下图显示了跨进程和主机边界的直接非托管引用(上行)和直接托管引用(下行)的流。
在此图中:
一个非托管客户端从托管对象获取对 COM 对象的引用,该托管对象从远程主机获取这个引用。 远程处理机制为 DCOM。
托管客户端从 COM 对象获取对托管对象的引用,该对象从远程主机获取此引用。 远程处理机制为 DCOM。
注释
必须注册托管服务器的导出类型库。
调用方和被调用方之间的进程边界的数目并不相干;对于进程内和进程外调用都会发生相同的直接引用处理。
托管远程处理
运行时还提供托管远程处理,可用于跨进程和主机边界在托管对象之间建立通信通道。 托管远程处理可以适应通信组件之间的防火墙,如下图所示:
使用 SOAP 或 TcpChannel 类跨防火墙进行远程调用
某些非托管调用可以通过 SOAP 传递,如服务组件和 COM 之间的调用。
相关主题
标题 | DESCRIPTION |
---|---|
默认封送处理行为 | 描述互操作封送处理服务用于封送数据的规则。 |
使用平台调用封送数据 | 介绍如何声明方法参数并将参数传递给非托管库导出的函数。 |
用 COM 互操作封送数据 | 介绍如何自定义 COM 包装器以更改封送行为。 |
如何:将 Managed-Code DCOM 迁移到 WCF | 介绍如何从 DCOM 迁移到 WCF。 |
如何:映射 HRESULT 和异常 | 介绍如何将自定义异常映射到 HRESULT,并提供从每个 HRESULT 到 .NET Framework 中可比较的异常类的完整映射。 |
使用泛型类型进行互作 | 描述使用泛型类型进行 COM 互操作性时支持哪些操作。 |
与非托管代码交互操作 | 介绍公共语言运行时提供的互作性服务。 |
高级 COM 互作性 | 提供有关将 COM 组件合并到 .NET Framework 应用程序中的详细信息的链接。 |
互作的设计注意事项 | 提供有关编写集成 COM 组件的提示。 |