Export Haskell lib as DLL - excel

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!

Related

GHC compile DLL for use in VBA

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

Why linkage failed even when nm found the symbol?

When I compile a demo main.cpp, it failed because undefined reference to a_mtk_bt_service_init(), but I have found the symbol by
nm -D ./libmtk_bt_service_client.so|grep a_mtk_bt_service_init,
it's output is 0000000000004098 T a_mtk_bt_service_init,
I'm sure the compiler found the correct dynamic library by use command aarch64-poky-linux-g++ -print-file-name=libmtk_bt_service_client.so -o main main.cpp
This is the demo code main.cpp
void a_mtk_bt_service_init();
int main()
{
a_mtk_bt_service_init();
return 0;
}
and my compile command is
aarch64-poky-linux-g++ -mcpu=cortex-a72.cortex-a53+crypto -mtune=cortex-a72.cortex-a53 --sysroot=/home/sundq/code/newT9/T9-Amazon-Sdk/build/tmp/sysroots/aud8516-slc -o build/xx main.cpp -I../../include -lmtk_bt_service_client
The answer is here Call a C function from C++ code, that is, when c++ code call a c function, we also must add extern "C" before the declare of c function,
so my function declare should like this
extern "C" void a_mtk_bt_service_init();

Why does UDT / struct with missing type information cause silent launch failure in vb6?

Consider this registered type library in a DLL:
[uuid(…), version(1.0)]
library structLib
{
importlib("stdole2.tlb");
[uuid(…)]
typedef struct MyStruct
{
BSTR m_sStr;
} MyStruct;
};
In vb6 I can reference this type library and use the UDT / struct in a compiled exe (simple form with a button), named a.exe:
Private Sub Command1_Click()
Dim obj As structLib.MyStruct
obj.m_sStr = "Hello"
MsgBox obj.m_sStr
End Sub
When I remove the struct from the type library and recompile it, the previously compiled a.exe still works, even though the struct definition is no longer present. I assume this succeeds because the definition is embedded into the executable during the vb6 compilation process.
However, things work differently when I compile the following vb6 code against the unmodified type library (struct included) into a new executable, named b.exe:
Private Sub Command1_Click()
Dim obj As structLib.MyStruct
obj.m_sStr = "Hello"
MsgBox obj.m_sStr
Dim vt as Variant
vt = obj
MsgBox vt.m_sStr
End Sub
Notice the assignment of the struct to a Variant.
When I remove the struct definition from the type library once again, recompile it, and try to run the previously compiled b.exe, the program silently fails as the form won't even show up. At minimum, I expected
Starting the executable loads the form.
Clicking the button throws an error, due to assigning the struct with missing type information to the Variant.
For the record, I have tried to reproduce this behavior in C++:
structLib::MyStruct obj;
obj.m_sStr = SysAllocString(L"Hello");
MessageBox(GetActiveWindow(), obj.m_sStr, obj.m_sStr, MB_OK);
ATL::CComVariant vtRec;
ATL::CComPtr<IRecordInfo> piRecInfo;
HRESULT hr = GetRecordInfoFromGuids(__uuidof(structLib::__structLib), 1, 0, 0, __uuidof(structLib::MyStruct), &piRecInfo);
vtRec.pRecInfo = piRecInfo;
vtRec.pvRecord = &obj;
PVOID pvItem = vtRec.pvRecord;
CComVariant vtStr;
hr = piRecInfo->GetField(pvItem, L"m_sStr", &vtStr);
MessageBox(GetActiveWindow(), vtStr.bstrVal, vtStr.bstrVal, MB_OK);
Here, the C++ client runs and GetRecordInfoFromGuids() correctly returns
0x8002802b (Element not found)
when the struct definition is missing in the type library.
Is this behavior in vb6 by design? What's the cause? And is it possible to start the vb6 executable and catch error information, even when extracting type information of a referenced struct fails?
Is this behavior in vb6 by design?
I don't think so.
What's the cause?
When you assign a IDL structure to a VARIANT, the [uuid] attribute is intrinsically used. As it doesn't exist anymore, you get an exception.
That's what you do when calling GetRecordInfoFromGuids in C++, you provide explicitely the uuid of the IDL structure __uuidof(structLib::MyStruct).
And is it possible to start the executable even when extracting type
information of a referenced struct fails?
I see 2 possibilities to achieve this goal:
use late binding instead of early binding, and check the reference
handle the exception which is thrown

G++: linker doesnt seem to link correctly

I try to compile a program I have to control a DAQ device. In Windows, g++ compile and links OK, but in Linux it doesn't. The linker (called by G++) displays:
g++ -Wall -o "acelerar-30-0" "acelerar-30-0.cpp" (en el directorio: /home/poly/)
/tmp/ccRLpB4q.o: In function `main':
acelerar-30-0.cpp:(.text+0x429): undefined reference to `AdxInstantAoCtrlCreate'
collect2: ld returned 1 exit status
Ha fallado la compilación.
The cpp file is this (cut):
include stdlib.h
include stdio.h
include math.h
include "compatibility.h"
include "bdaqctrl.h"
include "comunes.h"
using namespace Automation::BDaq;
define deviceDescription L"USB-4704,BID#0"
int32 channelStart = 0;
int32 channelCount = 1;
double voltaje[0];
int32 modo;
int32 ms;
int main(int argc, char* argv[])
{
if (argc!=3)
salidaerror(argv[0],1);
channelStart = atoi(argv[1]);
ms = atoi(argv[2]);
if (channelStart<0||channelStart>1||ms<10)
salidaerror(argv[0],1);
ErrorCode ret = Success;
InstantAoCtrl * instantAoCtrl = AdxInstantAoCtrlCreate();
...
I have been several hours on this, and can't find the answer. The SDK is for Debian/Ubuntu, and it has the same code for Linux and Windows.
Any hints? Thanks
EDIT: Removed some marks as the formatting was incorrect
In my (limited) experience, typical gcc behavior will require that you specify the library containing that function as an argument on the command line like so:
-lsome_library
This is required even if the library is in your library path (additional library paths can be specified with -L). Find the appropriate library file containing that function and use its filename minus extensions and leading "lib" in the argument format above.

DMD2 (D language) how to link with a c library (libdl.so.2)

Im using Eclipse with the DDT plugin and DMD 2.06 as the compiler. When I try to to use functions like dlopen, dlsym usw I get "unresolved reference" errors, in C and GCC I fixed them by linking with -ldl, -lsdl usw... but the DMD2 compiler doesnt have options like that, is there another way to link with specific libraries?
btw I define the C functions the following way
extern(C)
{
/* From <dlfcn.h>
* See http://www.opengroup.org/onlinepubs/007908799/xsh/dlsym.html
*/
const int RTLD_NOW = 2;
void *dlopen(const(char)* file, int mode);
int dlclose(void* handle);
void *dlsym(void* handle, const(char*) name);
const(char)* dlerror();
}
would be happy about any help.
D does have link pragmas:
pragma(lib, "dl");
which will cause DMD to emit "-L-ldl" (or the system-appropriate link flag) to the linker. If the linker is order-sensitive (as ld is), you need to specify the pragmas in the order which you manually pass them.
Just pass -L-ldl.
Also, you don't need to redefine all of these. They are available in the core.sys.posix.dlfcn module.

Resources