使用.NET Native生成时,参数不会传递到x86上的非托管DLL

本文关键字:x86 DLL Native NET 参数 使用 | 更新日期: 2023-09-27 18:26:14

我正在构建一个Windows 10通用应用程序(手机+平板电脑)+库。在这个解决方案中,我有一个C++dll项目,它构建了从C#调用的非托管my.dll。DLL导出如下:

// === C++ ===
typedef struct { int f1; uint32_t f2; } R;
// A and B are also structures.
MY_EXPORT R the_function( A *a, const B *b, const uint8_t *c );
// === C# ===
[DllImport( "my.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl )]
extern static R the_function(A a, B b, byte[] c);
[StructLayout( LayoutKind.Sequential )]
internal struct R
{
    public int f1;  // Actually a enum but it shouldn’t matter.
    public uint f2_id;
} 
internal struct A
{
    IntPtr nativePtr;
}
internal struct B
{
    IntPtr nativePtr;
}

该测试应用程序可在ARM和X64平台上运行。如果取消选中"使用.NET本机工具链编译",它可以在X86上工作。

如果选中"使用.NET本机工具链编译",则X86上的非托管DLL将崩溃,表示存在访问冲突。我可以在调试和发布版本中复制。

在使用调试器时,我发现参数的传递方式有错误。在C#方面,在一些编译器生成的C#代码中有这样一行:

unsafe___value = global::McgInterop.my_PInvokes.the_function( a, b, unsafe_c );

在调试器中,我确认参数是可以的。

在C++方面,这些值是错误的。b的值是a中传递的,c的值是b中传递的。

我试图创建一个极简主义的例子,但失败了,它还可以。my.dll导出了100+导出的__cdecl方法,这是一个大型的跨平台C++SDK,我正在努力将其引入Windows 10平台,看起来其他方法都可以工作。

你知道这里发生了什么吗?或者至少我该如何隔离这个问题?提前谢谢。

更新:好的,这里有一个最低限度的重复。

非托管代码:

typedef struct
{
    int f1;
    DWORD f2;
} R;
R __cdecl nativeBug( int a, int b )
{
    CStringA str;
    str.Format( "Unmanaged DLL: a = %i, b = %i'n", a, b );
    ::OutputDebugStringA( str );
    R res
    {
        11, 12
    };
    return res;
}

C#商店应用程序:

[StructLayout( LayoutKind.Sequential )]
struct R
{
    public int f1;
    public uint f2;
}
[DllImport( "NativeBugDll.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl )]
extern static R nativeBug( int a, int b );
private void Page_Loaded( object sender, RoutedEventArgs e )
{
    App.Current.UnhandledException += app_UnhandledException;
    R r = nativeBug( 1, 2 );
    Debug.WriteLine( "Result: f1={0}, f2={1}", r.f1, r.f2 );
}
private void app_UnhandledException( object sender, UnhandledExceptionEventArgs e )
{
    Debug.WriteLine( "Unhandled exception: " + e.Message );
}

没有.NET Native的调试输出很好:

Unmanaged DLL: a = 1, b = 2
Result: f1=11, f2=12

以下是.NET本机构建的调试输出:

Unmanaged DLL: a = 91484652, b = 1
Unhandled exception: Object reference not set to an instance of an object.
STATUS_STACK_BUFFER_OVERRUN encountered

然后视觉工作室完全挂起了。

X64版本即使使用.NET Native也能正常工作。

使用.NET Native生成时,参数不会传递到x86上的非托管DLL

哎呀!看起来.NET Native中可能存在错误。我已经请微软的人来看看。如果您想在内部与我们联系,请随时发送邮件至dotnetnative@microsoft.com.

我会随着我们的了解而更新。

编辑:因此,如果一个本机函数返回这样的结构,那么肯定会有一个真正的错误。优化器最终处于这样一种状态:它在两个参数之后向堆栈推送一个额外的参数,这就是导致错误的原因。

我打开了一个错误,我们将为VS.的更新2修复这个错误

C#:

[StructLayout(LayoutKind.Sequential)]
struct R
{
   public int f1;
   public int f2;
    }
[DllImport("DllImport_NativeDll.dll")]
extern static R nativeBug(int a, int b);
public static void Run()
{
        R r = nativeBug(1, 2);
}

本地:

typedef struct
{
    int f1;
    int f2;
} R;
extern "C" __declspec(dllexport) R nativeBug(int a, int b)
{
    R res
    {
        11, 12
    };
    return res;
}

生成的代码:

00f1766b 8b55fc          mov     edx,dword ptr [ebp-4]
00f1766e 52              push    edx
00f1766f 8b45f8          mov     eax,dword ptr [ebp-8]
00f17672 50              push    eax
00f17673 8d4ddc          lea     ecx,[ebp-24h]
00f17676 51              push    ecx <-- Bonus and unfortunate push
00f176ab ff1524b4d200    call    dword ptr [PInvokeAndCom!_imp__nativeBug (00d2b424)]
MY_EXPORT R the_function( A *a, const B *b, const uint8_t *c );

前两个参数包含结构的地址。

extern static R the_function(A a, B b, byte[] c);

在这里,您按值传递structs。考虑到问题中的代码,这是我能看到的唯一区别。要传递结构的地址,请将C#更改为:

extern static R the_function(ref A a, ref B b, byte[] c);