string -> string
我想在C# 程序中使用具有以下类型 :: 的 Haskell 函数
。
我想使用hs-dotnet来连接两个世界。作者声称这是可能的,但没有提供此案例的样本。提供的唯一示例是使用 Haskell 的 .NET 的示例。
有没有这种用法的样本,或者如何使用它?(我在桥接程序集上使用了.NET Reflector,但我什么都不懂。)
虽然您的方法可行,但值得注意的是,不幸的是您遇到的困难是您自己造成的(而不是GHC中的错误):((以下假设您在构建 DLL 时使用了 GHC 文档并将RTS加载到 DLL main )。
对于第一部分,您提出的内存分配问题,有一种更简单的 C# 本机方式来处理这个问题,这是不安全的代码。在不安全代码中分配的任何内存都将在托管堆之外分配。因此,这将消除对 C 技巧的需要。
第二部分是C#中LoadLibrary的使用。P/Invoke找不到您的导出的原因很简单:在您的 Haskell 代码中,您使用 声明了导出语句ccall
,而在 .NET 中,标准命名约定是stdcall
,这也是Win32 API 调用的标准。
stdcall
并且ccall
在参数清理方面具有不同的名称修饰和职责。
特别是,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);
也应该可以工作,但它不会因为 Marshaller 期望 String 已经被分配 CoTaskMemAlloc 并且会调用 CoTaskMemFree 和crash。
如果你想完全留在管理的土地上,你总是可以这样做
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);
然后它可以用作
string result = Marshal.PtrToStringUni(myExportedFunction("hello"));
工具可在此处获得http://hackage.haskell.org/package/Hs2lib-0.4.8
更新:我最近发现了一个大问题。我们必须记住,.NET 中的 String 类型是不可变的。因此,当编组器将其发送到 Haskell 代码时,我们得到的 CWString 是原始的副本。我们必须释放它。在 C# 中执行 GC 时,它不会影响 CWString,它是一个副本。
然而问题是当我们在 Haskell 代码中释放它时,我们不能使用 freeCWString。指针未使用 C (msvcrt.dll) 的 alloc 分配。有三种方法(我知道)可以解决这个问题。
就像更新一样,我通过制作一个haskell DLL并以这种方式桥接两个世界来解决了这个问题。
如果你想走同样的路,一定要使用::CoTaskMemAlloc
为.net 世界分配数据。另一个问题是 LoadLibrary/GetProcAdress 的使用,由于某些未知原因,导入不会按预期的方式自动工作。一篇更深入的文章,帮助从 .net 调用 haskell。
你当然可以至少从 C 中调用 Haskell——你在 Haskell 文件中使用“外部导出”,GHC 生成一个 C 头文件,然后你可以导入并使用它从 C 中调用 Haskell。
我还没有看到为 .NET 绑定做过这样的事情——所以我认为最好问问作者——Sigbjorn——和 haskell-cafe@ 上的例子。
如果您想在 .NET 上使用 Haskell,只需使用F#。