Question

C’est probablement une question très facile à répondre, mais pour une raison quelconque, j’ai vraiment du mal à la résoudre.

J'ai une DLL écrite en C pour accéder au matériel à un niveau de protocole et je veux écrire un programme Haskell qui appelle certaines de ces fonctions C. Voici un extrait de l'en-tête C pertinent (avec des noms légèrement obscurcis en raison de problèmes de copyright):

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

Ceci a été compilé en tant que DLL dans Visual Studio 2003 et j’ai chargé avec succès la DLL à partir de C et de C #. Je suis donc convaincu que la DLL fonctionne correctement. La DLL est nommée & Quot; hw-driver.dll & Quot;.

.

Ensuite, voici le code source Haskell juste pour vérifier si je peux charger correctement la DLL et appeler la fonction la plus simple:

{-# 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 pose problème est la ligne d'importation étrangère. Si je comprends bien, la syntaxe est étrangère (import / export) (ccall / stdcall) nom-bibliothèque nom-fonction-C nom-fonction-haskell :: Déclaration de type Haskell . Donc, le mien doit être importé de l'étranger stdcall (car vous utilisez stdcall lors du chargement d'une DLL dans Win32) & "Hw-driver &"; (car le fichier s'appelle & "; hw-driver.dll &"; 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 void, retournant un int).

Cependant, lorsque j'essaie d'exécuter ghci dlltest.hs, le résultat suivant est généré:

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

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

La ligne 8, colonne 43 est le premier guillemet sur HW_Init. Ok, alors peut-être que je dois mettre le nom de la bibliothèque et le nom de la fonction dans une chaîne, je l'ai vu à quelques endroits. Si j'essaie de l'exécuter, 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 le premier guillemet de la nouvelle chaîne " hw-driver HW_Init "..

Je ne crois pas qu'il y ait quelque chose qui cloche dans ma configuration ghc (6.10.3), car je peux exécuter le code suivant copié-collé depuis Real World Haskell dans 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 --}

Très longue question, comment puis-je déclarer correctement une importation étrangère sur une DLL Win32? Je n'ai pas trouvé quoi que ce soit sur Google.

Et puis, pour répondre à cette question, pourrai-je utiliser un programme comme c2hs ou hsc2hs pour analyser le fichier d'en-tête hw-driver.h, de sorte que je n'ai pas à écrire manuellement les appels d'importation étrangers pour tous les 20- 25 fonctions contenues dans cette DLL? Je n'ai pas été capable de trouver des exemples décents de cela non plus.

EDIT: ephemient a indiqué que la syntaxe correcte pour la ligne d'importation étrangère est la suivante:

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

Grâce à cela, je peux appeler ghci dlltest.hs -lhw-driver et appeler correctement la fonction principale avec un code de retour réussi. Cependant, la commande ghc --make dlltest.hs -lhw-driver échoue avec une erreur de l'éditeur de liens. Voici donc le résultat détaillé de cette commande (notez que j'ai tout le pilote hw-driver. {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


Il s'est avéré que la liaison réelle n'était pas aussi difficile que je le pensais. J'utilisais foreign import stdcall , ce qui, à mon avis, était correct avec une DLL créée dans Visual Studio 2003. Je devais télécharger l'outil pexports pour MinGW, qui répertorie les fonctions exportées à partir d'un fichier. DLL. L'éditeur de liens avait toujours recherché HWInit @ 0, mais ccall a déclaré que la DLL exportait uniquement HWInit.

J'ai remplacé ma ligne par ghc --make dlltest.hs hw-driver.lib ghc --make dlltest.hs -L. -lhw-driver et j'ai réussi à lier le programme à l'aide de <=> ou <=>, car les deux fichiers .lib et le fichier .dll disponible dans le répertoire de travail.

Était-ce utile?

La solution

Spécification FFI 4.1.1 Déclarations d'importation ,

  

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

chname est & "; nom d'en-tête C &", pas & "nom de bibliothèque &";

.

Spécification FFI 4.1.4 Spécification des fichiers d’en-tête

  

Un en-tête C spécifié dans une déclaration d'importation est toujours inclus par #include " chname ". Il n'y a pas de support explicite pour l'inclusion de style #include < chname >. La norme ISO C99 [ 3 ] standard garantit que tout chemin de recherche qui serait utilisé pour un .h chname . est également utilisé pour .lib chname stdcall et il est garanti que Ces chemins sont recherchés après tous les chemins uniques à foreign chname libvlc. De plus, nous avons besoin que chname se termine le <=> pour rendre l'analyse de la spécification des entités externes non ambiguë.

Essayez avec un nom d'en-tête approprié,

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

ou sans nom d'en-tête.

foreign import stdcall "HW_Init" hwInit :: IO CInt

Votre ligne de commande ne semble pas inclure <=> comme chemin de recherche dans la bibliothèque. Il est fort probable que ce soit le problème. GHCi inclut par magie <=> dans le chemin de recherche de la bibliothèque.

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

Si cela échoue toujours, c'est peut-être la bibliothèque statique qui pose problème. Peu probable, mais ...

GHC sous Windows utilise la liaison dynamique par défaut. Puisque vous avez une <=> bibliothèque statique, essayez d’informer l’éditeur de liens que vous souhaitez créer un lien statique.

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

En ce qui concerne les liaisons générées automatiquement, il y a

J'ai trouvé que c2hs était le plus facile à utiliser, mais je ne l'ai jamais essayé avec quoi que ce soit nécessitant <=> s.

Ce n'est pas que d'écrire tous les <=> fichiers manuellement, s'il n'y a que 25 appels ou plus. J'ai réussi à écrire manuellement des liaisons vers <=> quelques années en arrière, pour quelques petits projets ...

Autres conseils

Ci-dessous, un exemple de travail qui appelle [GetComputerName] ( http://msdn.microsoft.com/en-us/library/ms724295 (VS.85) .aspx) à partir 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

Construisez-le avec

ghc --make compname.hs -lkernel32
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top