托管的 .NET Core 运行时中的托管代码破坏了调用者堆栈

Managed code in a hosted .NET Core runtime corrupts the caller stack

我正在玩.NET Core runtime hosting example

当我在 Windows 上构建它 "as is"(64 位)时,它可以工作。

当我将其构建为 32 位应用程序(在 Windows 上)并将 .NET 运行时更改为 x86 (-r win-x86) 时,它崩溃了。

这就是正在发生的事情。在 managedDelegate returns 之后,调用方 (main()) 的堆栈已损坏,应用程序崩溃。

doWork_ptr managedDelegate;

createManagedDelegate(
        hostHandle,
        domainId,
        "ManagedLibrary, Version=1.0.0.0",
        "ManagedLibrary.ManagedWorker",
        "DoWork",
        (void**)&managedDelegate);

char* ret = managedDelegate("Test job", 5, sizeof(data) / sizeof(double), data, ReportProgressCallback);

当我将托管方法 (DoWork) 更改为 void-返回不带任何参数的方法时,它起作用了。

我似乎遗漏了有关调用约定的某些内容,但无法确定具体内容。默认的是 stdcall on Windows,但是 x86 和 x64 之间也有一些差异。 x64 使用特殊的 x64 fastcall 约定,我怀疑它在 32 位应用程序中托管 .NET CLR 时以某种方式搞砸了整个事情。

我需要更改什么才能获得此 运行?我是否需要使用特定的调用约定构建本机(主机)应用程序?我需要用特殊属性装饰托管方法吗?或者以某种方式配置托管的 .NET Core 运行时?

正如@HansPassant 在评论中提到的:

The declaration of the function pointers is critical, for x86 you get to deal with incompatible calling conventions. There is no distinction between cdecl and stdcall in x64. More macro soup needed, the Windows declaration would be typedef int (__stdcall *report_callback_ptr)(int progress);, etc.

这就是诀窍。

回调和托管方法函数指针需要额外用__stdcall属性修饰:

typedef int (__stdcall *report_callback_ptr)(int progress);
typedef char* (__stdcall *doWork_ptr)(const char* jobName, int iterations, int dataSize, double* data, report_callback_ptr callbackFunction);

回调实现也需要修饰:

int __stdcall ReportProgressCallback(int progress) { /* implementation here */ }

事实证明,托管封送拆收器将托管方法视为 x86 上的 __stdcall,这不足为奇。

应用这些更改可以使示例在构建为托管 x86 .NET Core 运行时的 x86 应用程序时正常工作。