Pregunta

Esta es, probablemente, realmente una pregunta fácil de responder, pero por alguna razón, estoy realmente luchando con él.

Tengo una DLL escrita en C para hardware de acceso a un nivel de protocolo, y quiero escribir un Haskell programa que llama a algunos de los C funciones.He aquí un fragmento de la correspondiente cabecera C (con nombres un poco confuso debido a posibles problemas de copyright):

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

Este ha sido compilado como una DLL en Visual Studio 2003, y me ha cargado correctamente el archivo DLL de C y C#, así que estoy seguro de que el archivo DLL funciona bien.El archivo DLL se denomina "hw-driver.dll".

La próxima, aquí está la Haskell código fuente sólo para probar si puedo cargar correctamente el archivo DLL y llamar a la función más sencilla que hay en ella:

{-# 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 importación extranjera de la línea.Como yo lo entiendo, la sintaxis es exterior (importación/exportación) (ccall/stdcall) biblioteca-nombre C-la función de nombre haskell-función-nombre :: Haskell tipo de declaración.Lo mío debe ser de importación extranjera stdcall (debido a que el uso de stdcall cuando se carga un archivo DLL de Win32) "hw-driver" (debido a que el archivo se denomina "hw-driver.dll" y está ubicado en el mismo directorio que dlltest.hs) "HW_Init" (el nombre de la función en C) hwInit ::IO (Cint) (void argumentos, devuelve un int).

Sin embargo, cuando trato de ejecutar ghci dlltest.hs, Obtengo el siguiente resultado:

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

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

La línea 8, columna 43 es la primera marca de comillas en HW_Init.Bueno, a lo mejor me tengo que poner tanto el nombre de la biblioteca y el nombre de la función en una cadena, he visto que en algunos lugares.Si trato de ejecución que, luego de recibir:

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

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

8:23 es la primera marca de comillas de la nueva cadena "hw-controlador HW_Init".

No creo que haya nada malo con mi ghc setup (6.10.3), porque puedo ejecutar el siguiente código que se copia-pega del Mundo Real con el intérprete de Haskell:

{-- 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 --}

Siempre pregunta a corto, ¿cómo puedo declarar correctamente una importación extranjera en una DLL de Win32?No he sido capaz de encontrar nada en Google.

Y para el tipo de etiqueta a lo largo de esa pregunta, voy a ser capaz de utilizar un programa como c2hs o hsc2hs para analizar el archivo de encabezado hw-driver.h así que no tengo que escribir manualmente la importación extranjera de llamadas para todos los 20-25 funciones contenidas en esa DLL?No he sido capaz de encontrar ninguna decente ejemplos de eso.


EDITAR:ephemient ha señalado que la sintaxis correcta para la importación extranjera de la línea es:

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

Con esto, soy capaz de llamar a ghci dlltest.hs -lhw-driver y llamar correctamente la función principal con éxito un código de retorno.Sin embargo, el comando ghc --make dlltest.hs -lhw-driver falla con un error del vinculador.Así que, aquí está la información detallada de la salida de ese comando (tenga en cuenta que los tengo a 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 resulta, la real vinculación no era tan difícil como me fue haciendo de él hacia fuera para ser.Yo estaba usando foreign import stdcall que yo creía ser correctas con DLL integrado en Visual Studio 2003.Tuve que descargar el pexports herramienta para MinGW, que enumera las funciones exportadas desde un archivo DLL.El vinculador había estado buscando HWInit@0 todo el tiempo, pero pexports dijo que el archivo DLL se la exportación de sólo HWInit.

He cambiado de línea a foreign import ccall en su lugar, y yo estaba con éxito capaz de enlazar el programa con cualquiera de ghc --make dlltest.hs hw-driver.lib o ghc --make dlltest.hs -L. -lhw-driver debido a que tanto el .lib y el .dll archivo disponible en el directorio de trabajo.

¿Fue útil?

Solución

FFI spec # 4.1.1 Declaraciones de Importación,

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

donde chname es "C nombre de encabezado de", no "nombre de la biblioteca".

FFI spec # 4.1.4 Especificación de Archivos de Encabezado

Un encabezado de C indicados en una declaración de importación, siempre está incluido por #include "chname".No hay ningún apoyo explícito para #include <chname> el estilo de inclusión.La ISO C99 [3] estándar garantiza que cualquier ruta de búsqueda que se usa para una #include <chname> también se utiliza para #include "chname" y es garantizado que estas rutas son buscados después de todas las rutas que son únicos para #include "chname".Además, se requiere que chname termina en .h para hacer el análisis de la especificación de entidades externas sin ambigüedades.

Pruebe con un nombre de encabezado,

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

o sin el nombre de encabezado en todos.

foreign import stdcall "HW_Init" hwInit :: IO CInt

Su línea de comandos no parece incluir . como una ruta de búsqueda de biblioteca.Lo más probable es que este es el problema.El intérprete por arte de magia incluye . en la ruta de búsqueda de biblioteca.

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

Si el error persiste, tal vez es la biblioteca estática que está causando problemas.Raro, pero...

GHC en Windows utiliza la vinculación dinámica por defecto.Puesto que usted tiene un .lib, que es una biblioteca estática, tratar de informar al enlazador que desea vinculación estática.

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

Como para auto-generado enlaces, hay

He encontrado c2hs a ser los más fáciles de usar, pero nunca he probado nada que requieren stdcalls.

No que oneroso como para escribir todos los foreign cosas manualmente, si sólo hay 25 llamadas o así.Me las arreglé para escribir manualmente los enlaces a libvlc un par de años atrás, algún pequeño proyecto...

Otros consejos

A continuación se muestra un ejemplo de trabajo que llama a [GetComputerName] ( http://msdn.microsoft.com/en-us/library/ms724295 (VS.85) .aspx) de 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

Construir con

ghc --make compname.hs -lkernel32
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top