11.6. Building and using Win32 DLLs
Prev | Chapter 11. Running GHC on Win32 systems | Next

## 11.6. Building and using Win32 DLLs

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.

### 11.6.1. Creating a DLL

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.

### 11.6.2. Making DLLs to be called from other languages

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).

#### 11.6.2.1. Using from VBA

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.

#### 11.6.2.2. Using from C++

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

Prev | Up | Next
11.5. Using GHC (and other GHC-compiled executables) with cygwin | Home | Chapter 12. Known bugs and infelicities