Управляемый код в размещенной среде выполнения.NET Core повреждает стек вызывающих
Я играю с примером хостинга.NET Core.
Когда я собираю его "как есть" (64 бит) в Windows, он работает.
Когда я создаю его как 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
Возврат одного без каких-либо параметров, это работает.
Кажется, я что-то упускаю в соглашениях о вызовах, но не могу точно определить, что именно. По умолчанию один stdcall
в Windows, но есть и некоторые различия между x86 и x64. х64 использует специальный x64 fastcall
соглашение, и я подозреваю, что это как-то портит весь процесс при размещении.NET CLR в 32-битном приложении.
Что мне нужно изменить, чтобы запустить это? Нужно ли создавать собственное (хостовое) приложение, используя определенное соглашение о вызовах? Нужно ли украшать управляемые методы специальными атрибутами? Или, может быть, каким-то образом настроить размещенную среду выполнения.NET Core?
1 ответ
Как отметил @HansPassant в комментариях:
Объявление указателей на функции имеет решающее значение, для x86 вам приходится иметь дело с несовместимыми соглашениями о вызовах. Там нет различий между
cdecl
а такжеstdcall
в х64. Требуется больше макро супа, декларация Windows будетtypedef int (__stdcall *report_callback_ptr)(int progress);
, так далее.
Это хитрость.
Обратный вызов и указатель на функцию управляемого метода должны быть дополнительно украшены __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 */ }
Оказывается, управляемый маршалер рассматривает управляемые методы как __stdcall
на х86, что неудивительно.
Применение этих изменений приводит к тому, что образец работает при сборке в виде приложения x86, на котором размещается среда выполнения x86 .NET Core.