调用本机(DllImport)函数时发生StackOverflow异常

本文关键字:StackOverflow 异常 函数 本机 DllImport 调用 | 更新日期: 2023-09-27 17:54:30

我目前正在进行微观基准测试,以更好地理解clr到本机代码的性能。在下面的例子中,我得到了一个StackOverflowException,当它被编译为发行版并在没有附加调试器的情况下执行时。当编译为调试版本或运行带有调试器的程序时,我不会遇到异常。此外,我也得到了这个错误只有SuppressUnmanagedCodeSecurityAttribute-属性。

我使用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中已知的Bug吗?我犯了什么错我认为附加调试器和未附加调试器之间的行为应该相同。

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

调用本机(DllImport)函数时发生StackOverflow异常

__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);

在这里您声明bint。这不匹配。当你调用函数时,情况会变得更糟。

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

在32位处理中,传递-1将等同于传递地址0xffffffff。试图取消对那个地址的引用不会有什么好处。

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

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

其他进口也是如此。