Creating a Windows DLL from a Haskell Program and calling it from C++

Every so often you have to leave the warm comfortable safe world of Haskell and venture out into what some unsavoury types call the "the real world". I've always thought it better to call the real world rather than have it call on me in Haskell-land. But when that does not work you have the bite the bullet and make a DLL that can be called from other Windows programs. Every so often I do this and it works (for a single DLL) and then time passes and GHC changes and then suddenly you have to do something else with the latest shiny version of GHC. So here is the additional information you need to what's in the Haskell Platform 2011.2.0.1 distribution documentation (11.6.2 Making DLLs to be called from other languages) which works with version 7.0.3 of GHC.

Step 1: Read the documentation but of course don't completely believe it because it is written from the perspective of people that for some strange reason use operating systems with names that don't start with "Windows".

Step 2: As the documentation says create a file called Adder.hs and put the Haskell adder function in it:

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

Step 3: You also need to create a program to start up and wind down the GHC run-time which you should put in the file StartEnd.c:

 // 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();
}

 

Step 4: Compile up these programs to generate the DLLs etc.

ghc -c Adder.hs
ghc -c StartEnd.c
ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o

which will create the files Adder.dll, Adder.dll.a and Adder_stub.h which you will need in the next step.

Step 5: Now you can fire up Visual Studio and write a program that calls the Haskell adder function.

 // haskell_from_cpp.cpp : main project file.

#include "stdafx.h"
#include <stdio.h>

#include "Adder_stub.h"

extern "C" {
    void HsStart();
    void HsEnd();
}


using namespace System;

int main(array<System::String ^> ^args)
{
    HsStart();
    // can now safely call functions from the DLL
    printf("12 + 5 = %i\n", adder(12,5))    ;
    HsEnd();
    return 0;

}

Note that you don't need to include the header file for HsFFI.h because this is included in the header file Adder_stub.h. HOWEVER you now need to hack one of the header files. Specifically, you have to change the way the calling convention is specified for the adder function so that it is accepted by the Microsoft C++ compiler by editing the file Adder_stub.h:

 #include "HsFFI.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HsInt __stdcall adder(HsInt a1, HsInt a2);
#ifdef __cplusplus
}
#endif

Specifically, replace "__attribute__((__stdcall__))" with just "__stdcall ".

Now you will need to copy over some extra header files from your Haskell install directory from the lib/include sub-directory (look at the Header Files list in the window below). On my machine the include files are in C:\Program Files (x86)\Haskell Platform\2011.2.0.1\lib\include. You need ghcautoconf.h, ghconfig.h, ghcplatform.h, HsFFI.h and from the std directory you need Types.h (copy the std directory).

image

You need to adjust your project setting so the C++ compiler can find the header files. You will also need to add Adder.dll.a as an input to the Additional Dependencies filed under the Input section of the Linker settings for the project and perhaps add the search path for this file to the Additional Library Dependencies input field under the Linker tab. Now you should be able to build to generate an executable (in my case in the Debug sub-directory of the project).

Step 7: Copy Adder.dll to the same directory as the executable and run the program.

satnams@MSRC-ardberg1 ~/haskell/dll/haskell_from_cpp/Debug
$ ./haskell_from_cpp
12 + 5 = 17

You have now finally built a DLL from a Haskell program that can be called from C++ under Windows. Now, it was not that painful. Was it?