あなたの方法を動作しますが、それは(GHCのバグをしていない)残念ながらやってあなたが遭遇したdificulties独自であったことは注目に値します:((以下は、DLLをビルドするときに、GHCのドキュメントを使用を前提とし、あなたのRTSを持っていますDLLメインでの読み込み)
最初の部分は、存在するメモリ割り当ての問題です。これは、安全ではないコードであるC#固有の扱いが非常に簡単です。安全でないコードで割り当てられたメモリは、ヒープ。これはCトリッキーの必要性を否定するでしょう。
2番目の部分はC#でのLoadLibraryの使用です。 n P/Invokeあなたの輸出は非常に簡単です:あなたのHaskellコードではccall
を使用してエクスポートステートメントを宣言しましたが、.NETでは標準の命名規則はstdcall
であり、Win32 APIコールの標準でもあります。
stdcall
およびccall
は、引数のクリーンアップの点で異なる名前のmanglingsとresposibilitiesを持っています。
特に、GHC/GCCは「wEval」にエクスポートされますが、.NETではデフォルトで「_wEval @ 4」が検索されます。これで簡単に修正できます.CallingConvention = CallingConvention.Cdeclを追加するだけです。
しかし、この呼び出し規約を使用すると、呼び出し元はスタックをクリーンアップする必要があります。だからあなたは余分な仕事が必要です。今あなたがWindows上でこれを使うつもりなら、Haskell関数をstdcall
としてエクスポートしてください。これにより、.NETコードがよりシンプルになります。
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern string myExportedFunction(string in);
ほとんど正確です。
何が正しいのは、たとえば
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public unsafe static extern char* myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);
ためのLoadLibraryのためのこれ以上の必要性などないだろう。管理された文字列を取得するには、たとえば
String result = new String(myExportedFunction("hello"));
などを使用します。
一つは
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
[return : MarshalAs(UnmanagedType.LPWStr)]
public static extern string myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);
があまりにも動作するはずだと思うだろうが、マーシャラーがString CoTaskMemAllocに割り当てられていることを期待ので、それはしませんし、それとクラッシュにCoTaskMemFreeを呼び出します。
string result = Marshal.PtrToStringUni(myExportedFunction("hello"));
ツールがここにhttp://hackage.haskell.org/package/Hs2lib-0.4.8
アップデート利用可能であるとして、あなたが管理する土地で完全に滞在したい場合
、あなたは常に
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);
を行うことができますし、それを使用することができます:私が最近発見した大きな落とし穴が多少あります。 .NETのString型は不変であることを覚えておく必要があります。したがって、マーシャラーがHaskellコードを送信すると、そこに到達するCWStringはオリジナルのコピーです。我々はこれを解放するを持っているを持っています。 GCがC#で実行されると、コピーであるCWStringには影響しません。
しかし問題は、私たちがHaskellコードで解放するとき、私たちはfreeCWStringを使うことができないということです。ポインタはC(msvcrt.dll)のallocで割り当てられませんでした。これを解決するには、私が知っている3つの方法があります。
- ハスケル関数を呼び出すときに、StringではなくC#コードでchar *を使用します。あなたは、あなたが戻り値を呼び出すときにフリーにするポインタを持っているか、fixedを使ってポインタを初期化します。
- HaskellでCoTaskMemFreeをインポートし、Haskellでポインタを解放します。
- Stringの代わりにStringBuilderを使用します。私はこれについて完全にはわかっていませんが、StringBuilderはネイティブポインタとして実装されているので、MarshallerはこのポインタをHaskellコードに渡します(btwも更新できます)。コールが戻った後にGCを実行すると、StringBuilderを解放する必要があります。
ハスケルが分かりませんので、私は助けることはできませんが、C#でHaskell関数を実装する方が簡単かどうかは不思議です。この関数はとても重要で難しいので、C#で書き直すことはできませんか? –
さて、私はhaskellで書かれた全プログラムを手に入れました。関数は単なる '単純な'インターフェースなので、C#で実際に書き直すことはできません。 –
Haskellの 'H'はサイレントではありません。したがって、適切な記事 'a'を 'an'の上に使用する必要があります。 –