2009-06-22 13 views
11

Dies ist wahrscheinlich eine wirklich einfache Frage zu beantworten, aber aus irgendeinem Grund habe ich wirklich damit zu kämpfen.Haskell fremden Import Stdcall auf DLL-Funktion

Ich habe eine DLL in C geschrieben, um auf Hardwareebene auf Protokollebene zuzugreifen, und ich möchte ein Haskell-Programm schreiben, das einige dieser C-Funktionen aufruft. Hier ist ein Ausschnitt des entsprechenden C-Header (mit Namen nur geringfügig aufgrund möglicher copyrighting Probleme verschleiert):

#ifdef HWDRIVER_EXPORTS 
#define HWDRIVER_API __declspec(dllexport) 
#else 
#define HWDRIVER_API __declspec(dllimport) 
#endif 
HWDRIVER_API int HW_Init(void); 

Dies hat sich als DLL in Visual Studio 2003 erstellt worden sind, und ich habe erfolgreich geladen, die DLL von beiden C und C#, also bin ich zuversichtlich, dass die DLL gut funktioniert. Die DLL heißt "hw-driver.dll".

Als nächstes ist hier die Quelle Haskell Code nur zu testen, ob ich richtig die DLL laden und die einfachste Funktion nennen:

{-# LANGUAGE ForeignFunctionInterface #-} 
module Main 
    where 
import Foreign 
import Foreign.C 

foreign import stdcall "hw-driver" "HW_Init" hwInit :: IO (CInt) 

main = do 
    x <- hwInit 
    if x == 0 
     then putStr "Successfully initialized" 
     else putStr "Could not initialize" 

Die Linie, die mir Mühe ist die ausländische Import Linie gibt. Wie ich es verstehe, ist die Syntax ausländische (Import/Export) (CCall/stdcall) BibliotheksnameC-Funktionsnamehaskell-function-name :: Haskell Typdeklaration. Also meins sollte ausländische Import stdcall (weil Sie stdcall beim Laden einer DLL in Win32) "hw-driver" (weil die Datei heißt "hw-driver.dll" und es befindet sich im selben Verzeichnis wie dlltest.hs) "HW_Init" (der Name der Funktion in C) hwInit :: IO (Cint) (Leere Argumente, Rückgabe eines int).

Allerdings, wenn ich ghci dlltest.hs versuchen laufen, erhalte ich die folgende Ausgabe:

[1 of 1] Compiling Main    (dlltest.hs, interpreted) 

dlltest.hs:8:43: parse error on input `"' 
Failed, modules loaded: none. 

Zeile 8, Spalte 43 ist die ersten Anführungszeichen auf HW_INIT. Okay, also muss ich vielleicht den Bibliotheksnamen und den Funktionsnamen in eine Zeichenfolge schreiben, das habe ich an einigen Stellen gesehen. Wenn ich das versuchen läuft, dann erhalte ich:

[1 of 1] Compiling Main    (dlltest.hs, interpreted) 

dlltest.hs:8:23: Malformed entity string 
Failed, modules loaded: none. 

8:23 das erste Anführungszeichen der neuen Zeichenfolge „hw-Treiber HW_INIT“ ist.

Ich glaube nicht, dass es etwas falsch mit meiner ghc Setup (6.10.3), da kann ich den folgenden Code ausführen, die kopier klebte von Real World Haskell in GHCI war:

{-- snippet pragma --} 
{-# LANGUAGE ForeignFunctionInterface #-} 
{-- /snippet pragma --} 

{-- snippet imports --} 
import Foreign 
import Foreign.C.Types 
{-- /snippet imports --} 

{-- snippet binding --} 
foreign import ccall "math.h sin" 
    c_sin :: CDouble -> CDouble 
{-- /snippet binding --} 

{-- snippet highlevel --} 
fastsin :: Double -> Double 
fastsin x = realToFrac (c_sin (realToFrac x)) 
{-- /snippet highlevel --} 

{-- snippet use --} 
main = mapM_ (print . fastsin) [0/10, 1/10 .. 10/10] 
{-- /snippet use --} 

So lange Frage kurz, wie deklariere ich ordnungsgemäß einen fremden Import auf einer Win32-DLL? Ich konnte nichts auf Google finden.

Und um Art auf dieser Frage zu markieren, werde ich in der Lage sein, ein Programm wie c2hs oder hsc2hs zu verwenden, um die Header-Datei hw-driver.h zu analysieren, so muss ich nicht manuell die ausländischen Importanrufe für alle 20-25 schreiben Funktionen in dieser DLL enthalten? Ich habe auch keine anständigen Beispiele dafür gefunden.


EDIT: ephemient hat darauf hingewiesen, dass die richtige Syntax für die ausländische Import Linie ist:

foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt 

Damit ich bin in der Lage ghci dlltest.hs -lhw-driver anrufen und richtig die Hauptfunktion mit einem erfolgreichen Rückruf Code. Der Befehl ghc --make dlltest.hs -lhw-driver schlägt jedoch mit einem Linkfehler fehl.So, hier ist die ausführliche Ausgabe des Befehls (beachten Sie, dass ich alle hw-Fahrer haben {dll, h, lib} im Arbeitsverzeichnis.):

Glasgow Haskell Compiler, Version 6.10.3, for Haskell 98, stage 2 booted by GHC version 6.10.1 
Using package config file: C:\ghc\ghc-6.10.3\package.conf 
hiding package base-3.0.3.1 to avoid conflict with later version base-4.1.0.0 
wired-in package ghc-prim mapped to ghc-prim-0.1.0.0 
wired-in package integer mapped to integer-0.1.0.1 
wired-in package base mapped to base-4.1.0.0 
wired-in package rts mapped to rts-1.0 
wired-in package haskell98 mapped to haskell98-1.0.1.0 
wired-in package syb mapped to syb-0.1.0.1 
wired-in package template-haskell mapped to template-haskell-2.3.0.1 
wired-in package dph-seq mapped to dph-seq-0.3 
wired-in package dph-par mapped to dph-par-0.3 
Hsc static flags: -static 
*** Chasing dependencies: 
Chasing modules from: *dlltest.hs 
Stable obj: [Main] 
Stable BCO: [] 
Ready for upsweep 
    [NONREC 
     ModSummary { 
     ms_hs_date = Mon Jun 22 13:20:05 Eastern Daylight Time 2009 
     ms_mod = main:Main, 
     ms_imps = [Foreign.C, Foreign] 
     ms_srcimps = [] 
     }] 
compile: input file dlltest.hs 
Created temporary directory: C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0 
*** Checking old interface for main:Main: 
[1 of 1] Skipping Main    (dlltest.hs, dlltest.o) 
*** Deleting temp files: 
Deleting: C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.s 
Warning: deleting non-existent C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.s 
Upsweep completely successful. 
*** Deleting temp files: 
Deleting: 
link: linkables are ... 
LinkableM (Mon Jun 22 13:22:26 Eastern Daylight Time 2009) main:Main 
    [DotO dlltest.o] 
Linking dlltest.exe ... 
*** Windres: 
C:\ghc\ghc-6.10.3\bin/windres --preprocessor="C:\ghc\ghc-6.10.3\gcc" "-BC:\ghc\ghc-6.10.3\gcc-lib/" "-IC:\ghc\ghc-6.10.3\include/mingw" "-E" "-xc" "-DRC_INVOKED" --use-temp-file --input=C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.rc --output=C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.o --output-format=coff 
*** Linker: 
C:\ghc\ghc-6.10.3\gcc -BC:\ghc\ghc-6.10.3\gcc-lib/ -IC:\ghc\ghc-6.10.3\include/mingw -v -o dlltest.exe -DDONT_WANT_WIN32_DLL_SUPPORT dlltest.o -lhw-driver C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.o -LC:\ghc\ghc-6.10.3\base-4.1.0.0 -LC:\ghc\ghc-6.10.3\integer-0.1.0.1 -LC:\ghc\ghc-6.10.3\ghc-prim-0.1.0.0 -LC:\ghc\ghc-6.10.3 -LC:\ghc\ghc-6.10.3/gcc-lib -lHSbase-4.1.0.0 -lwsock32 -lmsvcrt -lkernel32 -luser32 -lshell32 -lHSinteger-0.1.0.1 -lHSghc-prim-0.1.0.0 -lHSrts -lm -lffi -lgmp -lwsock32 -u _ghczmprim_GHCziTypes_Izh_static_info -u _ghczmprim_GHCziTypes_Czh_static_info -u _ghczmprim_GHCziTypes_Fzh_static_info -u _ghczmprim_GHCziTypes_Dzh_static_info -u _base_GHCziPtr_Ptr_static_info -u _base_GHCziWord_Wzh_static_info -u _base_GHCziInt_I8zh_static_info -u _base_GHCziInt_I16zh_static_info -u _base_GHCziInt_I32zh_static_info -u _base_GHCziInt_I64zh_static_info -u _base_GHCziWord_W8zh_static_info -u _base_GHCziWord_W16zh_static_info -u _base_GHCziWord_W32zh_static_info -u _base_GHCziWord_W64zh_static_info -u _base_GHCziStable_StablePtr_static_info -u _ghczmprim_GHCziTypes_Izh_con_info -u _ghczmprim_GHCziTypes_Czh_con_info -u _ghczmprim_GHCziTypes_Fzh_con_info -u _ghczmprim_GHCziTypes_Dzh_con_info -u _base_GHCziPtr_Ptr_con_info -u _base_GHCziPtr_FunPtr_con_info -u _base_GHCziStable_StablePtr_con_info -u _ghczmprim_GHCziBool_False_closure -u _ghczmprim_GHCziBool_True_closure -u _base_GHCziPack_unpackCString_closure -u _base_GHCziIOBase_stackOverflow_closure -u _base_GHCziIOBase_heapOverflow_closure -u _base_ControlziExceptionziBase_nonTermination_closure -u _base_GHCziIOBase_blockedOnDeadMVar_closure -u _base_GHCziIOBase_blockedIndefinitely_closure -u _base_ControlziExceptionziBase_nestedAtomically_closure -u _base_GHCziWeak_runFinalizzerBatch_closure -u _base_GHCziTopHandler_runIO_closure -u _base_GHCziTopHandler_runNonIO_closure -u _base_GHCziConc_runHandlers_closure -u _base_GHCziConc_ensureIOManagerIsRunning_closure 
Reading specs from C:/ghc/ghc-6.10.3/gcc-lib/specs 
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug 
Thread model: win32 
gcc version 3.4.5 (mingw-vista special r3) 
C:/ghc/ghc-6.10.3/gcc-lib/collect2.exe -Bdynamic -o dlltest.exe -u _ghczmprim_GHCziTypes_Izh_static_info -u _ghczmprim_GHCziTypes_Czh_static_info -u _ghczmprim_GHCziTypes_Fzh_static_info -u _ghczmprim_GHCziTypes_Dzh_static_info -u _base_GHCziPtr_Ptr_static_info -u _base_GHCziWord_Wzh_static_info -u _base_GHCziInt_I8zh_static_info -u _base_GHCziInt_I16zh_static_info -u _base_GHCziInt_I32zh_static_info -u _base_GHCziInt_I64zh_static_info -u _base_GHCziWord_W8zh_static_info -u _base_GHCziWord_W16zh_static_info -u _base_GHCziWord_W32zh_static_info -u _base_GHCziWord_W64zh_static_info -u _base_GHCziStable_StablePtr_static_info -u _ghczmprim_GHCziTypes_Izh_con_info -u _ghczmprim_GHCziTypes_Czh_con_info -u _ghczmprim_GHCziTypes_Fzh_con_info -u _ghczmprim_GHCziTypes_Dzh_con_info -u _base_GHCziPtr_Ptr_con_info -u _base_GHCziPtr_FunPtr_con_info -u _base_GHCziStable_StablePtr_con_info -u _ghczmprim_GHCziBool_False_closure -u _ghczmprim_GHCziBool_True_closure -u _base_GHCziPack_unpackCString_closure -u _base_GHCziIOBase_stackOverflow_closure -u _base_GHCziIOBase_heapOverflow_closure -u _base_ControlziExceptionziBase_nonTermination_closure -u _base_GHCziIOBase_blockedOnDeadMVar_closure -u _base_GHCziIOBase_blockedIndefinitely_closure -u _base_ControlziExceptionziBase_nestedAtomically_closure -u _base_GHCziWeak_runFinalizzerBatch_closure -u _base_GHCziTopHandler_runIO_closure -u _base_GHCziTopHandler_runNonIO_closure -u _base_GHCziConc_runHandlers_closure -u _base_GHCziConc_ensureIOManagerIsRunning_closure C:/ghc/ghc-6.10.3/gcc-lib/crt2.o C:/ghc/ghc-6.10.3/gcc-lib/crtbegin.o -LC:\ghc\ghc-6.10.3\base-4.1.0.0 -LC:\ghc\ghc-6.10.3\integer-0.1.0.1 -LC:\ghc\ghc-6.10.3\ghc-prim-0.1.0.0 -LC:\ghc\ghc-6.10.3 -LC:\ghc\ghc-6.10.3/gcc-lib -LC:/ghc/ghc-6.10.3/gcc-lib dlltest.o -lhw-driver C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.o -lHSbase-4.1.0.0 -lwsock32 -lmsvcrt -lkernel32 -luser32 -lshell32 -lHSinteger-0.1.0.1 -lHSghc-prim-0.1.0.0 -lHSrts -lm -lffi -lgmp -lwsock32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt C:/ghc/ghc-6.10.3/gcc-lib/crtend.o 
C:\ghc\ghc-6.10.3\gcc-lib\ld.exe: cannot find -lhw-driver 
collect2: ld returned 1 exit status 
*** Deleting temp files: 
Deleting: C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.o C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0/ghc4428_0.rc 
*** Deleting temp dirs: 
Deleting: C:\DOCUME~1\CHRISC~1\LOCALS~1\Temp\/ghc4428_0 


Wie sich herausstellt, ist die tatsächliche Verknüpfung war nicht so schwierig, wie ich es gemacht habe. Ich verwendete foreign import stdcall, die ich mit einer in Visual Studio 2003 eingebauten DLL korrekt glaubte. Ich musste das Tool pexports für MinGW herunterladen, das die aus einer DLL exportierten Funktionen auflistet. Der Linker gesucht hatte HWInit @ 0 die ganze Zeit, aber pexports sagte der DLL nur HWInit wurde exportiert.

Ich änderte meine Linie zu foreign importccall statt, und ich war erfolgreich in der Lage, das Programm zu verknüpfen entweder von ghc --make dlltest.hs hw-driver.lib oder ghc --make dlltest.hs -L. -lhw-driver wegen, die sowohl der LIB und die DLL-Datei in dem Arbeitsverzeichnis verwenden.

Antwort

5

FFI speC# 4.1.1 Import Declarations,

impent → "[static] [ChName] [&] [cid]"
                      | "dynamic"
                      | "wrapper"

wo ChName "C-Headername" ist, nicht "Bibliotheksname".

FFI speC# 4.1.4 Specification of Header Files

Ein Header-C in einer Einfuhranmeldung angegeben wird immer von #include "ChName" enthalten. Es gibt keine explizite Unterstützung für #include <ChName> Stil Inklusion. Die ISO-C99 [3] Standard garantiert, dass jeder Suchpfad, der auch für ein #include <ChName> verwendet werden würde, für #include "ChName verwendet wird" und es ist sichergestellt, dass diese Wege nach allen Wegen gesucht werden, die einzigartig sind zu #include "chname". Darüber hinaus fordern wir, dass ChName auf .h endet Parsen der Spezifikation von externen Entitäten eindeutig zu machen.

Versuchen mit einem richtigen Header-Namen,

foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt 

oder ohne Header-Namen überhaupt.

foreign import stdcall "HW_Init" hwInit :: IO CInt 

Ihre Befehlszeile scheint nicht . als Bibliothekssuchpfad aufzunehmen. Es ist sehr wahrscheinlich, dass dies das Problem ist. GHCi enthält magisch . im Bibliothekssuchpfad.

 
ghc --make dlltest.hs -L. -lhwdriver 

Wenn das immer noch fehlschlägt, ist es vielleicht die statische Bibliothek, die Probleme verursacht. Unwahrscheinlich, aber ...

GHC unter Windows verwendet standardmäßig dynamisches Verknüpfen. Da Sie eine .lib haben, die eine statische Bibliothek ist, versuchen Sie, den Linker zu informieren, dass Sie statisch verknüpfen möchten.

 
ghc --make dlltest.hs -L. -optl-Bstatic -lhwdriver -optl-Bdynamic 

Wie bei automatisch generierten Bindungen, gibt es

Ich habe c2hs fand die einfachste t sein o verwenden, aber ich habe es nie auf etwas versucht stdcall s.

Es ist nicht , dass mühsam, alle foreign Sachen manuell zu schreiben, wenn es nur 25 Anrufe oder so gibt. Ich schaffte es manuell Bindungen libvlc ein paar Jahre zurück, für einige kleine Projekt zu schreiben ...

+0

GHCI dlltest.hs -lhw-Treiber erlaubt mir die Hauptfunktion in GHCI zu laufen, aber ich habe Probleme mit in gcc kompilieren: C: \ temp \ hs> ghc --make dlltest.hs -lhw-Treiber Verknüpfung dlltest.exe ... C: \ ghc \ ghc-6.10.3 \ gcc-lib \ ld.exe: kann -lhw-driver nicht finden collect2: ld gab 1 Exit-Status zurück Das ist sehr seltsam für mich da es in ghci richtig funktioniert. Ich werde noch etwas damit herumspielen. –

+1

GHCi verwendet ld nicht und implementiert stattdessen einen eigenen Linker. Häufiger gibt es Situationen, in denen eine Bibliothek kompiliert werden kann, aber nicht interaktiv, ohne hasche Problemumgehungen, aber dieser umgekehrte Fall scheint ebenfalls sehr wahrscheinlich. Können Sie mit -v laufen und die Zwischenbefehle posten, die ghc ausführt? – ephemient

+0

Die -L. Option ändert meine Ausgabe von "kann -lhw-Treiber nicht finden" zu "undefinierter Verweis auf 'HW_Init @ 0'", aber keiner der Vorschläge hat mir eine erfolgreiche Verknüpfung. Ein ähnliches Problem wurde bei http://www.nabble.com/OpenVG:Linker-errors-with-ghc---make,-but-not-with-ghci--td22321487 gefunden.HTML aber keine nützliche Antwort. Könnte am Ende morgen auf die GHC-Mailinglisten kommen ... es sieht so aus, als wäre es nur ein Problem mit den Linker-Optionen irgendwo. Ich bin sicher, dass ich nicht die erste Person bin, die ld benutzt, um mit einer DLL zu verlinken. –

3

Im Folgenden finden Sie ein funktionierendes Beispiel, das nennt [GetComputerName] (http://msdn.microsoft.com/en-us/library/ms724295(VS.85).aspx) von kernel32.dll:

{-# LANGUAGE ForeignFunctionInterface #-} 

module Main where 

import Control.Monad 
import Foreign.C 
import Foreign.Marshal.Alloc 
import Foreign.Marshal.Array 
import System.Win32.Types 

foreign import stdcall "GetComputerNameW" 
    win32_getComputerName :: LPTSTR -> LPDWORD -> IO Bool 

getComputerName :: IO String 
getComputerName = do 
    withTString maxBuf $ 
    \buf -> do 
     alloca $ \len -> do 
     pokeArray len [fromIntegral maxLength] 

     success <- win32_getComputerName buf len 
     when (not success) $ fail "GetComputerName failed" 

     [len'] <- peekArray 1 len 
     peekTStringLen (buf, (fromIntegral len')) 
    where 
    maxBuf = take maxLength $ repeat 'x' 
    maxLength = 15 -- cheating 

main :: IO() 
main = getComputerName >>= putStrLn 

Baut sie mit

ghc --make compname.hs -lkernel32 
+1

Das codiert den C: \ windows \ system32-Pfad in die ausführbare Ausgabedatei, was ich nicht für wünschenswert halte. – ephemient

+0

Dann schlagen Sie bitte eine wünschenswerte Alternative vor! –

+1

Stellen Sie sicher, dass die Bibliothekssuchpfade korrekt sind (obwohl dieser Pfad bereits durchsucht werden sollte), verwenden Sie dann "-lkernel32". – ephemient

Verwandte Themen