2012-03-19 17 views
7

Chciałbym stworzyć aplikację Haskell z guzikiem .NET. Chciałbym użyć Cabal jak mój narzędzie build wykorzystać to zarządzanie pakiet itd. Myślę, że część Haskell powinien być wykonywalny, który wzywa do kodu .NET jak:Tworzenie aplikacji Haskell z GUI .NET

  1. ten unika konieczności ręcznej zainicjować Haskell RTC jak opisano tutaj: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/win32-dlls.html

  2. Cabal nie można łatwo produkować dLL Windows: http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Building_DLLs__with_Cabal

Znalazłem to dość łatwe do stworzenia Haskell wykonywalnych .NET tak nazywać Korzystanie hs-dotnet, ale potrzebuję również mojego kodu GUI do ponownego połączenia z Haskell. Miałem nadzieję osiągnąć to za pomocą komendy Haskell "export zagranicznych", a następnie wywołać tę funkcję eksportu za pośrednictwem natywnego współdziałania .NET. Jednak funkcja "eksportu zagranicznego" nie wydaje się tworzyć punktu wejścia w pliku wykonywalnym, nie widzę punktu wejścia, gdy robię dumpbin /EXPORTS na wynikowym pliku wykonywalnym. Nie jestem pewien, czy to dlatego, że GHC tworzy tylko punkt wejścia podczas tworzenia biblioteki dll za pomocą przełącznika -shared lub czy cabal dodaje flagę, która blokuje tworzenie punktu wejścia.

Sądzę więc, że pytanie brzmi: jak wymusić na GHC tworzenie punktów wejścia w pliku wykonywalnym Windows? A może lepiej wykorzystałbym plik wykonywalny .NET, wykonując niezbędne kroki, aby stworzyć bibliotekę Haskell z kabałą i ręcznie zainicjować Haskell RTC?

+0

Naprawdę chcesz kodować w .net i w haskell, które biorę? Następnie chciałbym zasugerować, aby dostać twój haskell w dll i wywołać funkcje z .net. Twój punkt 1 jest łatwy do pokonania, a nawet jeśli jest dobry, nadal musisz użyć czegoś innego, aby zbudować swoją .net część, więc twój punkt 2 jest bardziej wskazówką (tj. Nie używaj go) – Jonke

Odpowiedz

4

Zwykle rozwiązuję ten problem, przekazując wywołania zwrotne jako wskaźniki funkcji. Na przykład mam aplikację, w której naciśnięcie przycisku wymaga ponownego połączenia z Haskell (używam Cocoa, ale oprócz nazw jest bardzo podobny).

Najpierw podklasuj obiekt NSButton i nadaj mojej nowej klasie ButtonC prywatny element typu void(*onClickCallback)(). Deklaruję również funkcję void setClickCallback(ButtonC *button, void(*callback)()); Zaimplementuj swoją podklasę, aby za każdym razem, gdy kliknięto przycisk, wywołano wskaźnik funkcji. W .Net może być sprytny sposób na zrobienie tego z delegatami (minęło trochę czasu odkąd używam .Net).

Następnie my wiążących Haskell wygląda tak (niektóre kod pominięta):

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

Teraz Haskell dane typu IO() może być zawinięte w FunPtr i przekazywane do GUI z buttonSetCallback. Po każdym naciśnięciu przycisku wykonywane jest działanie IO().

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

Jedno wystrzegać się ze FunPtr s jest to, że nie są one zbierane śmieci. Musisz je zwolnić ręcznie, gdy skończysz lub będziesz miał wyciek pamięci. Dobrą praktyką jest nigdy nie udostępniać FunPtr s, a także nigdy nie zachowują odniesień do nich po stronie Haskell. W ten sposób obiekt może zwolnić FunPtr jako część jego oczyszczania. Wymaga to kolejnego wywołania zwrotnego do funkcji Haskell (funkcja freeFunPtr), która powinna być współdzielona między wszystkimi obiektami użytkownika i jest ona sama zwalniana po zakończeniu działania pliku wykonywalnego.

+0

Wydaje się rozsądnym podejściem, I zgadnij raz, gdy zostanie utworzony FunPtr, możesz go przekazać.NET jako IntPtr, ale gdy masz wskaźnik funkcji w świecie .NET, jak go wywołasz? – Robert

+1

@Robert - Możesz przekazać go delegatowi, a następnie przywołać delegata. Szczegóły na stronie: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getdelegateforfunctionpointer.aspx –

+0

Brzmi, dzięki! – Robert

Powiązane problemy