【问题标题】:What exactly happens during a "managed-to-native transition"?在“托管到本机的过渡”期间究竟会发生什么?
【发布时间】:2012-03-01 08:49:32
【问题描述】:

我知道 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();
    }
}

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

鉴于不需要进行封送处理(对吗?),有人可以解释一下在这个“托管到本机的转换”中实际发生了什么,为什么有必要?

【问题讨论】:

  • 也许调用堆栈中的那一行只是提供信息,让您知道何时过渡
  • @DavidHeffernan:哦.......我想这也有效......但我感觉还有其他事情发生(虽然我很想被证明是错误的!) .
  • @DavidHeffernan:有趣的是,从 kernel32.dll 转到 mscoree.dll 时,它并没有说同样的话……所以这让我怀疑确实发生了一些事情。跨度>
  • 存在封送处理,因为 CLR 使用 OutAttribute 自动将返回值视为普通参数。
  • @RichardSzalay:哦,你完全正确。但我想问题是,为什么在mscoreei.dll 之后没有过渡?

标签: c# .net pinvoke unmanaged managed


【解决方案1】:

首先需要设置调用堆栈,以便 STDCALL 可以发生。这是 Win32 的调用约定。

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

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

所以实际上这里并没有发生很多事情。这是一个非常简洁的代码路径。

【讨论】:

    【解决方案2】:

    除了负责为您转换参数和确定调用约定的编组层之外,运行时还需要做一些其他事情来保持内部状态的一致性。

    需要检查安全上下文,以确保允许调用代码访问本机方法。需要保存当前托管堆栈帧,以便运行时可以执行堆栈回溯以进行调试和异常处理(更不用说调用托管回调的本机代码)。需要设置内部状态位以指示我们当前正在运行本机代码。

    此外,可能需要保存寄存器,具体取决于需要跟踪的内容以及调用约定保证恢复的内容。寄存器(本地)中的 GC 根可能需要以某种方式进行标记,以便它们不会在本机方法期间被垃圾收集。

    所以主要是堆栈处理和类型封送处理,以及一些安全性的东西。虽然它不是大量的东西,但它代表了调用较小的本机方法的重大障碍。例如,尝试 P/Invoke 到优化的数学库很少会带来性能提升,因为开销足以抵消任何潜在的好处。讨论了一些性能分析结果here

    【讨论】:

    • 性能分析报告的链接已失效
    【解决方案3】:

    我知道这个问题已经得到解答,但我很惊讶没有人建议您在调试窗口中显示外部代码。如果您右键单击[Native to Managed Transition] 行并勾选Show External Code 选项,您将准确看到在转换中调用了哪些.NET 方法。这可能会给你一个更好的主意。这是一个例子:

    【讨论】:

    • 什么版本的 Visual Studio?在 Visual Studio 2017 中,我可以为调用堆栈中的 [External Code] 标签执行此操作,但对 [Native to Managed Transition] 没有影响
    【解决方案4】:

    由于您正在调用 dll。它需要脱离托管环境。它正在进入 Windows 核心。您正在打破 .net 障碍并进入与 .NET 运行方式不同的 Windows 代码。

    【讨论】:

    • 信息量不是很大。你只是在改写问题。 :)
    • @jalf 但“打破 .NET 障碍”的措辞很好。
    • 我不确定他在问什么。在托管到本地转换期间到底发生了什么。它正在调用 .NET 框架之外的 windows dll,这就是为什么它通知用户已经“管理到本机转换”的原因。没有试图变得聪明。一旦过渡到本机,用户就需要释放和清理资源。一旦它变成原生的,就没有很多可用的调试信息了。
    • @iefpw 如果您不确定某人在问什么,不要回答问题。您有足够的代表发表评论要求澄清。您的“答案”基本上没有意义,更没有帮助。
    • @AndrewBarber 我同意,但是阅读的纯粹乐趣,甚至更多的是“不开玩笑!!”评论不仅仅是弥补这个,em,回答。
    【解决方案5】:

    我真的看不出有太多需要做的事情。我怀疑它主要是提供信息,向您指示调用堆栈的一部分显示本机函数,并指示 IDE 和调试器在该转换期间可能表现不同(因为托管代码在调试器中的处理方式非常不同,并且您期望的某些功能可能不起作用)

    但我想你应该能够通过检查转换周围的反汇编来找出答案。看看它有没有什么异常。

    【讨论】:

    • 很难检查反汇编:VS告诉我我的C#代码中的指令是call 0xFFEFBFAC,但是那个地址没有任何东西——当我进入那个函数时,它会自动转到_GetVersionStub@0,位于75834437。所以它显然跳过了一些代码。也许我可以用 WinDbg 做到这一点?不确定,我可以试试看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-23
    • 2011-01-18
    • 1970-01-01
    相关资源
    最近更新 更多