11

これはおそらく答えるのが本当に簡単な質問ですが、何らかの理由で私は本当にそれに苦労しています。

プロトコルレベルでハードウェアにアクセスするために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);

これはVisualStudio2003でDLLとしてコンパイルされており、CとC#の両方からDLLを正常にロードしたので、DLLは正常に機能すると確信しています。DLLの名前は「hw-driver.dll」です。

次に、DLLを適切にロードして、その中で最も単純な関数を呼び出すことができるかどうかをテストするためのHaskellソースコードを次に示します。

{-# 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-function-name haskell-function-name :: Haskell型宣言です。したがって、私のものは外部インポートstdcallである必要があります(Win32でDLLをロードするときにstdcallを使用するため) "hw-driver"(ファイルの名前は "hw-driver.dll"であり、dlltest.hsと同じディレクトリにあるため) "HW_Init"(Cでの関数の名前)hwInit :: IO(Cint)(引数を無効にし、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つの文字列に入れなければならないかもしれません、私はそれをいくつかの場所で見ました。それを実行しようとすると、次のようになります。

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

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

8:23は、新しい文字列「hw-driverHW_Init」の最初の引用符です。

ghciでRealWorldHaskellからコピーして貼り付けた次のコードを実行できるため、ghcセットアップ(6.10.3)に問題はないと思います。

{-- 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 stdcallVisual Studio 2003に組み込まれているDLLで正しいと思われるものを使用していpexportsました。DLLからエクスポートされた関数を一覧表示する、MinGW用のツールをダウンロードする必要がありました。リンカはずっとHWInit@0を探していましたがpexports、DLLはHWInitだけをエクスポートしていると言いました。

行を代わりに変更しましたが、.libファイルと.dllファイルの両方が作業ディレクトリで使用可能であるため、またはそのforeign import ccallいずれかを使用してプログラムを正常にリンクできました。ghc --make dlltest.hs hw-driver.libghc --make dlltest.hs -L. -lhw-driver

4

2 に答える 2

5

FFI仕様#4.1.1インポート宣言

impent →"[ static][ chname ] [&] [ cid ]"
            | " dynamic"
            | 「wrapper

ここで、chnameは「ライブラリ名」ではなく「Cヘッダー名」です。

FFI仕様#4.1.4ヘッダーファイルの仕様

インポート宣言で指定されたACヘッダーは、常に#include "chname"に含まれます。#include <chname>スタイルの包含は明示的にサポートされていません。ISO C99 [ 3 ]標準は、chnameに使用されるすべての検索パスが#include <chname>にも使用されることを保証し、これらのパスがchnameに固有のすべてのパスの後に検索されることを保証#include "ます。さらに、外部エンティティの仕様の解析を明確にするために、 chnameが終了する必要があります。"#include "".h

適切なヘッダー名で試してください。

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

または、ヘッダー名がまったくありません。

foreign import stdcall "HW_Init" hwInit :: IO CInt

コマンドライン.にライブラリ検索パスが含まれていないようです。これが問題である可能性が最も高いです。GHCiは魔法のように.ライブラリ検索パスに含まれています。

ghc --makedlltest.hs-L。-lhwdriver

それでも失敗する場合は、問題を引き起こしているのは静的ライブラリである可能性があります。ありそうもないが...

Windows上のGHCは、デフォルトでダイナミックリンクを使用します。静的ライブラリであるがありますので、.lib静的リンクが必要であることをリンカに通知してみてください。

ghc --makedlltest.hs-L。-optl-Bstatic -lhwdriver -optl-Bdynamic

自動生成されたバインディングに関しては、

c2hsが最も使いやすいと思いましたが、stdcallsを必要とするもので試したことはありません。

呼び出しが25程度しかない場合は、すべてのものを手動で作成するのはそれほど面倒ではありません。いくつかの小さなプロジェクトのために、私は数年前foreignに手動でバインディングを書くことができました...libvlc

于 2009-06-22T16:10:24.463 に答える
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
于 2009-06-22T17:21:21.297 に答える