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 import
ccall
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.
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. –
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
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. –