Win32 dlls -- how to do it?

Andy Serpa ac@onehorseshy.com
Fri, 27 Jun 2003 17:27:20 -0400


>     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 
> then
>     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
> will silence those errors from the linker.
> 
Ok, "-package base" did the trick, and I was able to call the 
function from my other program.

> The warning given when compiling the C code in dllMain.c comes from
> the declaration of __stginit_Adder as
> 
> EXTFUN(__stginit_Adder)
> 
> Which is not really the type that startupHaskell expects.  Declare
> it instead as
> 
> extern void __stginit_Adder(void);
> 
> and the warning will be silenced.
> 
Ok.

> The much bigger gotcha is that the code in dllMain.c from Sect 11 of
> the user's guide will probably not work, because Windows forbids
> certain things 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 runtime into the same DLL.
> 
I did not try statically linking all into a single dll yet.  We'll
see.

> My typical solution is to provide entry points to explicitly startup
> and 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.
> 
>  Note
>    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
> again)
>    and that can be used to startup and shutdown the Haskell runtime.
>    
>  You
>    can't do this directly from Java, because the FFI functions don't
> have
>    the mangled names that Java expects, and you can't do it from
> Haskell
>    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);
>     hs_add_root(__stginit_Adder);
> }
> 
> JNIEXPORT void JNICALL Java_Adder_haskellExit(JNIEnv *e, jclass c) {
>     hs_exit();
> }
> ====================
> 
> 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 it
>    gives me finer (actually, just less verbose) control over DLL
> options):
> 
>   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
>    
> the
>    entries otherwise.
> 
> 6. Write a Java driver that loads the native library (the DLL),
> initializes
>    the Haskell runtime, calls our simple DLL function, and then 
> shutdowns
>    the Haskell runtime:
> 
> ==== Adder.java
> public class Adder {
>   static {
>     System.loadLibrary("adder");
>     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 . Adder.java
> 
> 8. Enjoy:
> 
>   java -classpath . Adder
> 
> ----

Aha.  I'll see if I can adapt that to my system.  Thanks for all the
help...

Andy Serpa
ac@onehorseshy.com