2009-06-22 44 views
11

這可能是一個非常容易回答的問題,但由於某種原因,我真的很掙扎。Haskell外匯進口stdcall上的DLL函數

我有一個用C編寫的DLL來訪問協議級別的硬件,我想寫一個Haskell程序來調用這些C函數。以下是有關C頭的片段(只有輕微模糊,由於可能copyrighting問題名稱):

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

這已被編譯爲在Visual Studio 2003中的DLL,我已成功加載來自C中的DLL和C#,所以我相信該DLL工作正常。該DLL被命名爲「hw-driver.dll」。

接下來,這裏是Haskell的源代碼只是爲了測試,如果我能正確地加載DLL和調用簡單的功能在裏面:

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

那是給我找麻煩該生產線是國外進口線。據我瞭解,語法是外國(進口/出口)(ccall/STDCALL)庫名C函數名哈斯克爾功能名稱 :: Haskell的類型聲明。所以我的應該是外​​部導入stdcall(因爲在Win32中加載DLL時使用stdcall)「hw-driver」(因爲該文件被命名爲「hw-driver.dll」,並且它位於與dlltest.hs相同的目錄中) 「HW_Init」(C中函數的名稱)hwInit :: IO(Cint)(void arguments,返回一個int)。

然而,當我嘗試運行ghci dlltest.hs,我得到以下輸出:8

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

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

行,列43是HW_Init在第一個引號。好的,所以也許我必須將庫名和函數名都放在一個字符串中,我已經在幾個地方看到了。如果我嘗試運行,那麼我得到:

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

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

8:23是新字符串「hw-driver HW_Init」的第一個引號。

我不相信有什麼毛病我GHC設置(6.10.3),因爲我可以運行下面的代碼,這是從真實世界哈斯克爾在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 --} 

這麼久問題簡短,我如何正確地聲明Win32 DLL上的外部導入?我一直無法在Google上找到任何東西

爲了解決這個問題,我可以使用像c2hs或hsc2hs這樣的程序來解析頭文件hw-driver.h,所以我不必手動編寫外部導入調用的所有20-25該DLL中包含的函數?我還沒有找到任何這樣的體面的例子。


編輯:ephemient指出,對於國外進口線的正確的語法是:

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

有了這個,我可以打電話給ghci dlltest.hs -lhw-driver和正確地調用的主要功能有成功返回碼。但是,命令ghc --make dlltest.hs -lhw-driver失敗,並顯示鏈接器錯誤。所以,這裏是該命令的詳細輸出(請注意,我已經將所有硬件驅動程序的{DLL,H,LIB}在工作目錄。):

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 


事實證明,實際的鏈接並不像我想要的那麼困難。我正在使用 foreign import stdcall我相信這是與在Visual Studio 2003中內置的DLL正確的。我不得不下載MinGW的 pexports工具,它列出了從DLL導出的函數。鏈接器一直在尋找HWInit @ 0,但是 pexports表示該DLL只輸出HWInit。

我改變了我行foreign importccall相反,我是能夠成功地使用任一ghc --make dlltest.hs hw-driver.libghc --make dlltest.hs -L. -lhw-driver程序鏈接,由於同時具有的.lib和在工作目錄中可用的.dll文件。

回答

5

FFI speC# 4.1.1 Import Declarations

impent → 「[static] [chname] [&] [CID]」
                      | 「dynamic
                      | 「wrapper

其中chname是 「C標題名稱」,而不是 「庫名稱」。

FFI speC# 4.1.4 Specification of Header Files

在導入聲明中指定C頭總是由#include "chname"包括在內。沒有明確支持#include <chname>樣式包含。在ISO C99 [3]標準保證將被用於#include <chname>任何搜索路徑也可用於#include "chname"並且可以保證這些路徑所特有的#include "所有路徑之後搜索chname"。此外,我們要求chname.h結尾,以明確解析外部實體的規範。

嘗試用一個適當的頭名,

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

或無頭名的。

foreign import stdcall "HW_Init" hwInit :: IO CInt 

您的命令行似乎並不包括.作爲庫搜索路徑。這很可能是問題所在。 GHCi在庫搜索路徑中神奇地包含.

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

如果仍然失敗,也許是靜態庫引起的問題。不太可能,但...

Windows上的GHC默認使用動態鏈接。由於您有一個.lib(它是一個靜態庫),請嘗試通知鏈接器您想要靜態鏈接。

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

至於自動生成的綁定,有

我發現c2hs是最容易ŧ o使用,但我從來沒有嘗試過任何需要stdcall s的東西。

這不是繁重的手動編寫所有的foreign東西,如果只有25個電話左右。我設法手動編寫綁定libvlc幾年前,對於一些小項目...

+0

ghci的dlltest.hs -lhw司機讓我來運行ghci中的主要功能,但我有與編譯gcc的問題: C:\ TEMP \ HS> GHC --make dlltest.hs -lhw-driver 鏈接dlltest.exe ... C:\ GHC \ GHC-6.10.3 \ GCC-LIB \ ld.exe:找不到-lhw驅動 collect2:LD返回1個退出狀態 這是很奇怪,我因爲它在ghci中正常工作。我會再玩一些。 – 2009-06-22 16:57:26

+1

GHCi不使用ld,而是實現自己的鏈接器。更常見的情況是,一個庫可以用於編譯,但不能交互式地使用,而沒有晦澀的解決方法,但這種相反的情況似乎也很可能。你能用-v運行併發布ghc運行的中間命令嗎? – ephemient 2009-06-22 17:13:02

3

下面是一個工作示例調用[GetComputerName(http://msdn.microsoft.com/en-us/library/ms724295(VS.85).aspx)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 

構建它
ghc --make compname.hs -lkernel32