Esta es probablemente una pregunta realmente fácil de responder, pero por alguna razón realmente estoy luchando con ella.Haskell foreign stdcall de importación en la función DLL
Tengo un archivo DLL escrito en C para acceder al hardware a nivel de protocolo, y quiero escribir un programa Haskell que llame a algunas de esas funciones en C. He aquí un fragmento de la cabecera de C correspondiente (con nombres ligeramente ofuscado debido a posibles problemas de derechos de copia):
#ifdef HWDRIVER_EXPORTS
#define HWDRIVER_API __declspec(dllexport)
#else
#define HWDRIVER_API __declspec(dllimport)
#endif
HWDRIVER_API int HW_Init(void);
Esto ha sido compilado como un archivo DLL en Visual Studio 2003, y he cargado correctamente el archivo DLL de ambos C y C#, así que estoy seguro de que la DLL funciona bien. La DLL se llama "hw-driver.dll".
A continuación, aquí está el código fuente Haskell sólo para probar si puedo cargar correctamente el archivo DLL y llamar a la función más simple en él:
{-# 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 línea que me está dando problemas es la línea de importación extranjera. Como yo lo entiendo, la sintaxis es extranjera (importación/exportación) (ccall/stdcall) nombre-bibliotecaC-nombre-funciónHaskell-nombre-función :: Haskell declaración de tipo. Así que la mía debe ser stdcall importación extranjera (porque utiliza stdcall al cargar un archivo DLL en Win32) "hw-driver" (porque el archivo se llama "hw-driver.dll" y se encuentra en el mismo directorio que dlltest.hs) "HW_Init" (el nombre de la función en C) hwInit :: IO (Cint) (argumentos vacíos, devolviendo un int).
Sin embargo, cuando trato de ejecutar ghci dlltest.hs
, me sale el siguiente resultado:
[1 of 1] Compiling Main (dlltest.hs, interpreted)
dlltest.hs:8:43: parse error on input `"'
Failed, modules loaded: none.
línea 8, columna 43 es el primer signo de comillas en HW_Init. Bueno, quizás tenga que poner el nombre de la biblioteca y el nombre de la función en una sola cadena, lo he visto en algunos lugares. Si trato de ejecución que, a continuación, me sale:
[1 of 1] Compiling Main (dlltest.hs, interpreted)
dlltest.hs:8:23: Malformed entity string
Failed, modules loaded: none.
8:23 es el primer signo de comillas de la nueva cadena "HW_Init HW-conductor".
No creo que haya nada malo con mi configuración GHC (6.10.3), porque puedo ejecutar el siguiente código que estaba pegada una copia impresa de 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 --}
En tanto pregunta breve, ¿cómo declaro correctamente una importación extranjera en una DLL de Win32? No he podido encontrar nada en Google.
Y al tipo de etiqueta a lo largo de esa pregunta, ¿podré usar un programa como c2hs o hsc2hs para analizar el archivo de encabezado hw-driver.h
para no tener que escribir manualmente las llamadas de importación foránea para todos los 20-25 funciones contenidas en esa DLL? No he podido encontrar ningún ejemplo decente de eso tampoco.
EDIT: ephemient ha señalado que la sintaxis correcta para la línea de importación extranjera es:
foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt
Con esto, yo soy capaz de llamar ghci dlltest.hs -lhw-driver
y debidamente llamar a la función principal, con un retorno exitoso código. Sin embargo, el comando ghc --make dlltest.hs -lhw-driver
falla con un error de enlazador.Por lo tanto, aquí está el resultado detallado de ese comando (tenga en cuenta que tengo todos HW-controlador {DLL, h, lib} en el directorio de trabajo.):
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
Como resultado, la vinculación real no fue tan difícil como lo imaginaba. Estaba usando
foreign import
stdcall
que creí que era correcto con una DLL integrada en Visual Studio 2003. Tuve que descargar la herramienta
pexports
para MinGW, que enumera las funciones exportadas desde una DLL. El enlazador había estado buscando HWInit @ 0 todo el tiempo, pero
pexports
dijo que el DLL estaba exportando solo HWInit.
cambié de línea a foreign import
ccall
lugar, y yo era capaz de enlazar con éxito el programa utilizando cualquiera de ghc --make dlltest.hs hw-driver.lib
ghc --make dlltest.hs -L. -lhw-driver
o debido a tener tanto el .lib y el archivo .dll disponibles en el directorio de trabajo.
ghci dlltest.hs -lhw-conductor me permitió ejecutar la función principal de ghci, pero estoy teniendo problemas con la compilación de gcc: C: \ temp \ hs> GHC --make dlltest.hs -lhw-driver Vinculación dlltest.exe ... C: \ ghc \ ghc-6.10.3 \ gcc-lib \ ld.exe: no se puede encontrar -lhw-driver collect2: ld devuelto 1 estado de salida Esto es muy extraño para mí ya que funciona correctamente en ghci. Voy a jugar con eso un poco más. –
GHCi no usa ld e implementa su propio enlazador en su lugar. Más comúnmente, hay situaciones en las que se puede usar una biblioteca para compilar, pero no interactivamente, sin soluciones provisionales, pero este caso inverso parece bastante probable también. ¿Puedes correr con -v y publicar los comandos intermedios que ejecuta ghc? – ephemient
La -L. option cambia mi salida de "no puedo encontrar -lhw-driver" a "referencia no definida a 'HW_Init @ 0'", pero ninguna sugerencia me consiguió un enlace exitoso. Se encontró un problema similar en http://www.nabble.com/OpenVG:-Linker-errors-with-ghc---make,-but-not-with-ghci--td22321487.html pero no hay respuesta útil. Podría terminar en las listas de correo de GHC mañana ... parece que es solo un problema con las opciones del vinculador en alguna parte. Estoy seguro de que no soy la primera persona en usar ld para vincular con una DLL. –