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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With