Domanda

Questa è probabilmente una domanda molto semplice a cui rispondere, ma per qualche ragione sto davvero lottando con essa.

Ho una DLL scritta in C per accedere all'hardware a livello di protocollo e voglio scrivere un programma Haskell che chiama alcune di queste funzioni C. Ecco un frammento dell'intestazione C pertinente (con nomi leggermente offuscati a causa di possibili problemi di copyright):

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

Questo è stato compilato come DLL in Visual Studio 2003 e ho caricato correttamente la DLL da C e C #, quindi sono sicuro che la DLL funzioni correttamente. La DLL è denominata & Quot; hw-driver.dll & Quot ;.

Quindi, ecco il codice sorgente di Haskell solo per testare se posso caricare correttamente la DLL e chiamare la funzione più semplice in essa:

{-# 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 linea che mi dà problemi è la linea di importazione estera. A quanto ho capito, la sintassi è straniera (import / export) (ccall / stdcall) nome-libreria nome-funzione-C nome-funzione-haskell :: dichiarazione del tipo Haskell . Quindi il mio dovrebbe essere import import stdcall (perché usi stdcall quando carichi una DLL in Win32) & Quot; hw-driver & Quot; (perché il file è denominato " hw-driver.dll " e si trova nella stessa directory di dlltest.hs) " HW_Init " (il nome della funzione in C) hwInit :: IO (Cint) (argomenti vuoti, restituendo un int).

Tuttavia, quando provo a eseguire ghci dlltest.hs, ottengo il seguente output:

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

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

Riga 8, colonna 43 è la prima virgoletta su HW_Init. Ok, quindi forse devo mettere sia il nome della libreria che il nome della funzione in una stringa, l'ho visto in alcuni punti. Se provo a eseguirlo, ottengo:

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

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

8:23 è il primo segno di virgolette della nuova stringa " hw-driver HW_Init " ;.

Non credo che ci sia qualcosa di sbagliato nella mia configurazione di ghc (6.10.3), perché posso eseguire il seguente codice che è stato incollato da Real World Haskell in 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 --}

In breve, breve domanda, come posso dichiarare correttamente un'importazione straniera su una DLL Win32? Non sono stato in grado di trovare nulla su Google.

E per tipo di tag lungo quella domanda, sarò in grado di usare un programma come c2hs o hsc2hs per analizzare il file di intestazione hw-driver.h in modo da non dover scrivere manualmente le chiamate di importazione esterne per tutti i 20- 25 funzioni contenute in quella DLL? Nemmeno io sono stato in grado di trovare esempi decenti.


EDIT: ephemient ha sottolineato che la sintassi corretta per la linea di importazione estera è:

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

Con questo, sono in grado di chiamare ghci dlltest.hs -lhw-driver e chiamare correttamente la funzione principale con un codice di ritorno riuscito. Tuttavia, il comando ghc --make dlltest.hs -lhw-driver ha esito negativo con un errore del linker. Quindi, ecco l'output dettagliato di quel comando (nota che ho tutto il driver hw. {Dll, h, lib} nella directory di lavoro):

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


A quanto pare, il collegamento reale non è stato così difficile come lo stavo facendo. Stavo usando foreign import stdcall che ritenevo corretto con una DLL costruita in Visual Studio 2003. Ho dovuto scaricare lo strumento pexports per MinGW, che elenca le funzioni esportate da un DLL. Il linker era sempre alla ricerca di HWInit @ 0, ma ccall ha affermato che la DLL esportava solo HWInit.

Ho invece modificato la mia riga in ghc --make dlltest.hs hw-driver.lib ghc --make dlltest.hs -L. -lhw-driver e sono stato in grado di collegare correttamente il programma utilizzando <=> o <=> grazie alla presenza sia di .lib che di il file .dll disponibile nella directory di lavoro.

È stato utile?

Soluzione

Specifiche FFI # 4.1.1 Dichiarazioni di importazione ,

  

impent & # 8594; & Quot; [static] [ chname ] [& amp;] [ cid ] "
  nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; | & Quot; dynamic "
  nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; nbsp &; | & Quot; wrapper "

dove chname è " Nome intestazione C " ;, non " nome libreria " ;.

Specifiche FFI # 4.1.4 Specifica dei file di intestazione

  

Un'intestazione C specificata in una dichiarazione di importazione è sempre inclusa da #include " chname ". Non esiste un supporto esplicito per l'inclusione dello stile #include < chname >. ISO C99 [ 3 ] standard garantisce che qualsiasi percorso di ricerca che verrebbe utilizzato per un .h chname . viene utilizzato anche per .lib chname stdcall ed è garantito che questi percorsi vengono cercati dopo tutti i percorsi che sono unici per foreign chname libvlc. Inoltre, richiediamo che chname termina con <=> per rendere inequivocabile l'analisi delle specifiche di entità esterne.

Prova con un nome di intestazione corretto,

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

o senza alcun nome di intestazione.

foreign import stdcall "HW_Init" hwInit :: IO CInt

La tua riga di comando non sembra includere <=> come percorso di ricerca nella libreria. È molto probabile che questo sia il problema. GHCi include magicamente <=> nel percorso di ricerca della libreria.

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

Se il problema persiste, forse è la libreria statica a causare problemi. Improbabile, ma ...

GHC su Windows utilizza il collegamento dinamico per impostazione predefinita. Dato che hai una <=>, che è una libreria statica, prova a informare il linker che desideri un collegamento statico.

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

Per quanto riguarda i binding generati automaticamente, c'è

Ho trovato c2hs il più semplice da usare, ma non l'ho mai provato su nulla che richiedesse <=> s.

Non è così oneroso scrivere tutte le <=> cose manualmente, se ci sono solo 25 chiamate. Sono riuscito a scrivere manualmente i binding su <=> qualche anno fa, per qualche piccolo progetto ...

Altri suggerimenti

Di seguito è riportato un esempio funzionante che chiama [GetComputerName] ( http://msdn.microsoft.com/en-us/library/ms724295 (VS.85) .aspx) da 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

Costruiscilo con

ghc --make compname.hs -lkernel32
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top