문제

이것은 아마도 대답하기 쉬운 질문 일 것입니다. 그러나 어떤 이유로 나는 정말로 그것에 어려움을 겪고 있습니다.

프로토콜 수준에서 하드웨어에 액세스하기 위해 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"입니다.

다음으로 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- 기능-이름 Haskell-function-name :: Haskell 유형 선언. 따라서 광산은 외국 수입 stdcall이어야합니다 (Win32에 dll을로드 할 때 stdcall을 사용하기 때문에 파일의 이름이 "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 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의 Real World Haskell에서 복사 한 다음 코드를 실행할 수 있기 때문에 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에서 외국 수입을 올바르게 선언하는 방법은 무엇입니까? 나는 찾을 수 없었습니다 아무것 구글에서.

그리고 그 질문에 대한 태그의 종류를 위해, 나는 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 성공적인 반환 코드로 기본 기능을 올바르게 호출하십시오. 그러나 명령 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 DLL에서 내보낸 함수를 나열하는 Mingw 용 도구. 링커는 항상 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 수입 선언,

인기 → " [static] [chname] [&] [CID] "
            | " dynamic "
            | " wrapper "

어디 chname "라이브러리 이름"이 아닌 "C 헤더 이름"입니다.

FFI 사양 # 4.1.4 헤더 파일의 사양

가져 오기 선언에 지정된 AC 헤더는 항상 #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가 사용하기가 가장 쉬운 것으로 나타 났지만 필요한 것에 대해 시도한 적이 없습니다. stdcall에스.

그렇지 않습니다 저것 모든 글을 쓰는 것이 번거 롭습니다 foreign 25 개의 전화 만 있으면 수동으로 물건을 넣습니다. 나는 수동으로 바인딩을 작성했다 libvlc 몇 년 전, 작은 프로젝트를 위해 ...

다른 팁

아래는 [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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top