【问题标题】:StackOverflowException while calling native (DllImport) function [duplicate]调用本机(DllImport)函数时出现 StackOverflowException [重复]
【发布时间】:2015-06-09 13:51:46
【问题描述】:

我目前正在做微基准测试,以便更好地了解 clr 到本机代码的性能。在以下示例中,当编译为发行版并在未附加调试器的情况下执行时,我得到了一个StackOverflowException。编译为 debug-build 或运行带有调试器的程序时,我没有遇到异常。此外,我也仅使用SuppressUnmanagedCodeSecurityAttribute-Attribute 得到此错误。

我使用 c 和 VS2013 (platformtoolset=v120) 构建了一个 dll,其中包含一个函数:

__declspec(dllexport) int __cdecl NativeTestFunction(int a, int b, int c, int d)
{
    return a + c + b + d;
}

在我的 C# 程序中,我使用 DllImport 调用此函数并进行一些计时测量:

[DllImport("Native.dll", EntryPoint = "NativeTestFunction")]
static extern int NativeTestFunction(int a, int b, int c, int d);

[DllImport("Native.dll", EntryPoint = "NativeTestFunction"), SuppressUnmanagedCodeSecurityAttribute]
static extern int NativeTestFunctionSuppressed(int a, int b, int c, int d);

static void Main(string[] args)
{
    byte[] data = new byte[64];
    int c = 0;

    Stopwatch sw = Stopwatch.StartNew();

    for (int i = 0; i < 10000000; i++)
        c += NativeTestFunction(2, -1, -2, 1);

    Console.WriteLine("Unsuppressed: " + sw.Elapsed.ToString());
    sw = Stopwatch.StartNew();

    for (int i = 0; i < 10000000; i++)
        c += NativeTestFunctionSuppressed(2, -1, -2, 1);

    Console.WriteLine("Suppressed..: " + sw.Elapsed.ToString());
}

如果我将此代码编译为发行版并在没有附加调试器的情况下启动它,则输出为:

Unsuppressed: 00:00:00.2666255

Process is terminated due to StackOverflowException.

但是,在附加调试器的情况下执行或编译为调试并在附加或不附加调试器的情况下启动程序都会成功:

Unsuppressed: 00:00:00.2952272
Suppressed..: 00:00:00.1278980

这是 .NET/CLR 中的已知错误吗?我的错误是什么?我认为附加和未附加调试器之间的行为应该是相同的。

此错误发生在 .NET 2.0 和 .NET 4.0 中。我的软件编译为 x86(因此仅针对 x86 进行了测试)以兼容 Native.dll。如果你不想自己设置这个场景,你可以下载我的测试项目:Sourcecode

【问题讨论】:

  • 您的声明是错误的,缺少的 CallingConvention = CallingConvention.Cdecl 使堆栈失衡。是的,当您在没有调试器的情况下运行 Release 构建并且代码优化器可以发挥它的魔力时,这很容易被炸毁。您应该已经收到警告,请确保您没有关闭 PInvokeStackImbalance 调试器助手。
  • 抱歉重复了。在询问时我不知道 __cdecl/__stdcall 是问题所在。

标签: c# .net stack-overflow dllimport


【解决方案1】:
__declspec(dllexport) int __cdecl NativeTestFunction(int a, char* b, int c, int d)

注意b 的类型。它是char*。然后在你写的C#代码中:

[DllImport("Native.dll", EntryPoint = "NativeTestFunction"), 
    SuppressUnmanagedCodeSecurityAttribute]
static extern int NativeTestFunctionSuppressed(int a, int b, int c, int d);

在这里,您将b 声明为int。那不匹配。调用函数时情况会变得更糟。

NativeTestFunctionSuppressed(2, -1, -2, 1);

在 32 位进程中,传递 -1 将等同于传递地址 0xffffffff。尝试取消引用该地址不会有任何好处。

另一个问题是调用约定不匹配。本机代码使用__cdecl,但托管代码使用默认的__stdcall。将托管代码更改为:

[DllImport("Native.dll", EntryPoint = "NativeTestFunction", 
    CallingConvention = CallingConvention.Cdecl), 
    SuppressUnmanagedCodeSecurityAttribute]

对于其他导入也是如此。

【讨论】:

  • 他已经更正了他的源代码......现在可能只是__cdecl与默认stdcall的问题
  • 对不起!这是我的错。它必须是整数。 (我的第一个测试是使用不安全和固定字节*,我只是没有更新 c 示例。)
  • 感谢您的回答。我认为CallingConvention 应该是CallingConvention.Cdecl
  • 你是对的。我太马虎了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-10
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 2021-02-24
  • 1970-01-01
  • 2017-05-17
相关资源
最近更新 更多