“托管到本地转换”在发生时会出现什么情况?(涉及IT技术)

28

我了解CLR在某些情况下需要进行封送处理,但假设我有:

using System.Runtime.InteropServices;
using System.Security;

[SuppressUnmanagedCodeSecurity]
static class Program
{
    [DllImport("kernel32.dll", SetLastError = false)]
    static extern int GetVersion();

    static void Main()
    {
        for (; ; )
            GetVersion();
    }
}

当我用调试器进入这个程序时,我总是看到:

假设没有需要执行的封送(对吗?),可以有人解释一下在这个“托管到本地转换”中实际发生了什么以及为什么它是必要的吗?


6
也许调用栈中的那一行仅仅是提供信息,告诉你何时进行了转换。 - David Heffernan
@DavidHeffernan:哦……我猜那也可以……但我有一种感觉还有其他事情发生了(虽然我很想被证明是错的!)。 - user541686
1
有一种叫做“封送(marshaling)”的技术,因为CLR会自动使用OutAttribute将返回值视为普通参数。 - Paolo Moretti
我不确定你的意思。你是在谈论回调吗? - David Heffernan
1
@RichardSzalay:哦,你说得完全正确。但我想问题是,为什么在“mscoreei.dll”之后没有过渡呢? - user541686
显示剩余4条评论
5个回答

17

首先需要设置调用栈以便进行STDCALL操作。这是Win32的调用约定。

接下来,运行时将推送一个所谓的执行框架。有许多不同类型的框架:安全性声明、GC保护区域、本机代码调用等。

运行时使用这样的框架来跟踪当前正在运行的本机代码。这对于可能并发的垃圾回收和可能的其他内容都有影响。它还有助于调试器。

实际上这里没有太多事情发生。这是一个非常简单的代码路径。


7
除了负责为您转换参数并确定调用约定的封送层外,运行时还需要执行一些其他操作,以保持内部状态的一致性。
需要检查安全上下文以确保调用代码被允许访问本机方法。当前的托管堆栈帧需要被保存,这样运行时就可以进行回溯栈来处理调试和异常处理(更不用说调用托管回调的本机代码)。需要设置内部状态的内部位,以指示我们当前正在运行本机代码。
此外,寄存器可能需要被保存,这取决于需要跟踪哪些内容以及哪些内容被调用约定保证将被恢复。在寄存器(局部变量)中的GC根可能需要以某种方式标记,以便在本机方法期间不会被垃圾回收。
因此,主要是堆栈处理和类型封送,其中还包括一些安全性方面的内容。虽然它涉及的内容不多,但它确实会对调用较小的本机方法造成相当大的障碍。例如,尝试P/Invoke到优化的数学库通常不会带来性能提升,因为开销足以抵消任何可能的好处。一些性能分析结果在这里进行了讨论。

性能分析报告的链接已失效。 - pvoosten

5
我知道这个问题已经有人回答了,但我很惊讶没有人建议您在调试窗口中显示外部代码。如果您在[Native to Managed Transition]行上右键单击并选中Show External Code选项,则可以看到在转换中调用的确切.NET方法。这可能会给您一个更好的想法。以下是一个示例:

Displaying a Native to Managed Transition


Visual Studio 的哪个版本?在 Visual Studio 2017 中,我可以对调用堆栈中的 [External Code] 标签执行此操作,但对于 [Native to Managed Transition] 没有影响。 - giles

0

我实在看不出有多少需要做的。我怀疑这主要是为了提供信息,告诉你部分调用堆栈显示本地函数,并指示IDE和调试器在过渡期间可能会表现出不同的行为(由于调试器在处理托管代码时会有非常不同的方式,因此您期望的某些功能可能无法正常工作)

但我想您应该能够通过检查过渡周围的反汇编来发现它是否执行任何异常操作。


1
很难检查反汇编代码:VS告诉我,我的C#代码中的指令是call 0xFFEFBFAC,但在那个地址上什么也没有--当我进入那个函数时,它自动跳转到_GetVersionStub@0,该函数位于75834437。所以显然跳过了一些代码。也许我可以用WinDbg试试?不确定,可以尝试一下看看。 - user541686

-1

由于您正在调用一个dll,它需要退出托管环境。它进入了Windows核心。您正在突破.NET屏障并进入不像.NET运行的Windows代码。


2
不是很有信息量。你只是在改述问题而已。 :) - jalf
2
@jalf 但是“突破.NET障碍”是一个不错的改述。 - phoog
我不确定他在问什么。在托管到本地转换期间到底发生了什么。它调用了位于.NET框架之外的Windows DLL,这就是为什么它会通知用户发生了“托管到本地转换”的原因。没有试图聪明。一旦转换为本地代码,资源需要由用户释放和清理。一旦变成本地代码,可用的调试信息就不多了。 - iefpw
3
如果你不确定别人在问什么,请不要回答这个问题。你已经有足够的声望去留下一条评论请求澄清。你的“答案”基本上没有意义,更不用说有帮助了。 - Andrew Barber
@AndrewBarber 我同意,但是阅读并且点赞“不开玩笑!!”的评论带来的纯粹快乐,更是弥补了这个,嗯,回答的不足。 - Evgeniy Berezovsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接