Frage

Dies ist wahrscheinlich eine sehr einfache Frage, die zu beantworten ist, aber aus irgendeinem Grund habe ich wirklich Probleme damit.

Ich habe eine DLL in C geschrieben, um auf Protokollebene auf Hardware zuzugreifen, und ich möchte ein Haskell -Programm schreiben, das einige dieser C -Funktionen aufruft. Hier ist ein Ausschnitt des relevanten C -Headers (mit Namen, die aufgrund möglicher Urheberrechtsprobleme nur geringfügig verschleiert werden):

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

Dies wurde als DLL in Visual Studio 2003 zusammengestellt, und ich habe die DLL sowohl von C als auch C#erfolgreich geladen, daher bin ich zuversichtlich, dass die DLL einwandfrei funktioniert. Die DLL heißt "hw-driver.dll".

Als nächstes ist hier der Haskell -Quellcode, um zu testen, ob ich die DLL ordnungsgemäß laden und die einfachste Funktion darin aufrufen kann:

{-# 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"

Die Linie, die mir Ärger macht, ist die fremde Importlinie. Soweit ich es verstehe, ist die Syntax fremd (Import/Export) (ccall/stdcall) Bibliotheksname C-Funktionsname Haskell-Funktionsname :: Haskell -Typ Deklaration. Mine sollte also fremd importieren stdcall (weil Sie STDCall beim Laden einer DLL in Win32 verwenden) "HW-Fahrer" (weil die Datei "hw-driver.dll" bezeichnet wird und im selben Verzeichnis wie DllTest.HS) liegt. "Hw_init" (der Name der Funktion in c) hwinit :: io (cint) (void Argumente, zurückgegeben, ein int).

Wenn ich jedoch versuche zu rennen ghci dlltest.hs, Ich bekomme die folgende Ausgabe:

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

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

Zeile 8, Spalte 43 ist das erste Anführungszeichen für HW_INIT. Okay, vielleicht muss ich sowohl den Bibliotheksnamen als auch den Funktionsnamen in eine Zeichenfolge einfügen, das habe ich an einigen Stellen gesehen. Wenn ich versuche, das auszuführen, dann bekomme ich:

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

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

8:23 ist das erste Anführungszeichen der neuen Zeichenfolge "HW-Fahrer hw_init".

Ich glaube nicht, dass mit meinem GHC-Setup (6.10.3) etwas falsch ist, da ich den folgenden Code ausführen kann, der in GHCI von Real World Haskell kopiert wurde:

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

Kurz gesagt, wie lange erkläre ich einen ausländischen Import für eine Win32 -DLL richtig? Ich konnte nicht finden irgendetwas auf Google.

Und um diese Frage mitzuteten, kann ich ein Programm wie C2HS oder HSC2HS verwenden, um die Header -Datei zu analysieren hw-driver.h Ich muss also nicht manuell die fremden Importanforderungen für alle 20 bis 25 in dieser DLL enthaltenen Funktionen schreiben? Ich konnte auch keine anständigen Beispiele dafür finden.


EDIT: EHEMIENT hat darauf hingewiesen, dass die richtige Syntax für die Fremdimportlinie lautet:

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

Damit kann ich anrufen ghci dlltest.hs -lhw-driver und rufen Sie die Hauptfunktion richtig mit einem erfolgreichen Rückgaberocode auf. Der Befehl jedoch ghc --make dlltest.hs -lhw-driver fällt mit einem Linker -Fehler fehl. Hier ist also die ausführliche Ausgabe dieses Befehls (beachten Sie, dass ich ganz HW-Fahrer habe. {Dll, h, lib} im Arbeitsverzeichnis):

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


Wie sich herausstellte, war die tatsächliche Verknüpfung nicht so schwierig, wie ich es ausschaffte. Ich habe benutzt foreign import stdcall was ich für korrekt mit einer DLL in Visual Studio 2003 korrekt war. Ich musste die herunterladen pexports Tool für Mingw, das die von einer DLL exportierten Funktionen auflistet. Der Linker hatte die ganze Zeit nach hwinit@0 gesucht, aber pexports sagte, die DLL exportierte nur Hwinit.

Ich änderte meine Linie in foreign import ccall Stattdessen konnte ich erfolgreich das Programm mit einem von verknüpfen ghc --make dlltest.hs hw-driver.lib oder ghc --make dlltest.hs -L. -lhw-driver Aufgrund der im Arbeitsverzeichnis verfügbaren .lib- und der .dll -Datei.

War es hilfreich?

Lösung

FFI Spec # 4.1.1 Importdeklarationen,

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

wo Chname ist "C Headername", nicht "Bibliotheksname".

FFI Spec # 4.1.4 Spezifikation von Header -Dateien

Der in einer Einfuhrerklärung angegebene Wechselstromkopf ist immer von enthalten von #include "Chname". Es gibt keine explizite Unterstützung für #include <Chname> Stileinbeziehung. Die ISO C99 [3] Standard garantiert, dass jeder Suchpfad, der für a verwendet wird #include <Chname> wird auch für verwendet #include "Chname" und es ist garantiert, dass diese Pfade nach allen Wegen durchsucht werden, die einzigartig sind #include "Chname". Darüber hinaus benötigen wir das Chname endet auf .h Analyse der Spezifikation externer Einheiten eindeutig.

Versuchen Sie es mit einem richtigen Headernamen,

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

oder ohne Kopfzeilenname.

foreign import stdcall "HW_Init" hwInit :: IO CInt

Ihre Befehlszeile scheint nicht einzuschließen . als Bibliothekssuchpfad. Es ist höchstwahrscheinlich, dass dies das Problem ist. GHCI beinhaltet auf magische Weise . im Bibliothekssuchpfad.

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

Wenn das noch fehlschlägt, ist es vielleicht die statische Bibliothek, die Probleme verursacht. Unwahrscheinlich, aber ...

GHC auf Windows verwendet standardmäßig eine dynamische Verknüpfung. Da hast du eine .lib, Das ist eine statische Bibliothek, die versuchen, den Linker zu informieren, den Sie möchten, dass Sie eine statische Verknüpfung haben möchten.

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

Bei automatisch generierten Bindungen gibt es

Ich habe festgestellt stdcalls.

Es ist nicht das belastend, alle zu schreiben foreign Sachen manuell, wenn es nur 25 Anrufe gibt oder so. Ich habe es geschafft, Bindungen manuell zu schreiben libvlc Vor ein paar Jahren für ein kleines Projekt ...

Andere Tipps

Im Folgenden finden Sie ein funktionierendes Beispiel, das aufgerufen wird [getComputername] (http://msdn.microsoft.com/en-us/library/ms724295(vs.85).aspx) aus 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

Bauen es mit

ghc --make compname.hs -lkernel32
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top