Управляемый код в размещенной среде выполнения.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.

Другие вопросы по тегам