这可能是一个非常容易回答的问题,但由于某种原因,我真的很难接受它.
我有一个用C语言编写的DLL来访问协议级别的硬件,我想写一个调用其中一些C函数的Haskell程序.这是相关C标题的片段(由于可能的版权问题,名称只是略微混淆):
#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和C#加载DLL,因此我确信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-function-name :: Haskell类型声明.所以我的应该是外部导入stdcall(因为你在Win32中加载DLL时使用stdcall)"hw-driver"(因为该文件名为"hw-driver.dll",它与dlltest.hs位于同一目录中) "HW_Init"(C中函数的名称)hwInit :: IO(Cint)(void参数,返回int).
但是,当我尝试运行时ghci dlltest.hs
,我得到以下输出:
[1 of 1] Compiling Main ( dlltest.hs, interpreted ) dlltest.hs:8:43: parse error on input `"' Failed, modules loaded: none.
第8行,第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中的Real World 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 --}
这么长的问题,如何在Win32 DLL上正确声明外部导入?我无法在Google 上找到任何内容.
并且对于那个问题上的标记,我是否能够使用像c2hs或hsc2hs这样的程序来解析头文件,hw-driver.h
这样我就不必为该DLL中包含的所有20-25个函数手动编写外部导入调用?我也未能找到任何合适的例子.
编辑:ephemient指出外国导入行的正确语法是:
foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt
有了这个,我能够ghci dlltest.hs -lhw-driver
使用成功的返回码调用并正确调用main函数.但是,该命令ghc --make dlltest.hs -lhw-driver
因链接器错误而失败.所以,这是该命令的详细输出(请注意,我在工作目录中拥有所有hw-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是正确的.我必须下载pexports
MinGW工具,它列出了从DLL导出的函数.链接器一直在寻找HWInit @ 0,但是pexports
说DLL只导出了HWInit.
我改变了我的行foreign import
ccall
,我成功地能够使用ghc --make dlltest.hs hw-driver.lib
或者ghc --make dlltest.hs -L. -lhw-driver
由于在工作目录中同时提供.lib和.dll文件来链接程序.
FFI规范#4.1.1导入声明,
impent →"[
static
] [ chname ] [&] [ cid ]"
| "dynamic
"
| "wrapper
"
其中chname是"C header name",而不是"library name".
FFI规范#4.1.4头文件的规范
#include "
chname中始终包含导入声明中指定的AC标头"
.没有明确支持#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
至于自动生成的绑定,有
绿卡
C->哈斯克尔
hsc2hs
我发现c2hs是最容易使用的,但我从来没有尝试过任何需要stdcall
s的东西.
这并不是说繁重编写所有foreign
手工的东西,如果只有25个电话左右.libvlc
对于一些小项目,我设法在几年前手动编写绑定...