Use a BlitzMax NG dll in Haxe

Started by rc, November 27, 2019, 21:19:08

Previous topic - Next topic

rc

Hello Community

I'm developing in BlitzMax for over 15 years now and I am really happy to see that BlitzMax still exists. I'm a software developer and I use c# and python at work, but for hobby projects I like to use BlitzMax.

However, I was wondering if it is possible to create a dll in BMax ng and use it in a Haxe application with cpp as build target.

I have created a dlltest.dll with BlitzMax ng (Creating DLLs)
with the following code:
Function TheAnswer:Int() Export
Return 42
EndFunction


Buildsettings are:
App Options: Build Shared Library
Platform: Win32
Architecture: x64

So the  calling convention cdecl.



In Haxe i have the following code:
lib.load

class Main {

static var InitBRL:()->Int =  cpp.Lib.load("dlltest","InitBRL",0);
static var TheAnswer:()->Int = cpp.Lib.load("dlltest","TheAnswer",0);

static function main():Void {
InitBRL();
trace(TheAnswer());
}
}


With the following build parameters:
-cp src
-cpp cpp
-D analyzer-optimize
-main Main


Haxe builds the exe without eny errors. However, when i execute the exe, i get the following errors:


Error : Could not load module dlltest@InitBRL__0
Error : Could not load module dlltest@TheAnswer__0


The .def file generated by BlitzMax NG is:
[code]LIBRARY dlltest
EXPORTS
TheAnswer
InitBRL


I also looked into the c output of BMax ng:
BBINT TheAnswer(){
return 42;
}
BBINT InitBRL(){
bbLibInit();
return 0;
}


So this looks fine.

Can someone give me some advice on how i get this working?

Derron

#1
I created some DLL with NG (but all for the same project - so approach stuff similar).

Instead of exposing "initBRL" (so others need to know that they call this before everything else - to eg make the GC working). I have this function in my DLL:

Code (BlitzMax) Select

' === DLL STARTUP FUNCTIONS ===
Global dllInitDone:Int = False
Function InitDLL:Int()
If dllInitDone Then Return True

InitBRL()
dllInitDone = True
Return True
End Function


Then in each exposed function I do

Code (BlitzMax) Select

Function MyDLL_IsMeasurementDone:Int() Export
InitDLL()

Return measurementDone
End Function


Yes, a bit tedious - but this is to hide the "initBRL()" thing. Nothing you need to do if it is your DLL and you yourself are using it :)



If something is run in threads somehow, make sure that the function is registered in that thread:
Code (BlitzMax) Select

Function _MyDLL_MeasurementDoneCallback(deviceID:Int, errorCode:Int, userContext:Byte Ptr)
If Not GCThreadIsRegistered() Then GCRegisterMyThread()
[...]
End Function




Next is to build - as you did. And then maybe validate first, that the DLL works as expected by using it straight in BlitzMax (or C). Then later on you can try to make it work with Haxe.

test:
Code (BlitzMax) Select

Print "Loading DLLs"
Global dllHandle:Byte Ptr = LoadLib("mydll.dll")

Global MyDLL_IsMeasurementDone:Int() = GetProcAddress(dllHandle, "MyDLL_IsMeasurementDone")



For haxe - maybe you need to export not as _cdecl but stdcall (MVSC style)? In BlitzMax I think this is if you export with "Win32". Brucey told me multiple times to only do this if I knew what I do. -- what I don't ;D


bye
Ron

Derron

@ Win32

DLL:
Code (BlitzMax) Select

Global versionPtr:Byte Ptr = "My Super DLL v1.0".ToCString()

Function MyDLL_GetVersion:Byte Ptr() Export "Win32"
InitDLL()
Return versionPtr
End Function


Test:
Code (BlitzMax) Select

Global MyDLL_GetVersion$z() "Win32" = GetProcAddress(dllHandle, "MyDLL_GetVersion")


Print "dll version: " + MyDLL_GetVersion()



bye
Ron

rc

#3
Hello Ron

Thanks for the detailed answer.

I'm wondering where LoadLib is comming from?
Is this a build-in function from bmax-ng?

Just trying to verify the dll by loading it in bmax as you have suggested.
However i get the error:
Identifier loadlib not defined

When i try this, it works with bmax:
Global dllHandle:Byte Ptr = LoadLibraryA("dlltest.dll")

Global Test:Int() = GetProcAddress(dllHandle,"Test")

Print(Test())


Dll code:
' === DLL STARTUP FUNCTIONS ===
Global dllInitDone:Int = False
Function InitDLL:Int()
        If dllInitDone Then Return True

        InitBRL()
        dllInitDone = True
        Return True
End Function

Function Test:Int() Export
InitDLL()
Return 66
End Function


However, loading it in Haxe still results in the same error.
Error : Could not load module dlltest@Test__0

import cpp.Lib;

class Main
{
    static var Test:()->Int = Lib.load("dlltest", "Test", 0);

    static function main()
    {
        trace(Test());
    }
}

Derron

#4
loadlib - convenience function by me (to check file existence etc).

what happens if you do

Function Test:Int() "Win32" Export

adding that "Win32" will make it use another convention when exporting.


Edit: googled for "Haxe load dll" and this was one of the first hits:
http://old.haxe.org/forum/thread/756
https://stackoverflow.com/questions/42599263/how-to-use-third-part-dll-from-haxe

bye
Ron

rc

Yes I also googled. I found the second one but not the first one. Not being able to load a third  party dll directly in Haxe is really a bummer.
My intention was to wrap some basic Max2D functions like initializing the graphics context, loading and drawing images and so on, so I could use it within haxe. But that does not seem to work then.