This is an alternative approach to this question here: Export Haskell lib as DLL
I am working with GHC version 8.6.1 [latest] and am following the documentation for compiling a DLL from a haskell library for the subsequent use in VBA.
My files look like this:
Adder.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
module Adder where
adder :: Int -> Int -> IO Int
adder x y = return (x+y)
foreign export ccall adder :: Int -> Int -> IO Int
StartEnd.c:
#include <Rts.h>
void HsStart() {
int argc = 1;
char* argv[] = {"ghcDll", NULL};
char** args = argv;
hs_init(&argc, &args);
}
void HsEnd() {
hs_exit();
}
I copied the folder ghc-8.6.1\lib\include to the build location and copied the two files into that folder (because I cannot figure out how to pass the -I[PATH] parameter correctly).
Running these compilation steps, I get:
ghc -c Adder.hs
--> no error
ghc -c StartEnd.c
--> no error
ghc -shared -no-hs-main -o Adder.dll Adder.o Adder_stub.h StartEnd.o
--> no error
and the files
Adder.dll
Adder.dll.a
Adder.hi
Adder.o
Adder_stub.h
startEnd.o
I tried using the third compilation command like it is in the documentation ghc -shared -o Adder.dll Adder.o Adder_stub.h StartEnd.o and with the parameter -no-hs-main just to be sure.
My VBA script looks like this:
Private Declare Function Adder Lib "PATH\TO\Adder.dll" Alias "adder" _
(ByVal x As Long, ByVal y As Long) As Long
Private Declare Sub HsStart Lib "PATH\TO\Adder.dll" ()
Private Declare Sub HsEnd Lib "PATH\TO\Adder.dll" ()
Public Sub test()
HsStart
MsgBox "12 + 5 = " & Adder(12, 5)
HsEnd
End Sub
However, whenever I try to run the Sub test, I get the error message:
Run-Time error '48'
File not found:
PATH\TO\Adder.dll
Can you please tell me where I made a mistake and how I can fix it?
If it helps, the output of dumpbin /EXPORTS Adder.dll is this:
Microsoft (R) COFF/PE Dumper Version 14.00.24234.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file Adder.dll
File Type: DLL
Section contains the following exports for Adder.dll
00000000 characteristics
5BBD9533 time date stamp Wed Oct 10 07:59:15 2018
0.00 version
1 ordinal base
29737 number of functions
29737 number of names
ordinal hint RVA name
1 0 004E0F18 ALLOC_BH_adm
...
75 4A 00001681 HsEnd
76 4B 00001640 HsStart
...
472 1D7 00001540 adder
...
My Path:
C:\Users\scfa\AppData\Local\Programs\MiKTeX 2.9\miktex\bin\x64\;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin;C:\Users\scfa\Documents\myProgramms\dev_msys2\mingw64\bin;C:\Users\scfa\Documents\myProgramms\dev_msys2\mingw32\bin;C:\Users\scfa\Documents\myProgramms\ghc-8.6.1\bin;C:\Users\scfa\Documents\myProgramms\ghc-8.6.1\mingw\bin
Related
I am working with GHC version 7.6.1, following the steps in the docs for creating a DLL from a Haskell lib for using it in VBA:
GHC Docs 7.6.1 # Make a DLL
The files I use are the exact same ones as in the Docs. When it comes to compiling, the following to commands work nearly as expected:
ghc -c Adder.hs
ghc -c StartEnd.c
The first command returns this:
Adder.hs:7:1: Warning:
the 'stdcall' calling convention is unsupported on this platform,
treating as ccall
When checking declaration:
foreign export stdcall "adder" adder :: Int -> Int -> IO Int
Which I guess should be fine...
However, the last command ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o produces this error:
Adder_stub.h:1:19: fatal error: HsFFI.h: No such file or directory
compilation terminated.
This thread pointed me towards recognizing that GHC isn't using the header files located in its GHC-HOME/lib/include.
The solution in this thread was to use GHC with the option -I [path to include-folder], however, when trying it (and looking at the man and docs) there is no such option for this version of GHC; nor is there in the newer versions up to 8.4.3.
When reading this docs I also encountered that it should be possible to use the option mk-dll under windows to generate dlls; however, according to the man page of my installed GHC, there is no such option, even though I downloaded and installed the windows 64-bit installer.
I then tried to just copy the include folder to some other location; copying the files needed by the third compilation command into the folder too and tried it again. It successfully compiled the DLL;
However, when I try to use the DLL from an Excel(macro-enabled) workbook, I get the error DLL not found. When attempting to reference the DLL in excel (via Tools->References->Browse-> chooseDll) it tells me that it can't add the DLL.
Examining the DLL with DependencyWalker, I get this error:
Error: At least one required implicit or forwarded dependency was not found.
Error: At least one module has an unresolved import due to a missing export function in an implicitly dependent module.
Error: Modules with different CPU types were found.
Warning: At least one delay-load dependency module was not found.
Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.
Do you have any ideas on how to resolve this?
Thanks a lot...
Update
When I try to use the compiled library from C++ as discribed in the docs I end up with this error message:
PS C:\Users\dev\programming\excel_haskell_binding> ghc -o tester.exe .\Tester.cpp .\include\Adder.dll.a
C:\Users\dev\AppData\Local\Temp\ghc4088_0\ghc4088_0.o:ghc4088_0.c:(.text+0x0): multiple definition of `main'
Tester.o:Tester.cpp:(.text+0x0): first defined here
C:\Users\dev\AppData\Local\Temp\ghc4088_0\ghc4088_0.o:ghc4088_0.c:(.text+0x4f): undefined reference to `ZCMain_main_closure'
c:/ghc/ghc-7.6.1/mingw/bin/../lib/gcc/x86_64-w64-mingw32/4.6.3/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\dev\AppData\Local\Temp\ghc4088_0\ghc4088_0.o: bad reloc address 0x0 in section `.pdata'
collect2: ld returned 1 exit status
Update 2
I am also trying a different approach; see this question GHC compile DLL for use in VBA
I was struggling with the same problem, but after a long fight, I managed to run the command from 64bit Excel. Follow these steps:
1) Create Adder.hs (notice ccall, not stdcall):
{-# LANGUAGE ForeignFunctionInterface #-}
module Adder where
adder :: Int -> Int -> IO Int -- gratuitous use of IO
adder x y = return (x+y)
foreign export ccall adder :: Int -> Int -> IO Int
2) Create StartEnd.c:
#include <Rts.h>
void HsStart()
{
int argc = 1;
char* argv[] = {"ghcDll", NULL}; // argv must end with NULL
// Initialize Haskell runtime
char** args = argv;
hs_init(&argc, &args);
}
void HsEnd()
{
hs_exit();
}
3) Compile these files:
ghc -c Adder.hs
ghc -c StartEnd.c
4) Copy following files from "C:\Program Files\Haskell Platform\8.6.3\lib\include" to your build folder (remember to put stg/Types.h into stg folder). You may also copy all the contents of /include folder if you wish:
HsFFI.h
ghcconfig.h
ghcautoconf.h
ghcplatform.h
stg/Types.h
5) Create the dll:
ghc -shared -o Adder.dll Adder.o Adder_stub.h StartEnd.o
6) Now open the Excel and use this macro (use first HsStart before Adder). Remember to link to your own folder:
Private Declare PtrSafe Function Adder Lib "C:\Users\User\haskell\Adder.dll" Alias "adder" _
(ByVal x As Long, ByVal y As Long) As Long
Private Declare PtrSafe Sub HsStart Lib "C:\Users\User\haskell\Adder.dll" ()
Private Declare PtrSafe Sub HsEnd Lib "C:\Users\User\haskell\Adder.dll" ()
Private Sub Document_Close()
HsEnd
End Sub
Private Sub Document_Open()
HsStart
End Sub
Public Sub Test()
MsgBox "12 + 5 = " & Adder(12, 5)
End Sub
7) Be amazed!
I have compiled a DLL from a C++ Library I have written according to this tutorial.
The dumpbin for the dll is as follows:
Section contains the following exports for HelloDLL.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
6 number of functions
6 number of names
ordinal hint RVA name
1 0 00011032 ?Add#Functions#MathLibrary##SANNN#Z = #ILT+45(?Add#Functions#MathLibrary##SANNN#Z)
2 1 00011037 ?AddMultiply#Functions#MathLibrary##SANNN#Z = #ILT+50(?AddMultiply#Functions#MathLibrary##SANNN#Z)
3 2 000112EE ?Multiply#Functions#MathLibrary##SANNN#Z = #ILT+745(?Multiply#Functions#MathLibrary##SANNN#Z)
4 3 000110F5 Add = #ILT+240(_Add)
5 4 00011073 AddMultiply = #ILT+110(_AddMultiply)
6 5 0001105F Multiply = #ILT+90(_Multiply)
Now I want to use the Functions in an Excel-VBA Project like this:
Declare Function LIB_AddMultiply Lib "C:\Users\xxxx\source\repos\HelloDLL\Debug\HelloDLL.dll" Alias "AddMultiply" (ByVal a As Double, ByVal b As Double) As Double
Public Sub test()
Dim a As Double
Dim b As Double
a = 3
b = 4
Dim c As Double
c = LIB_AddMultiply(a, b)
MsgBox ("hi " + c)
End Sub
But whenever I want to run the test() I get a Bad DLL Calling convention; Error 49.
I already looked at the following (and some other) resources, but couldn't resolve my problem:
Runtime Error 49, Bad DLL calling convention
Error 49
Declare Statement MSDN
Do you have any advice?
Thanks a lot...
UPDATE:
This is the code for the header file:
#pragma once
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllexport)
#endif
namespace MathLibrary
{
class Functions
{
public:
static MATHLIBRARY_API double Add(double a, double b);
//[...]
};
extern "C" MATHLIBRARY_API double Add(double a, double b)
{
return MathLibrary::Functions::Add(a, b);
}
//[...]
}
Thank you Hans Passant for your help;
I have changed the project properties to the calling convention;
then did another dumpbin with the result of my Functions being named as
_Add#16
and then just changed the Alias in the VBA code...
I'd like to build the 32-bit DLL with 64-bit GHC. And here is the minimal example.
Test.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Test where
import Foreign.C.Types
foreign export ccall c_hello :: IO()
foreign export ccall boo :: CInt
c_hello :: IO()
c_hello = do
print "Hello!"
init_exit.cpp
#include "Test_stub.h"
#include <C:\Program Files\Haskell Platform\8.0.1\lib\include\Rts.h>
#define DLLExport extern "C" __declspec(dllexport)
DLLExport void hello()
{
c_hello();
}
DLLExport int HsStart()
{
int argc = 1;
char* argv[] = {"ghcDLL", NULL};
char** args = argv;
hs_init(&argc, &args);
printf("Haskell library has been initialized!\n");
return 0;
}
DLLExport int HsEnd()
{
hs_exit();
printf("Haskell library has been finalized!\n");
return 0;
}
And then I build the library, using the following commands:
ghc -c -O Test.hs
ghc -c init_exit.cpp
ghc -shared -o Test.dll Test.o init_exit.o
What flags should I pass to ghc or maybe to gcc to build the 32-bit DLL instead of 64-bit? Or maybe there is another way to do this.
A normal Windows 64-bit GHC build (such as the one you can download from the GHC website) is only capable of building 64-bit object files. For example, it doesn't include 32-bit versions of any of the libraries that come with GHC.
Your options are to build a Windows 64-bit to Windows 32-bit cross-compiler, or just run the normal Windows 32-bit build of GHC (probably much easier).
I have the following code in a .dll:
namespace MyNamespace
{
extern "C" __declspec(dllexport) int __stdcall GetOptionID(unsigned long num)
{
return 0;
}
}
This is compiled on Visual C++ 2010, so I also have a .def file containing GetOptionID. I can see that the function is exported, and mangled as _GetOptionID#4, using dumpbin /exports:
File Type: DLL
Section contains the following exports for MyLibrary.dll
00000000 characteristics
53D269CB time date stamp Fri Jul 25 15:29:31 2014
0.00 version
1 ordinal base
13 number of functions
13 number of names
ordinal hint RVA name
1 0 0006F030 CmdOne = _CmdOne#16
2 1 0006F510 CmdUnimpl = _CmdUnimpl#16
3 2 0006EBB0 DefineThing = _DefineThing#32
4 3 0006E0C0 GetOptionID = _GetOptionID#4
In a separate executable, I attempt to check for the presence of GetOptionID:
HINSTANCE hinst = LoadLibraryEx(file_name, NULL, DONT_RESOLVE_DLL_REFERENCES);
if(!hinst)
return FALSE;
FARPROC_IDI lp = (FARPROC_IDI) GetProcAddress(hinst, "_GetOptionID#4");
auto e = GetLastError();
Running through this code in the debugger, I can see that:
LoadLibraryEx succeeds - I have a valid-looking hinst
GetProcAddress fails - lp is 0x00000000
GetLastError returns 127
I can see the function has been exported, and I can see its name matches the entry point I'm looking for. How come GetProcAddress is failing?
Ah, solved it myself. Defining the function in the .def file causes its name to be completely unmangled, meaning the correct target for GetProcAddress was simply GetOptionID.
However, since I have other .dlls that undergo the same check and really are _GetOptionID#4, the actual solution was to remove GetOptionID from the .def file.
I am working through a sample program that uses both C++ source code as well as CUDA. This is the essential content from my four source files.
matrixmul.cu (main CUDA source code):
#include <stdlib.h>
#include <cutil.h>
#include "assist.h"
#include "matrixmul.h"
int main (int argc, char ** argv)
{
...
computeGold(reference, hostM, hostN, Mh, Mw, Nw); //reference to .cpp file
...
}
matrixmul_gold.cpp (C++ source code, single function, no main method):
void computeGold(float * P, const float * M, const float * N, int Mh, int Mw, int Nw)
{
...
}
matrixmul.h (header for matrixmul_gold.cpp file)
#ifndef matrixmul_h
#define matrixmul_h
extern "C"
void computeGold(float * P, const float * M, const float * N, int Mh, int Mw, int Nw);
#endif
assist.h (helper functions)
I am trying to compile and link these files so that they, well, work. So far I can get matrixmul_gold.cpp compiled using:
g++ -c matrixmul_gold.cpp
And I can compile the CUDA source code with out errors using:
nvcc -I/home/sbu/NVIDIA_GPU_Computing_SDK/C/common/inc -L/home/sbu/NVIDIA_GPU_Computing_SDK/C/lib matrixmul.cu -c -lcutil_x86_64
But I just end up with two .O files. I've tried a lot of different ways to link the two .O files but so far it's a no-go. What's the proper approach?
UPDATE: As requested, here is the output of:
nm matrixmul_gold.o matrixmul.o | grep computeGold
nm: 'matrixmul.o': No such file
0000000000000000 T _Z11computeGoldPfPKfS1_iii
I think the 'matrixmul.o' missing error is because I am not actually getting a successful compile when running the suggested compile command:
nvcc -I/home/sbu/NVIDIA_GPU_Computing_SDK/C/common/inc -L/home/sbu/NVIDIA_GPU_Computing_SDK/C/lib -o matrixmul matrixmul.cu matrixmul_gold.o -lcutil_x86_64
UPDATE 2: I was missing an extern "C" from the beginning of matrixmul_gold.cpp. I added that and the suggested compilation command works great. Thank you!
Conventionally you would use whichever compiler you are using to compile the code containing the main subroutine to link the application. In this case you have the main in the .cu, so use nvcc to do the linking. Something like this:
$ g++ -c matrixmul_gold.cpp
$ nvcc -I/home/sbu/NVIDIA_GPU_Computing_SDK/C/common/inc \
-L/home/sbu/NVIDIA_GPU_Computing_SDK/C/lib \
-o matrixmul matrixmul.cu matrixmul_gold.o -lcutil_x86_64
This will link an executable binary called matrimul from matrixmul.cu, matrixmul_gold.o and the cutil library (implicitly nvcc will link the CUDA runtime library and CUDA driver library as well).