Win32 dlls -- how to do it?

Kevin S. Millikin" <
Fri, 27 Jun 2003 14:35:15 -0500

    ac> New to Haskell, new to GHC.  My initial intention in picking up
    ac> Haskell is to be able to write some functions in Haskell and 
    ac> compile them into .dlls for Win32 that I can call from programs
    ac> written in other languages.  So before I get too deeply 
invested I
    ac> want to make sure that is possible.

It's certainly possible.  I do it here all the time on a commercial
project.  I even volunteered a few months ago to write some better
documentation, but I sadly have not had the time to do it.

As Sigbjorn says, adding -package base when linking the DLL with ghc 
silence those errors from the linker.

The warning given when compiling the C code in dllMain.c comes from the
declaration of __stginit_Adder as


Which is not really the type that startupHaskell expects.  Declare it
instead as

extern void __stginit_Adder(void);

and the warning will be silenced.

The much bigger gotcha is that the code in dllMain.c from Sect 11 of 
user's guide will probably not work, because Windows forbids certain 
from happening in dllMain (basically, anything that could possibly 
cause one
to reattach to the DLL).  That includes creating new threads, and that
includes creating timers, and startupHaskell tries to do that.  I don't
remember at the moment if it is a problem when the GHC runtime is in a
separate DLL, but it is certainly a problem when linking the GHC 
into the same DLL.

My typical solution is to provide entry points to explicitly startup 
shutdown the Haskell runtime, outside of a call to dllMain.  Here is an
example, calling Adder from Java:

1. Write some Haskell code to implement the add function in the DLL. 
   that Java will expect a mangled name, which we supply manually:

==== Adder.hs
module Adder (add) where

import Foreign (Ptr, Int32)

data JNIEnv
data JClass

foreign export stdcall "Java_Adder_add"
    add :: Ptr JNIEnv -> Ptr JClass -> Int32 -> Int32 -> IO Int32

add :: Ptr JNIEnv -> Ptr JClass -> Int32 -> Int32 -> IO Int32
add _ _ m n = return (m + n)

2. Compile.  Don't forget -fglasgow-exts:

  ghc -fglasgow-exts -c Adder.hs

3. Write C functions that can be called by Java (mangling the names 
   and that can be used to startup and shutdown the Haskell runtime. 
   can't do this directly from Java, because the FFI functions don't 
   the mangled names that Java expects, and you can't do it from 
   code for obvious reasons.

==== ccode.c
#include <jni.h>
#include <HsFFI.h>

extern void __stginit_Adder(void);

static char *args[] = { "ghcDll", 0 };
static int argc = 1;
static char **argv = args;

JNIEXPORT void JNICALL Java_Adder_haskellInit(JNIEnv *e, jclass c) {
    hs_init(&argc, &argv);

JNIEXPORT void JNICALL Java_Adder_haskellExit(JNIEnv *e, jclass c) {

4. Compile it.  JAVAHOME is presumed to be set to the root of your Java
   install (the directory containing the "include" subdirectory):

  ghc -I$JAVAHOME/include -I$JAVAHOME/include/win32 -optc -mno-cygwin 
-c ccode.c

5. Link into DLL, using ghc (I generally call dllwrap directly, because 
   gives me finer (actually, just less verbose) control over DLL 

  ghc --mk-dll -optdll --add-stdcall-alias -o adder.dll Adder.o 
Adder_stub.o ccode.o -package base

   "-optdll --add-stdcall-alias" is important, because Java can't find 
   entries otherwise.

6. Write a Java driver that loads the native library (the DLL), 
   the Haskell runtime, calls our simple DLL function, and then 
   the Haskell runtime:

public class Adder {
  static {
    haskellInit(); }

  public static native void haskellInit();
  public static native void haskellExit();

  public static native int add(int m, int n);

  public static void main(String[] args) {
    try {
      System.out.println("Answer is: " + Adder.add(32, 10)); }
    finally {
      haskellExit(); }}}

7. Compile the Java code:

  javac -classpath .

8. Enjoy:

  java -classpath . Adder

Kevin S. Millikin           Architecture Technology Corporation
Research Scientist          Specialists in Computer Architecture
(952)829-5864 x162