flxldn.
the original snub
Calling Haskell from Excel
I finally managed to do it - mostly following this blog post … except that I don’t have Excel 2007 and none of the files created wanted to work. I knew that xlw was working for me so I restarted from there. So here is what I did - without jumping any steps:
1 - Getting the necessary libraries and compiler
- I used Visual Studio 2005 Professional (it might work with other versions)
- Get xlw - for Excel 2003 / VS 2005 I used version 2.1 - and build the Visual Studio 2005 solution (folder xlwVisio8). Compiling the solution gives you the file xlwVisio.xll in \debug which should work after adding it in Excel as an addin … everythin below is useless if this doesn’t work. It also produces the executable InterfaceGenerator.exe which you will need later.
- Get Haskell - I used the latest which is ghc 6.10.4. Only static dll’s work with this version which I thought is fine with me.
2- Produce a Haskell dll
- Disclaimer: Most of this is lifted from the blog post above and the ghc documentation.
- You need the following files in your path or simply in the directory with the files below: link.exe, lib.exe and mspdb80.dll - you will find them by searching in Program Files.
- The following files are needed:
- adder.hs - the actual Haskell module to be called by Excel
- adder.h - the header for the import in your c++ project
- adder.def - the definition file for the dll. I needed this, even if it seems to be optional
- dllMain.c - code that makes the rest of the dll
adder.hs
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
adder.h
#ifdef __cplusplus
extern “C”
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif
adder.def
LIBRARY Adder
EXPORTS
adder@8=_adder
HsBegin@0=_HsBegin
HsEnd@0=_HsEnd
dllMain.c
#include <windows.h>
#include <rts.h>
extern void __stginit_Adder(void);
static char* args[] = { “ghcDll”, NULL }; /* N.B. argv arrays must end with NULL */
BOOL STDCALL DllMain( HANDLE hModule, DWORD reason, void* reserved)
{ return TRUE;}
__stdcall void HsBegin()
{
startupHaskell(1, args, __stginit_Adder);
}
__stdcall void HsEnd()
{
hs_exit();
}
Now you need to compile this. The adder_stub.h and adder_stub.c files will be created by this. I added -static everywhere. Not sure if this is actually necessary:
ghc -static -c adder.hs -fglasgow-exts
ghc -static -c dllMain.c
ghc -static -shared -o adder.dll adder.o adder_stub.o dllMain.o
lib /MACHINE:x86 /DEF:adder.def /OUT:adder.lib /NOLOGO /SUBSYSTEM:WINDOWS
After this you have three files that will be necessary for your xll project: adder.h, adder.lib and adder.dll. Place the adder.dll in a directory that is in the path (e.g. c:\Windows).
3 - Create the xll
For this I modified the sample project xlwViso (and bits of xlwLib). Since xlwLib takes care of the memory management and also the initiation and cleanup of the xll when loaded / unloaded I added the following two lines to xlOpenClose.h:
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
This probably means that the xlwLib created by this will fail if there is no dll loaded which provides these two functions - but it avoids the dependency of the library of a specific header file - as long as your Haskell dll provides these this should be fine.
These two functions need to be called by xlAutoOpen and xlAutoClose. In xlOpenClose.cpp i added a call to HsBegin(); and HsEnd(); just before the return statement.
I removed all headers and source files from the xlwVisio project except for Test.h, Test.cpp and xlwTest.cpp - the latter one will be regenerated by the InterfaceGenerator.exe.
In Test.h I added the following line:
short SumTwo(short d1, short d2);
and in Test.cpp the following lines:
#include <adder.h>
short SumTwo(short d1, short d2){
return adder(d1, d2);
}
The directory with adder.h and adder.lib needs to be added to the “Additional Include Directories” and “Additional Library Directories” in the properties of the xlwViso project. Also adder.lib needs to be added to the “Additional Dependencies”.
From the command line, regenerate the xlwTest.cpp file with the InterfaceGenerator.exe that you got when recompiling the xlw solution at the beginning:
InterfaceGenerator Test.h
Build the project, add the xll to Excel and … try a “=SumTwo(1,2)”.
I did mention to put the adder.dll in a reachable directory?