Haskell Foreign Function Interface with GHCI in Windows



首先,我指定我使用Windows 10 64位和Haskell平台8.0.1。

我尝试使用以下代码在Windows中使用Haskell的FFI。

import Control.Monad
import Data.Char
import Foreign.C
getCh :: IO Char
getCh = liftM (chr . fromEnum) c_getch
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt
main :: IO ()
main = getCh >>= x -> print x

在此之后,我可以用ghc很好地编译它

> ghc Examples.hs
[1 of 1] Compiling Main             ( Examples.hs, Examples.o )
Linking Examples.exe ...

它完全运行。

> Examples.exe
'1'

(当我在运行后键入 1 时)

但是,GHCI 会出现此问题。当我将其加载到 ghci 时,我收到了这些消息。

> ghci Examples.hs
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( Examples.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
ByteCodeLink: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  getch
This may be due to you not asking GHCi to load extra object files,
archives or DLLs needed by your current session.  Restart GHCi, specifying
the missing library using the -L/path/to/object/dir and -lmissinglibname
flags, or simply by naming the relevant files on the GHCi command line.
Alternatively, this link failure might indicate a bug in GHCi.
If you suspect the latter, please send a bug report to:
  glasgow-haskell-bugs@haskell.org
*Main>

我尝试加载"缺少的库",例如需要使用conio.h的"-lmsvcrt",但结果悲观地相同。

> ghci -lmsvcrt Examples.hs
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( Examples.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
ByteCodeLink: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  getch
This may be due to you not asking GHCi to load extra object files,
archives or DLLs needed by your current session.  Restart GHCi, specifying
the missing library using the -L/path/to/object/dir and -lmissinglibname
flags, or simply by naming the relevant files on the GHCi command line.
Alternatively, this link failure might indicate a bug in GHCi.
If you suspect the latter, please send a bug report to:
  glasgow-haskell-bugs@haskell.org
*Main>

GHCI 可能会加载库,因为当我尝试加载错误的库时,ghci 会打印有关该库的错误。

我尝试了其他一些事情,例如使用ghci Examples.hs -fobject-codeghci -lmsvcrt Examples.hs -fobject-code,甚至

ghci Examples.hs "-luser32" "-lgdi32" "-lwinmm" "-ladvapi32" "-lshell32"
"-lshfolder" "-lwsock32" "-luser32" "-lshell32" "-lmsvcrt" "-lmingw32" 
"-lmingwex" "-luser32" "-lmingw32" "-lmingwex" "-lm" "-lwsock32" "-lgdi32" "-lwinmm"

这是在ghc Examples.hs -v5中发现的.

可悲的是,没有什么对我的main有用,我找不到任何其他方法。

附言有没有人知道如何在 Windows 中使用 hSetBuffering(它是在 8 年前在 ghc 票证 #2189 中发布的。不是固定的吗?

这是因为Windows上没有getchgetch是POSIX,POSIX已在Windows上弃用。它仍然存在,但函数已移动到不同的命名空间(以释放根命名空间给用户程序)。如您所见,MSDN 说getch已弃用 https://msdn.microsoft.com/en-us/library/ms235446.aspx 并改用_getch

import Control.Monad
import Data.Char
import Foreign.C
getCh :: IO Char
getCh = liftM (chr . fromEnum) c_getch
foreign import ccall unsafe "conio.h _getch" c_getch :: IO CInt
main :: IO ()
main = getCh >>= x -> print x

将工作编译和解释。

至于为什么它在使用getch时编译而不解释:

MingW-w64项目从未像Microsoft那样删除已弃用的函数

因此

$ objdump -t /home/Tamar/ghc2/inplace/mingw/x86_64-w64-mingw32/lib/libmsvcr120.a | grep getch
[  7](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 getch
[  8](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 __imp_getch
[  7](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 _getch
[  8](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 __imp__getch

getch被重定向到_getch,因此它们具有两个版本。这是MSVC++和GCC之间不兼容的来源。

但是Microsoft已经删除了它们

>dumpbin /exports C:WindowsSystem32msvcr120.dll | findstr getch
        699  2BA 0006B8B4 _getch = _getch

那么,当您链接到msvcrt时会发生什么:

  1. 在编译模式下,GCC 和 GHC 都会首先选择静态库,这恰好是一个导入库libmsvcrt.dll.a。这是由于链接器 (ld) 的链接顺序。

  2. 在解释模式下,GHCi 将始终首选库的动态版本而不是静态版本。原因是在重新链接期间(当您引入新作用域或重新加载时必须发生),动态库要快得多,因为我们不必在内部进行重定位和符号解析。还有一些我们仍然不支持的东西,比如弱符号或通用符号,所以出于这些原因,我们更喜欢动态符号。

  3. GHCi 8.0.1 不支持导入库。因此,虽然您可以强制 GHCi 使用静态库(只需为-l提供全名,例如 -llibmsvcr.a ) 它不起作用,因为运行时加载器不知道如何处理它。但是,这在当前的 GIT 主站中受支持,并且可能会8.0.2

最新更新