我目前正在做微基准测试,以便更好地理解本机代码性能的clr。在下面的示例中,我得到一个StackOverflowException
,当编译为release并在没有附加调试器的情况下执行。编译为debug-build或运行附带调试器的程序时,我没有得到异常。此外,我也只能使用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());
}
如果我将此代码编译为release并在未连接调试器的情况下启动它,则输出为:
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。
答案 0 :(得分:5)
__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);
传递-1
将在32位进程中等同于传递地址0xffffffff
。尝试取消引用该地址没有任何好处。
另一个问题是调用约定不匹配。本机代码使用__cdecl
,但托管代码使用默认值__stdcall
。将托管代码更改为:
[DllImport("Native.dll", EntryPoint = "NativeTestFunction",
CallingConvention = CallingConvention.Cdecl),
SuppressUnmanagedCodeSecurityAttribute]
同样适用于其他导入。