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