title: Haskell foreign import stdcall on DLL function
body: <p>This is probably a really easy question to answer, but for some reason I'm really struggling with it.</p>
  
  <p>I have a DLL written in C to access hardware at a protocol level, and I want to write a Haskell program that calls some of those C functions.  Here's a snippet of the relevant C header (with names just slightly obfuscated due to possible copyrighting issues):</p>
  
  <pre><code>#ifdef HWDRIVER_EXPORTS
  #define HWDRIVER_API __declspec(dllexport)
  #else
  #define HWDRIVER_API __declspec(dllimport)
  #endif
  HWDRIVER_API int HW_Init(void);
  </code></pre>
  
  <p>This has been compiled as a DLL in Visual Studio 2003, and I have successfully loaded the DLL from both C and C#, so I am confident that the DLL works fine.  The DLL is named "hw-driver.dll".</p>
  
  <p>Next, here's the Haskell source code just to test if I can properly load the DLL and call the simplest function in it:</p>
  
  <pre><code>{-# LANGUAGE ForeignFunctionInterface #-}
  module Main
      where
  import Foreign
  import Foreign.C
  
  foreign import stdcall "hw-driver" "HW_Init"  hwInit :: IO (CInt)
  
  main = do
      x &lt;- hwInit
      if x == 0 
          then putStr "Successfully initialized"
          else putStr "Could not initialize"
  </code></pre>
  
  <p>The line that is giving me trouble is the foreign import line.  As I understand it, the syntax is foreign (import/export) (ccall/stdcall) <em>library-name</em> <em>C-function-name</em> <em>haskell-function-name</em> :: <em>Haskell type declaration</em>.  So mine should be foreign import stdcall (because you use stdcall when loading a DLL in Win32) "hw-driver" (because the file is named "hw-driver.dll" and it is located in the same directory as dlltest.hs) "HW_Init" (the name of the function in C) hwInit :: IO (Cint) (void arguments, returning an int).</p>
  
  <p>However, when I try running <code>ghci dlltest.hs</code>, I get the following output:</p>
  
  <pre><code>[1 of 1] Compiling Main             ( dlltest.hs, interpreted )
  
  dlltest.hs:8:43: parse error on input `"'
  Failed, modules loaded: none.
  </code></pre>
  
  <p>Line 8, column 43 is the first quotation mark on HW_Init.  Okay, so maybe I have to put both the library name and the function name in one string, I've seen that in a few places.  If I try running that, then I get:</p>
  
  <pre><code>[1 of 1] Compiling Main             ( dlltest.hs, interpreted )
  
  dlltest.hs:8:23: Malformed entity string
  Failed, modules loaded: none.
  </code></pre>
  
  <p>8:23 is the first quotation mark of the new string "hw-driver HW_Init".</p>
  
  <p>I don't believe there's anything wrong with my ghc setup (6.10.3), because I can run the following code that was copy-pasted from Real World Haskell in ghci:</p>
  
  <pre><code>{-- 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 -&gt; CDouble
  {-- /snippet binding --}
  
  {-- snippet highlevel --}
  fastsin :: Double -&gt; Double
  fastsin x = realToFrac (c_sin (realToFrac x))
  {-- /snippet highlevel --}
  
  {-- snippet use --}
  main = mapM_ (print . fastsin) [0/10, 1/10 .. 10/10]
  {-- /snippet use --}
  </code></pre>
  
  <p>So long question short, how do I properly declare a foreign import on a Win32 DLL?  I haven't been able to find <em>anything</em> on Google.</p>
  
  <p>And to kind of tag along on that question, will I be able to use a program like c2hs or hsc2hs to parse the header file <code>hw-driver.h</code> so I don't have to manually write the foreign import calls for all 20-25 functions contained in that DLL?  I haven't been able to find any decent examples of that either.</p>
  
  <hr/>
  
  <p>EDIT: ephemient has pointed out that the correct syntax for the foreign import line is:</p>
  
  <pre><code>foreign import stdcall "hw-driver.h HW_Init" hwInit :: IO CInt
  </code></pre>
  
  <p>With this, I am able to call <code>ghci dlltest.hs -lhw-driver</code> and properly call the main function with a successful return code.  However, the command <code>ghc --make dlltest.hs -lhw-driver</code> fails with a linker error.  So, here's the verbose output of that command (note that I have all of hw-driver.{dll,h,lib} in the working directory):</p>
  
  <pre><code>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
  </code></pre>
  
  <p><hr/>
  As it turns out, the actual linking wasn't as difficult as I was making it out to be.  I was using <code>foreign import</code> <strong><code>stdcall</code></strong> which I believed to be correct with a DLL built in Visual Studio 2003.  I had to download the <code>pexports</code> tool for MinGW, which lists the functions exported from a DLL.  The linker had been looking for HWInit@0 the whole time, but <code>pexports</code> said the DLL was exporting just HWInit.</p>
  
  <p>I changed my line to <code>foreign import</code> <strong><code>ccall</code></strong> instead, and I was successfully able to link the program using either of <code>ghc --make dlltest.hs hw-driver.lib</code> or <code>ghc --make dlltest.hs -L. -lhw-driver</code> due to having both the .lib and the .dll file available in the working directory.</p>