Ceci est probablement une question vraiment facile à répondre, mais pour une raison quelconque, je suis vraiment aux prises avec cela.Haskell import étranger stdcall sur la fonction DLL
J'ai une DLL écrite en C pour accéder au matériel au niveau du protocole, et je veux écrire un programme Haskell qui appelle certaines de ces fonctions C. Voici un extrait de l'en-tête correspondant C (avec des noms légèrement brouillées en raison de problèmes possibles copyrighting):
#ifdef HWDRIVER_EXPORTS
#define HWDRIVER_API __declspec(dllexport)
#else
#define HWDRIVER_API __declspec(dllimport)
#endif
HWDRIVER_API int HW_Init(void);
Cela a été compilé en tant que DLL dans Visual Studio 2003, et je l'ai chargé avec succès la DLL à la fois C et C#, donc je suis convaincu que la DLL fonctionne bien. La DLL est nommée "hw-driver.dll".
Ensuite, voici le code source Haskell juste pour tester si je peux charger correctement la DLL et appeler la fonction simple en elle:
{-# 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"
La ligne qui me donne du mal est la ligne d'importation étrangère. Si je comprends bien, la syntaxe est étrangère (import/export) (ccall/stdcall) bibliothèque nomC-nom_de_la_fonctionhaskell-nom_de_la_fonction :: déclaration de type Haskell. Donc le mien devrait être stdcall import étranger (parce que vous utilisez stdcall lors du chargement d'une DLL dans Win32) "hw-driver" (parce que le fichier est nommé "hw-driver.dll" et il se trouve dans le même répertoire que dlltest.hs) "HW_Init" (le nom de la fonction en C) hwInit :: IO (Cint) (arguments vides, retournant un int).
Cependant, lorsque je tente en cours d'exécution ghci dlltest.hs
, je reçois la sortie suivante:
[1 of 1] Compiling Main (dlltest.hs, interpreted)
dlltest.hs:8:43: parse error on input `"'
Failed, modules loaded: none.
ligne 8, colonne 43 est la première marque de cotation sur HW_Init. Bon, alors peut-être que je dois mettre à la fois le nom de la bibliothèque et le nom de la fonction dans une chaîne, j'ai vu ça dans quelques endroits. Si j'essaie en cours d'exécution, alors je reçois:
[1 of 1] Compiling Main (dlltest.hs, interpreted)
dlltest.hs:8:23: Malformed entity string
Failed, modules loaded: none.
8:23 est la première marque de cotation de la nouvelle chaîne « HW_Init hw-pilote ».
Je ne crois pas qu'il y ait quelque chose de mal avec ma configuration GHC (6.10.3), parce que je peux exécuter le code suivant qui était la copie collée depuis Real World Haskell en ghci:
{-- 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 --}
Tant question courte, comment puis-je déclarer correctement une importation étrangère sur une DLL Win32? Je n'ai pas pu trouver quoi que ce soit sur Google. Pour suivre cette question, serai-je capable d'utiliser un programme comme c2hs ou hsc2hs pour analyser le fichier d'en-tête hw-driver.h
afin que je n'aie pas à écrire manuellement les appels d'importation étrangers pour tous les 20-25? fonctions contenues dans cette DLL? Je n'ai pas non plus trouvé d'exemples décents de cela.
EDIT: ephemient a souligné que la syntaxe correcte pour la ligne d'importation étrangère est:
foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt
Avec cela, je suis en mesure d'appeler ghci dlltest.hs -lhw-driver
et correctement appeler la fonction principale avec un retour réussi code. Cependant, la commande ghc --make dlltest.hs -lhw-driver
échoue avec une erreur de l'éditeur de liens.Alors, voici la sortie prolixe de cette commande (notez que j'ai tous hw-conducteur {dll, h, lib} dans le répertoire de travail.):
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
Comme il se trouve, la liaison réelle n'était pas aussi difficile que je le pensais être. J'utilisais
foreign import
stdcall
que je pensais être correct avec une DLL construite dans Visual Studio 2003. J'ai dû télécharger l'outil
pexports
pour MinGW, qui répertorie les fonctions exportées à partir d'une DLL. L'éditeur de liens recherchait HWInit @ 0 depuis le début, mais
pexports
indiquait que la DLL exportait uniquement HWInit.
je changé ma ligne foreign import
ccall
à la place, et j'ai été avec succès en mesure de lier le programme à l'aide soit de ghc --make dlltest.hs hw-driver.lib
ou ghc --make dlltest.hs -L. -lhw-driver
en raison d'avoir à la fois le .lib et le fichier .dll dans le répertoire de travail.
ghci dlltest.hs -lhw-pilote m'a permis de courir la fonction principale dans ghci, mais je vais avoir des problèmes avec la compilation de gcc: C: \ temp \ hs> GHC --make dlltest.hs -lhw-driver Liaison dlltest.exe ... C: \ ghc \ ghc-6.10.3 \ gcc-lib \ ld.exe: impossible de trouver -lhw-driver collect2: ld a renvoyé 1 statut de sortie Ceci est très étrange pour moi puisque cela fonctionne correctement dans ghci. Je vais jouer avec ça encore plus. –
GHCi n'utilise pas ld et implémente son propre éditeur de liens à la place. Plus communément, il existe des situations où une bibliothèque peut être utilisée dans la compilation mais pas interactivement, sans solutions de contournement hackish, mais ce cas inverse semble très probable aussi. Pouvez-vous exécuter avec -v et afficher les commandes intermédiaires exécutées par ghc? – ephemient
Le -L. option change ma sortie de "ne peut pas trouver -lhw-driver" en "référence non définie à 'HW_Init @ 0'", mais aucune de ces suggestions ne m'a permis d'obtenir un lien réussi. Trouvé un problème similaire à http://www.nabble.com/OpenVG:-Linker-errors-with-ghc---make,-but-not-with-ghci--td22321487.html mais pas de réponse utile. Ça pourrait finir par arriver sur les listes de diffusion GHC demain ... on dirait que c'est juste un problème avec les options de l'éditeur de liens quelque part. Je suis sûr que je ne suis pas la première personne à utiliser ld pour créer un lien avec une DLL. –