2009-09-29 31 views
14

次のタイプのHaskell関数を使用したいと考えています。:: string -> string C#プログラムから。.NETでHaskell関数を呼び出す

hs-dotnetを使用して両方の世界を橋渡ししたいと考えています。作者はそれが可能だと主張しますが、この事例のサンプルは提供しません。提供される唯一のサンプルは、Haskellの.NETを使用するものです。

この使用例または使用方法はありますか? (私はブリッジアセンブリで.NET Reflectorを使用しましたが、私は物事を理解しませんでした)

+0

ハスケルが分かりませんので、私は助けることはできませんが、C#でHaskell関数を実装する方が簡単かどうかは不思議です。この関数はとても重要で難しいので、C#で書き直すことはできませんか? –

+0

さて、私はhaskellで書かれた全プログラムを手に入れました。関数は単なる '単純な'インターフェースなので、C#で実際に書き直すことはできません。 –

+3

Haskellの 'H'はサイレントではありません。したがって、適切な記事 'a'を 'an'の上に使用する必要があります。 –

答えて

14

あなたの方法を動作しますが、それは(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を解放する必要があります。
+0

ありがとう、私はそれを正しい方法で行うことができます。私はドキュメンテーションで溺れてしまったに違いないと思う... –

2

少なくともHaskellをCから呼び出すことができます - あなたはHaskellファイルで "foreign export"を使用し、GHCはCヘッダーをインポートしてCからHaskellに呼び出すことができます。

これは.NETバインディングでは見られませんでした。だから私は作者Sigbjornとhaskell例としては-cafe @です。

4

ちょうどアップデートとして、私はhaskell DLLを作り、そのように2つの世界を橋渡しすることで問題を解決しました。

同じパスを使用する場合は、::CoTaskMemAllocを使用して、.netワールドのデータを割り当ててください。別の問題はLoadLibrary/GetProcAdressの使用です。何らかの不明な理由により、インポートが自動的に正しく動作しません。よりたくさんのin depth articleは、.netからのhaskellの呼び出しに役立ちます。

-11

Haskell on .NETを使用する場合は、F#を使用してください。