调用本机 (DllImport) 函数时发生 StackOverflowException

StackOverflowException while calling native (DllImport) function

我目前正在做微基准测试,以更好地了解 clr 到本机代码的性能。在下面的示例中,当编译为发布并在没有附加调试器的情况下执行时,我得到了 WhosebugException。当编译为 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 WhosebugException.

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

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

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

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

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

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

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

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

其他导入也一样。