И вот наконец мне удалось решить это самому! После недели борьбы. И любые полезные комментарии, чтобы добавить его, этот ответ приветствуется.
Я сделал это следующим образом:
В DLL класса C #:
Мне нужно было найти способ "экспортировать" мою функцию fn_called_from_hs()
в небезопасный собственный код. Я обнаружил, что это не совсем просто, и в Интернете действительно есть довольно много статей, объясняющих, как это делается. Все сводится к фактической разборке .NET DLL с помощью инструмента ildasm
, и в сгенерированном промежуточном файле IL добавление префикса «.export» к функции, которую мы хотим экспортировать, и затем снова сборка файла IL обратно в Форма DLL с использованием ilasm
.
Я обнаружил, что все эти шаги автоматизированы с помощью NUGetPackage Неуправляемый экспорт , поэтому первым шагом является установка этого пакета как части вашего проекта .NET, а затем добавление атрибута DLLExport
в ваш функция для экспорта. Убедитесь, что в вашем списке импорта RGiesecke.DllExport
:
using RGiesecke.DllExport;
[DllExport("fn_called_from_hs", CallingConvention=CallingConvention.Cdecl)]
public static string FnCalledFromHs()
{
// Your function code here
}
Как видите, я назвал фактическую функцию как FnCalledFromHs()
(в соответствии с соглашением об именах в C #), но экспортировал ту же функцию, что и fn_called_from_hs
(в соответствии с соглашением об именах в Haskell). Таким образом, когда вы смотрите на код на Haskell, вы не увидите ничего, что выглядит неуместным.
Один из самых важных шагов для фактической работы - убедиться, что проект, в который вы экспортируете функцию, выполнен для цели x64 или x86 - по умолчанию проекты нацелены на «Любой процессор» - RGiesecke.DllExport
делает не работает, если проект нацелен на «Любой процессор».
Теперь соберите проект, чтобы получить csharp.dll , который содержит экспортированный файл fn_called_from_hs
.
Перед установкой ссылки на код Haskell
Mingw GCC (который ghc используется в Windows для внутреннего использования) может напрямую связываться с DLL, если они были созданы с помощью gcc ранее. Однако, поскольку мы создали нашу C # DLL с использованием компилятора .NET csc, нам нужно специально создать библиотеку импорта, которую может видеть наш Haskell.
Мы используем два инструмента: gendef
и dlltool
, оба из которых находятся в папке «mingw \ bin» в вашей установке ghc (поэтому, конечно, вам нужно иметь это в вашей PATH env переменная для доступа к этим инструментам).
Вот как я это сделал:
Создан файл .def, который, в свою очередь, можно использовать для создания библиотеки импорта:
gendef csharp.dll
Создана библиотека импорта с помощью dlltool:
dlltool -k -d csharp.def -l csharp.lib
Скопировал указанную выше библиотеку импорта в тот же каталог, в котором находилась DLL.
Последний шаг (ниже) теперь будет использовать эту библиотеку импорта для фактического связывания с библиотекой csharp.
Связывание кода на Haskell с указанной выше библиотекой импорта
Это было немного хитрее, и, возможно, заставило меня исправить ошибку в стеке / GHC (не уверен), но уже подал сюда .
Я сделал это следующим образом:
Добавлен extra-lib-dirs
в мой stack.yaml и добавлен каталог, в котором был создан вышеупомянутый import-lib:
extra-lib-dirs: ["<drive>:\\path\\to\\importlib"]
(обратите внимание, что это также может быть добавлено в ваш package.yaml в разделе "библиотеки", но я решил добавить его в мой stack.yaml).
Добавлено extra-libraries
в мой stack.yaml, под библиотеками.
extra-libraries: csharp
И добавил также опции -l и -L к моим опциям ghc для связывания моей библиотеки. Это то, что я сделал, чтобы обойти (возможную) ошибку , так как стек не передает extra-lib-dirs
и extra-libraries
в ghc и ld во время соединения. Итак, мой последний раздел «library» в package.yaml выглядит следующим образом (сравните его с тем, что было раньше в моем вопросе выше):
library:
source-dirs:
- src
- src/csrc
include-dirs: src/csrc
ghc-options:
- -shared
- -fno-shared-implib
- -lcslib
- -L<drive>:\\path\\to\\importlib
extra-libraries: csharp
Заключение
После всего этого мой код на Haskell теперь просто хорошо собирается с помощью обычной команды stack build
, без каких-либо ошибок "unreferenced symbols". Выполняя мой код на Haskell, я также проверил, что функция c # fn_called_from_hs
действительно была вызвана, и результаты были возвращены правильно.
Конечно, в стороне от c # есть еще кое-что: правильная сортировка параметров и т. Д., И мне пришлось работать над ними, чтобы получить правильный результат. Единственное место, где я могу охватить все эти мелочи, находится в блоге :-)
Пожалуйста, не стесняйтесь перепроверить мое решение, а также прокомментировать любой лучший способ сделать это. Это был лучший способ, который я мог понять после моей борьбы!