Discussion:
[Haskell-cafe] FFI and Excel VBA
Lewis-Sandy, Darrell
2007-06-19 15:57:05 UTC
Permalink
I am new to using the Haskell FFI, and have been trying to implement the
example in section 11.6 of the GHC user's guide.



I have finally gotten to the point where my dll compiles (there is a missing
space in the mainDll.h code at line 4:12) using GHC 6.6.1, and used the
declare statement to expose the "adder" function in Excel VBA. My VBA Code
is given below:



Private Declare Function adder Lib " adder.dll" Alias "***@8" (ByVal x As
Integer, ByVal y As Integer) As Integer



Private Sub test()

Debug.Print adder(1, 2)

End Sub







My problem is this:

The function works fine (the immediate window displays 3), but when I
terminate Excel, I get an application error ("The instruction at ...
referenced memory at ... . The memory could not be read."). Does anyone
else have any experience calling Haskell from VBA that might be relevant?
Cyril Schmidt
2007-06-21 06:59:27 UTC
Permalink
A while ago I built a Haskell DLL that had to be invoked from Excel.
For some reason (I do not remember exactly why) it did not work directly,
so I wrote a small wrapper DLL in C++ that invoked Haskell functions.
The wrapper DLL was invoked by Excel via COM.

As regards your example, the only thing that springs to my mind is:
check that the calling convention is set correctly (that is, to stdcall
instead of ccall).

Cheers,

Cyril
Post by Lewis-Sandy, Darrell
I have finally gotten to the point where my dll compiles (there is a missing
space in the mainDll.h code at line 4:12) using GHC 6.6.1, and used the
declare statement to expose the "adder" function in Excel VBA. My VBA Code
Integer, ByVal y As Integer) As Integer
Private Sub test()
Debug.Print adder(1, 2)
End Sub
The function works fine (the immediate window displays 3), but when I
terminate Excel, I get an application error ("The instruction at …
referenced memory at … . The memory could not be read."). Does anyone else
have any experience calling Haskell from VBA that might be relevant?
Lewis-Sandy, Darrell
2007-06-21 14:46:56 UTC
Permalink
I was able to figure out how to get the example in chapter 11 of the GHC
manual to work. Since it was not intuitive (to a non C programmer), I
thought that I should post this so that others might take advantage of it.

KEYWORDS: Foreign, Export, Win32, DLL, VBA, Excel, GCH, Example

The contents of adder.hs are as follows:
--------------------------------------------
module Adder(adder) where

import Foreign

adder :: Int -> Int -> IO Int
adder x y = return (id$!(x+y))

foreign export stdcall adder :: Int -> Int -> IO Int
--------------------------------------------

The contents of the dllMain.c file are below:
--------------------------------------------

#include <windows.h>
#include <Rts.h>

extern void __stginit_Adder(void);

static char* args[] = { "ghcDll", NULL };

BOOL STDCALL DllMain ( HANDLE hModule, DWORD reason, void* reserved )
{
if (reason == DLL_PROCESS_ATTACH) {
startupHaskell(1, args, __stginit_Adder);
return TRUE;
}

if (reason == DLL_PROCESS_DETACH) {
shutdownHaskell();
return TRUE;
}

return TRUE;
}

--------------------------------------------
Note that the GHC manual omits the space between the "void" and the
__stgint_Adder(void) which causes a compile error. The second case (if
(reason == DLL_PROCESS_DETACH) is essential for garbage collection, and
shuts down the Haskell runtime when the DLL is unloaded.

To execute the Haskell code, I created a VBA module in an Excel workbook
with the following code snippet:
--------------------------------------------
Private Declare Function adder Lib "adder.dll" _
(ByVal x As Integer, ByVal y As Integer) As Integer

Private Sub test()
Debug.Print adder(1, 2)
End Sub
--------------------------------------------
NOTE: If the dll is not ultimately placed in your environments' PATH, then
you may have to modify the library name to include the actual path
information (or else you will get an error indicating that the library can't
be found). Example: ... adder Lib "c:\myDlls\adder\adder.dll" ...

Finally, you need to explicitly define an exports file to keep the c
compiler from mangling the names too badly. The contents of adder.def are
as follows:
--------------------------------------------
EXPORTS
adder
suber
hello
--------------------------------------------

With these preliminaries out of the way, you can compile this to a dll using
the following sequence of commands:
--------------------------------------------
ghc -c adder.hs -fglasgow-exts
ghc -c dllMain.c
ghc --mk-dll -optdll--def=adder.def -o adder.dll adder.o adder_stub.o
dllMain.o
--------------------------------------------

When this is all done, open the XLProject and press F5 to run, or simply
type "=adder(1,2)" in any cell of the workbook.

I hope that this was helpful.

-Darrell Lewis-Sandy
Lewis-Sandy, Darrell
2007-06-21 15:05:30 UTC
Permalink
Whoops - I posted the wrong version of the exports list. Compilation with
that adder.def will fail (since it the Haskell code doesn't export either
suber or hello). Below is the revised example:

KEYWORDS: Foreign, Export, Win32, DLL, VBA, Excel, GCH, Example

The contents of adder.hs are as follows:
--------------------------------------------
module Adder(adder) where

import Foreign

adder :: Int -> Int -> IO Int
adder x y = return (id$!(x+y))

foreign export stdcall adder :: Int -> Int -> IO Int
--------------------------------------------

The contents of the dllMain.c file are below:
--------------------------------------------

#include <windows.h>
#include <Rts.h>

extern void __stginit_Adder(void);

static char* args[] = { "ghcDll", NULL };

BOOL STDCALL DllMain ( HANDLE hModule, DWORD reason, void* reserved ) {
if (reason == DLL_PROCESS_ATTACH) {
startupHaskell(1, args, __stginit_Adder);
return TRUE;
}

if (reason == DLL_PROCESS_DETACH) {
shutdownHaskell();
return TRUE;
}

return TRUE;
}

--------------------------------------------
Note that the GHC manual omits the space between the "void" and the
__stgint_Adder(void) which causes a compile error. The second case (if
(reason == DLL_PROCESS_DETACH) is essential for garbage collection, and
shuts down the Haskell runtime when the DLL is unloaded.

To execute the Haskell code, I created a VBA module in an Excel workbook
with the following code snippet:
--------------------------------------------
Private Declare Function adder Lib "adder.dll" _
(ByVal x As Integer, ByVal y As Integer) As Integer

Private Sub test()
Debug.Print adder(1, 2)
End Sub
--------------------------------------------
NOTE: If the dll is not ultimately placed in your environments' PATH, then
you may have to modify the library name to include the actual path
information (or else you will get an error indicating that the library can't
be found). Example: ... adder Lib "c:\myDlls\adder\adder.dll" ...

Finally, you need to explicitly define an exports file to keep the c
compiler from mangling the names too badly. The contents of adder.def are
as follows:
--------------------------------------------
EXPORTS
adder
--------------------------------------------

With these preliminaries out of the way, you can compile this to a dll using
the following sequence of commands:
--------------------------------------------
ghc -c adder.hs -fglasgow-exts
ghc -c dllMain.c
ghc --mk-dll -optdll--def=adder.def -o adder.dll adder.o adder_stub.o
dllMain.o
--------------------------------------------

When this is all done, open the XLProject and press F5 to run, or simply
type "=adder(1,2)" in any cell of the workbook.

My apologies for any confusion caused by my earlier posting.

-Darrell Lewis-Sandy
Jason Dagit
2007-06-21 17:15:22 UTC
Permalink
Darrell,

Would you be willing to put your step by step instructions on the
wiki? I think having them on the wiki would benefit the largest
audience.

Thanks!
Jason
Post by Lewis-Sandy, Darrell
Whoops - I posted the wrong version of the exports list. Compilation with
that adder.def will fail (since it the Haskell code doesn't export either
KEYWORDS: Foreign, Export, Win32, DLL, VBA, Excel, GCH, Example
--------------------------------------------
module Adder(adder) where
import Foreign
adder :: Int -> Int -> IO Int
adder x y = return (id$!(x+y))
foreign export stdcall adder :: Int -> Int -> IO Int
--------------------------------------------
--------------------------------------------
#include <windows.h>
#include <Rts.h>
extern void __stginit_Adder(void);
static char* args[] = { "ghcDll", NULL };
BOOL STDCALL DllMain ( HANDLE hModule, DWORD reason, void* reserved ) {
if (reason == DLL_PROCESS_ATTACH) {
startupHaskell(1, args, __stginit_Adder);
return TRUE;
}
if (reason == DLL_PROCESS_DETACH) {
shutdownHaskell();
return TRUE;
}
return TRUE;
}
--------------------------------------------
Note that the GHC manual omits the space between the "void" and the
__stgint_Adder(void) which causes a compile error. The second case (if
(reason == DLL_PROCESS_DETACH) is essential for garbage collection, and
shuts down the Haskell runtime when the DLL is unloaded.
To execute the Haskell code, I created a VBA module in an Excel workbook
--------------------------------------------
Private Declare Function adder Lib "adder.dll" _
(ByVal x As Integer, ByVal y As Integer) As Integer
Private Sub test()
Debug.Print adder(1, 2)
End Sub
--------------------------------------------
NOTE: If the dll is not ultimately placed in your environments' PATH, then
you may have to modify the library name to include the actual path
information (or else you will get an error indicating that the library can't
be found). Example: ... adder Lib "c:\myDlls\adder\adder.dll" ...
Finally, you need to explicitly define an exports file to keep the c
compiler from mangling the names too badly. The contents of adder.def are
--------------------------------------------
EXPORTS
adder
--------------------------------------------
With these preliminaries out of the way, you can compile this to a dll using
--------------------------------------------
ghc -c adder.hs -fglasgow-exts
ghc -c dllMain.c
ghc --mk-dll -optdll--def=adder.def -o adder.dll adder.o adder_stub.o
dllMain.o
--------------------------------------------
When this is all done, open the XLProject and press F5 to run, or simply
type "=adder(1,2)" in any cell of the workbook.
My apologies for any confusion caused by my earlier posting.
-Darrell Lewis-Sandy
_______________________________________________
Haskell-Cafe mailing list
http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
http://www.haskell.org/mailman/listinfo/haskell-cafe
Lennart Augustsson
2007-06-23 09:54:45 UTC
Permalink
There is a number of problems, I'm not sure which one you are encountering.
Here are some that I remember:

The sample C code doesn't shut down the ghc runtime properly when the DLL
is unloaded. This causes a timer interrupt to jump into the void. This is
easily fixed with a couple of more lines of C.

The ghc runtime installs handlers for various things that it shouldn't touch
when used as a DLL.

-- Lennart
Post by Lewis-Sandy, Darrell
I am new to using the Haskell FFI, and have been trying to implement the
example in section 11.6 of the GHC user's guide.
I have finally gotten to the point where my dll compiles (there is a
missing space in the mainDll.h code at line 4:12) using GHC 6.6.1, and
used the declare statement to expose the "adder" function in Excel VBA. My
As Integer, ByVal y As Integer) As Integer
Private Sub test()
Debug.Print adder(1, 2)
End Sub
*My problem is this:*
The function works fine (the immediate window displays 3), but when I
terminate Excel, I get an application error ("The instruction at …
referenced memory at … . The memory could not be read."). Does anyone else
have any experience calling Haskell from VBA that might be relevant?
_______________________________________________
Haskell-Cafe mailing list
http://www.haskell.org/mailman/listinfo/haskell-cafe
Simon Marlow
2007-06-25 11:45:17 UTC
Permalink
We don't recommend calling shutdownHaskell() from DllMain(). Some information here:

http://haskell.org/haskellwiki/GHC/Using_the_FFI#Debugging_Haskell_DLLs

It should be safe to call shutdownHaskell() (aka hs_exit()) *before* unloading a
DLL, and before exiting the program.

Cheers,
Simon
Post by Lennart Augustsson
There is a number of problems, I'm not sure which one you are encountering.
The sample C code doesn't shut down the ghc runtime properly when the
DLL is unloaded. This causes a timer interrupt to jump into the void.
This is easily fixed with a couple of more lines of C.
The ghc runtime installs handlers for various things that it shouldn't
touch when used as a DLL.
-- Lennart
I am new to using the Haskell FFI, and have been trying to implement
the example in section 11.6 of the GHC user's guide.
I have finally gotten to the point where my dll compiles (there is a
missing space in the mainDll.h code at line 4:12) using GHC 6.6.1,
and used the declare statement to expose the "adder" function in
(ByVal x As Integer, ByVal y As Integer) As Integer
Private Sub test()
Debug.Print adder(1, 2)
End Sub
*My problem is this:*
The function works fine (the immediate window displays 3), but when
I terminate Excel, I get an application error ("The instruction at …
referenced memory at … . The memory could not be read."). Does
anyone else have any experience calling Haskell from VBA that might
be relevant?
_______________________________________________
Haskell-Cafe mailing list
http://www.haskell.org/mailman/listinfo/haskell-cafe
------------------------------------------------------------------------
_______________________________________________
Haskell-Cafe mailing list
http://www.haskell.org/mailman/listinfo/haskell-cafe
Loading...