Making Haskell libraries into DLLs doesn't work on Windows at the moment; we hope to re-instate this facility in the future (see Section 4.12, “Using shared libraries”). Note that building an entire Haskell application as a single DLL is still supported: it's just multi-DLL Haskell programs that don't work. The Windows distribution of GHC contains static libraries only.
Sealing up your Haskell library inside a DLL is straightforward; compile up the object files that make up the library, and then build the DLL by issuing a command of the form:
ghc –shared -o foo.dll bar.o baz.o wibble.a -lfooble
By feeding the ghc compiler driver the option –shared
, it
will build a DLL rather than produce an executable. The DLL will
consist of all the object files and archives given on the command
line.
A couple of things to notice:
By default, the entry points of all the object files will be exported from
the DLL when using –shared
. Should you want to constrain
this, you can specify the module definition file to use
on the command line as follows:
ghc –shared -o .... MyDef.def
See Microsoft documentation for details, but a module definition file simply lists what entry points you want to export. Here's one that's suitable when building a Haskell COM server DLL:
EXPORTS DllCanUnloadNow = DllCanUnloadNow@0 DllGetClassObject = DllGetClassObject@12 DllRegisterServer = DllRegisterServer@0 DllUnregisterServer = DllUnregisterServer@0
In addition to creating a DLL, the –shared
option also
creates an import library. The import library name is derived from the
name of the DLL, as follows:
DLL: HScool.dll ==> import lib: libHScool.dll.a
The naming scheme may look a bit weird, but it has the purpose of allowing
the co-existence of import libraries with ordinary static libraries (e.g.,
libHSfoo.a
and
libHSfoo.dll.a
.
Additionally, when the compiler driver is linking in non-static mode, it
will rewrite occurrence of -lHSfoo
on the command line to
-lHSfoo.dll
. By doing this for you, switching from
non-static to static linking is simply a question of adding
-static
to your command line.
This section describes how to create DLLs to be called from other languages, such as Visual Basic or C++. This is a special case of Section 8.2.1.2, “Making a Haskell library that can be called from foreign code”; we'll deal with the DLL-specific issues that arise below. Here's an example:
Use foreign export declarations to export the Haskell functions you want to call from the outside. For example:
-- Adder.hs {-# LANGUAGE ForeignFunctionInterface #-} module Adder where adder :: Int -> Int -> IO Int -- gratuitous use of IO adder x y = return (x+y) foreign export stdcall adder :: Int -> Int -> IO Int
Add some helper code that starts up and shuts down the Haskell RTS:
// StartEnd.c #include <Rts.h> extern void __stginit_Adder(void); void HsStart() { int argc = 1; char* argv[] = {"ghcDll", NULL}; // argv must end with NULL // Initialize Haskell runtime char** args = argv; hs_init(&argc, &args); // Tell Haskell about all root modules hs_add_root(__stginit_Adder); } void HsEnd() { hs_exit(); }
Here, Adder
is the name of the root module in the module
tree (as mentioned above, there must be a single root module, and hence a
single module tree in the DLL). Compile everything up:
ghc -c Adder.hs ghc -c StartEnd.c ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o
Now the file Adder.dll
can be used from other
programming languages. Before calling any functions in Adder it is necessary
to call HsStart
, and at the very end call
HsEnd
.
Warning: It may appear tempting to use
DllMain
to call
hs_init
/hs_exit
, but this won't work
(particularly if you compile with -threaded
). There are
severe restrictions on which actions can be performed during
DllMain
, and hs_init
violates these
restrictions, which can lead to your dll freezing during startup (see
bug
#3605).
An example of using Adder.dll
from VBA is:
Private Declare Function Adder Lib "Adder.dll" Alias "adder@8" _ (ByVal x As Long, ByVal y As Long) As Long Private Declare Sub HsStart Lib "Adder.dll" () Private Declare Sub HsEnd Lib "Adder.dll" () Private Sub Document_Close() HsEnd End Sub Private Sub Document_Open() HsStart End Sub Public Sub Test() MsgBox "12 + 5 = " & Adder(12, 5) End Sub
This example uses the
Document_Open
/Close
functions of
Microsoft Word, but provided HsStart
is called before the
first function, and HsEnd
after the last, then it will
work fine.
An example of using Adder.dll
from C++ is:
// Tester.cpp #include "HsFFI.h" #include "Adder_stub.h" #include <stdio.h> extern "C" { void HsStart(); void HsEnd(); } int main() { HsStart(); // can now safely call functions from the DLL printf("12 + 5 = %i\n", adder(12,5)) ; HsEnd(); return 0; }
This can be compiled and run with:
$ ghc -o tester Tester.cpp Adder.dll.a $ tester 12 + 5 = 17