C# 托管.NET核心运行时中的托管代码会损坏调用方堆栈

C# 托管.NET核心运行时中的托管代码会损坏调用方堆栈,c#,.net,.net-core,stack-corruption,.net-runtime,C#,.net,.net Core,Stack Corruption,.net Runtime,我在玩这个游戏 当我在Windows上按原样(64位)构建它时,它可以工作 当我将其构建为32位应用程序(在Windows上)并将.NET运行时更改为x86(-r win-x86)时,它会崩溃 这就是正在发生的事情。managedDelegate返回后,调用方(main())的堆栈损坏,应用程序崩溃 doWork_ptr managedDelegate; createManagedDelegate( hostHandle, domainId, "

我在玩这个游戏

当我在Windows上按原样(64位)构建它时,它可以工作

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

这就是正在发生的事情。
managedDelegate
返回后,调用方(
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
——返回一个不带任何参数的方法时,它就工作了

似乎我遗漏了一些关于呼叫约定的内容,但无法准确指出具体内容。Windows上的默认值是
stdcall
,但x86和x64之间也存在一些差异。x64使用了一种特殊的
x64快速调用
约定,我怀疑它在32位应用程序中托管.NET CLR时会把整个事情搞砸


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

如评论中提到的@HansPassant:

函数指针的声明非常重要,对于x86,您需要处理不兼容的调用约定。x64中的
cdecl
stdcall
之间没有区别。如果需要更多的宏,Windows声明将是
typedef int(u stdcall*report_callback_ptr)(int progress),等等

这就是诀窍

回调和托管方法函数指针还需要使用
\uu 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上将托管方法视为
\uu stdcall
,这并不奇怪


当作为x86.NET核心运行时宿主的x86应用程序构建时,应用这些更改可以使示例正常工作。

正如注释中提到的@HansPassant:

函数指针的声明非常重要,对于x86,您需要处理不兼容的调用约定。x64中的
cdecl
stdcall
之间没有区别。如果需要更多的宏,Windows声明将是
typedef int(u stdcall*report_callback_ptr)(int progress),等等

这就是诀窍

回调和托管方法函数指针还需要使用
\uu 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上将托管方法视为
\uu stdcall
,这并不奇怪


当作为x86.NET核心运行时宿主的x86应用程序构建时,应用这些更改可以使该示例正常工作。

是的,他们在该示例上抄近路,显然打算将其仅用于x64运行时。这对于.NETCore来说是很正常的,他们为什么决定支持x86有点神秘。可能需要做太多的工作才能移除它。函数指针的声明对于x86来说是至关重要的。x64中的cdecl和stdcall之间没有区别。如果需要更多的宏,Windows声明将是
typedef int(u stdcall*report_callback_ptr)(int progress),等等@HansPassant还有32位的机器,例如Acer Aspire Switch 10I将通过,他们在这里射击messenger,我真的不知道他们为什么这样编码样本。只需分享您在自己的帖子中发现的内容,并将其标记为答案。是的,他们在该示例上抄近路,显然打算将其仅用于x64运行时。这对于.NETCore来说是很正常的,他们为什么决定支持x86有点神秘。可能需要做太多的工作才能移除它。函数指针的声明对于x86来说是至关重要的。x64中的cdecl和stdcall之间没有区别。如果需要更多的宏,Windows声明将是
typedef int(u stdcall*report_callback_ptr)(int progress),等等@HansPassant还有32位的机器,例如Acer Aspire Switch 10I将通过,他们在这里射击messenger,我真的不知道他们为什么这样编码样本。只需分享你在自己的帖子中发现的内容,并将其标记为答案。