使用.NET GUI创建Haskell应用程序

7
我想创建一个带有.NET界面的Haskell应用程序。 我想使用cabal作为构建工具,以利用其包管理等功能。 我认为Haskell部分应该是调用.NET代码的可执行文件,如下所示:
  1. 这样可以避免手动初始化Haskell RTC,如此描述:http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/win32-dlls.html

  2. cabal无法轻松生成Windows dll:http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Building_DLLs__with_Cabal

我发现使用hs-dotnet很容易创建调用.NET的Haskell可执行文件,但我还需要让我的GUI代码回调到Haskell。我希望使用Haskell的"foreign export"命令实现此目的,然后通过.NET本地交互调用此导出的函数。但是,“foreign export”函数似乎不会在可执行文件中创建入口点,当我在生成的可执行文件上运行dumpbin /EXPORTS时找不到入口点。我不确定这是因为GHC只在使用-shared开关创建dll时才创建入口点,还是因为cabal添加了一个抑制入口点创建的标志。
所以我想问的问题是:如何强制GHC在我的Windows可执行文件中创建入口点?或者我最好使用.NET可执行文件,经过必要的步骤创建一个带有cabal的Haskell dll,并手动初始化Haskell RTC?

你真的想要用 .net 和 Haskell 编程吗?那我建议你将 Haskell 写成一个 DLL 并从 .net 中调用函数。你提到的第一点很容易克服,即使 cabal 很好用,你仍然必须使用其他工具来构建 .net 部分,所以你的第二点更像是一个提示(即不要使用它)。 - Jonke
1个回答

4

我通常通过传递回调函数指针来解决这个问题。例如,我有一个应用程序,在其中按下按钮需要回调到Haskell(我正在使用Cocoa,但除了名称外,它非常相似)。

首先,我创建了一个NSButton对象的子类,并为我的新ButtonC类添加了一个私有成员void(*onClickCallback)()。我还声明了一个函数void setClickCallback(ButtonC *button, void(*callback)());实现您的子类,以便每当单击按钮时,调用函数指针。在.Net中,可能有一种聪明的方法可以使用委托来完成这项工作(我已经有一段时间没有使用.Net了)。

接下来,我的Haskell绑定看起来像这样(省略了一些代码):

module Foreign.Button where

data ButtonObj
type ButtonPtr = ForeignPtr ButtonObj

foreign import ccall unsafe "setClickCallback"
  c_setClickCallback :: Ptr ButtonObj -> FunPtr (IO ()) -> IO ()

foreign import ccall "wrapper"
  makeClickCallback :: IO () -> IO (FunPtr (IO ()))

buttonSetCallback :: ButtonPtr -> FunPtr (IO ()) -> IO ()
buttonSetCallback btn cb =
    withForeignPtr btn $ \p -> c_setClickCallback p cb

buttonNew :: IO ButtonPtr
buttonNew = ...

现在,类型为IO ()的Haskell数据可以被包装在FunPtr中,并使用buttonSetCallback传递到GUI。每当按钮被按下时,就会执行IO ()操作。
createButton :: IO ButtonPtr
createButton = do
    btn <- buttonNew
    let buttonClicked = print "The button was clicked!"
    btnCallback <- makeClickCallback buttonClicked
    buttonSetCallback btn btnCallback
    return btn

要注意的一件事是,FunPtr不能进行垃圾回收。 当您完成时,需要手动释放它们,否则将导致内存泄漏。 一个好的做法是永远不要共享FunPtr,并且也不要在Haskell侧保留对它们的引用。 这样,您的对象可以在其清理过程中释放FunPtr。这需要再次回调到Haskell (一个freeFunPtr函数),应该在所有您的对象之间共享,并且仅在可执行文件终止时才被释放。

看起来这是一个合理的方法,我猜一旦创建了FunPtr,你可以将其作为IntPtr传递给.NET,但是一旦你在.NET世界中拥有了这个函数指针,如何调用它呢? - Robert
1
@Robert - 你可以将它封送到委托,然后调用该委托。详情请参见:http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.marshal.getdelegateforfunctionpointer.aspx - John L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接