从Haskell调用DLL

3

我有一个名为Foo.dll的Windows DLL。它导出(除其他外)以下内容:

extern "C" __declspec(dllexport) unsigned int Version();

我应该如何编写一个 Haskell 程序,调用这个函数并输出结果?

我发现可以这样写:

foreign import ccall "Version" cpp_Version :: CUInt

这段代码编译没有问题,但链接时却出现了严重错误。这并不奇怪;此时 GHC 完全不知道该从哪里寻找这个函数。但我无法弄清楚需要按哪个神奇的按钮才能解决这个问题。有人能告诉我如何成功构建吗?

(我也不确定调用约定应该是ccall还是stdcall;它们有什么区别?)


粗略地说(可能不准确),stdcall 是 Win32API 的调用约定,ccall 则是几乎所有其他情况——当然包括 GCC(MinGW/MSYS)和 Unix。OpenGL 使用的是 ccall [http://www.haskell.org/pipermail/libraries/2011-June/016505.html],我不确定它现在是否仍在使用。同样,我也不确定使用 Visual Studio 编译的 .DLL 文件会使用哪种调用约定。 - stephen tetley
1个回答

1
使用以下选项进行编译似乎可以工作:
ghc -O2 -L. -lFoo --make Wrapper

看起来添加-lFoo告诉GHC查找Foo.dll文件,添加-L.告诉它将当前目录包含在DLL搜索路径中。

我不确定这是否是在运行时加载DLL,还是以某种方式将DLL的代码静态链接到二进制文件中。 (!!)

ccall更改为stdcall会导致发出一堆警告(但编译后的代码仍然可以正常工作)。因此,似乎ccall是正确的选择。

如果有人能够再次确认我所写的内容不是完全胡说八道,我仍然会非常感激...


在你的导入语句中,你可以声明函数来自哪个头文件,例如:"foreign import ccall "Version foo.h" cpp_Version :: CUInt"。我认为这不是必需的,但它是 FFI 标准的一部分(参见8.5.1)。你说得对,stdcall 是用于 Windows(但不适用于 64 位 Windows),而 ccall 则用于其他所有地方。使用动态链接。在编译时,会创建一个名为 foo 的存根库,该库调用你的 .dll 文件。通过删除 foo.dll 来测试它,但我认为它就是这样工作的。 - Michael Steele
GHC文档明确指出,GHC完全忽略任何包含文件的规范。(我也没有包含文件...) - MathematicalOrchid
好的。使用“-l”指定库是很常见的。对我来说,使用.cabal文件会使事情变得更容易一些。 - Michael Steele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接