Creating a Haskell application with a .NET GUI

I'd like to create a Haskell app with a .NET gui. I'd like to use cabal as my build tool to take advantage of it's package management etc. I think the Haskell part should be the executable that calls into the .NET code as:

  • This avoids having to manually initialize the Haskell RTC as described here: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/win32-dlls.html

  • cabal cannot easily produce Windows dlls: http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Building_DLLs__with_Cabal

  • I've found it fairly easy to create a Haskell executable that call's .NET using hs-dotnet, but I also need my GUI code to call back into Haskell. I was hoping to achieve this using Haskell's "foreign export" command, then call this exported function via .NET native interop. However the "foreign export" function doesn't seem to create an entry point in the executable, I can't see the entry point when I do dumpbin /EXPORTS on the resulting executable. I'm not sure whether this is because GHC only create's entry point's when creating a dll via the -shared switch or whether cabal add a flag that suppresses entry point creation.

    So I guess the question is how do I force GHC to create entry points in my Windows executable? Or would I be better using a .NET executable going though the necessary steps to create a Haskell dll with cabal and manually initializing the Haskell RTC?


    I usually solve this by passing callbacks as function pointers. For example, I have an app where pressing a button needs to call back into Haskell (I'm using Cocoa, but except for the names it's very similar).

    First, I subclass an NSButton object, and give my new ButtonC class a private member of type void(*onClickCallback)() . I also declare a function void setClickCallback(ButtonC *button, void(*callback)()); Implement your subclass so that whenever the button is clicked, the function pointer is called. In .Net, there may be a clever way to do this with delegates (it's been a while since I've used .Net).

    Next, my Haskell binding looks like this (some code omitted):

    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 = ...
    

    Now Haskell data of type IO () can be wrapped in a FunPtr and passed into the GUI with buttonSetCallback . Whenever the button is pressed, the IO () action is performed.

    createButton :: IO ButtonPtr
    createButton = do
        btn <- buttonNew
        let buttonClicked = print "The button was clicked!"
        btnCallback <- makeClickCallback buttonClicked
        buttonSetCallback btn btnCallback
        return btn
    

    One thing to beware of with FunPtr s is that they aren't garbage collected. You need to manually deallocate them when you've finished or you'll have a memory leak. A good practice is to never share FunPtr s, and also never retain references to them on the Haskell side. That way your object can free the FunPtr as part of its cleanup. This requires yet another callback into Haskell (a freeFunPtr function) that should be shared between all your objects and is itself only freed when your executable terminates.

    链接地址: http://www.djcxy.com/p/58428.html

    上一篇: 文件中的信息不正确:'./database

    下一篇: 用.NET GUI创建一个Haskell应用程序