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