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

Rob*_*ert 7 haskell cabal

我想用.NET gui创建一个Haskell应用程序.我想使用cabal作为我的构建工具来利用它的包管理等.我认为Haskell部分应该是调用.NET代码的可执行文件:

  1. 这样就不必手动初始化Haskell RTC,如下所述:http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/win32-dl​​ls.html

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

我发现创建一个使用hs-dotnet调用.NET的Haskell可执行文件相当容易,但我还需要我的GUI代码才能回调到Haskell.我希望使用Haskell的"外部导出"命令来实现这一点,然后通过.NET native interop调用这个导出的函数.但是,"外部导出"功能似乎没有在可执行文件中创建入口点,当我dumpbin /EXPORTS对生成的可执行文件执行操作时,我看不到入口点.我不确定这是否是因为GHC仅在通过-shared交换机创建dll时创建入口点,或者cabal是否添加了禁止创建入口点的标志.

所以我想问题是如何强制GHC在我的Windows可执行文件中创建入口点?或者我会更好地使用.NET可执行文件,通过必要的步骤创建一个带有cabal的Haskell dll并手动初始化Haskell RTC?

Joh*_*n L 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 = ...
Run Code Online (Sandbox Code Playgroud)

现在 Haskell 类型的数据IO ()可以包装在 a 中FunPtr并使用 传递到 GUI 中buttonSetCallback。每当按下按钮时,IO ()就会执行该操作。

createButton :: IO ButtonPtr
createButton = do
    btn <- buttonNew
    let buttonClicked = print "The button was clicked!"
    btnCallback <- makeClickCallback buttonClicked
    buttonSetCallback btn btnCallback
    return btn
Run Code Online (Sandbox Code Playgroud)

对于 s 需要注意的一件事FunPtr是它们不会被垃圾收集。完成后您需要手动释放它们,否则会出现内存泄漏。一个好的做法是永远不要共享FunPtrs,也永远不要在 Haskell 端保留对它们的引用。这样你的对象就可以FunPtr在清理过程中释放它。这需要对 Haskell(一个函数)进行另一个回调,freeFunPtr该回调应该在所有对象之间共享,并且仅在可执行文件终止时才被释放。