Writing & Using DLL in BMax NG

Started by _PJ_, August 26, 2024, 11:14:28

Previous topic - Next topic

_PJ_

So I wanted to look into creating and using DLL libs with BlitzMax NG

I always only ever use x64 architecture and win32 platform - so any other platforms or architectures are going to be an issue.

I have the latest NG release (( I am semi-confident that it is correctly installed. At least, the IDE shows bcc release version 0.144 and running "Rebuild All Modules" succeeds without any errors.

______________________________________________

I found the documentation  here:
https://blitzmax.org/docs/en/language/creating_dlls/

and created the following in BMax
Function mydlltest:Int() Export
DebugLog "dll function"
Return 1
End Function

I compiled this with the "Build Shared Library" option, and it succeeded in creating a DLL file

__________


Now to test if I can use the functionality within the DLL from a blitzmax program, I then created a very simple bmx test:

Local RetValue:Int = mydlltest()
Print RetValue

Though, this of course failss because mydlltest is not recognised.

I have tried to use Import and Extern to tell BMax to use the DLL but to no avail. What is the correct way to access/expose the DLL functions?

___

Also, there is mention of requiring
InitBRL()

This command does not exist according to BMax IDE. Should some other mod be imported?




Derron

I just wrote some sample stuff ... for 32bit builds it needs some adjustments (32bit builds surely require some stuff regarding "parameters" and the .def files it creates ...)

So the following are 64bit builds - the dll as "shared library", the sample (user) as "console application".

sample_dll.bmx
SuperStrict
Framework Brl.StandardIO

Global versionPtr:Byte Ptr = "Sample DLL. Version 1.0.".ToCString()


'Simple paramless function
Function Test:Int() Export "Win32"
InitDLL()

Print "TestMe() called."
Return 1
End Function


Function TestPassedString:Int(cString:Byte Ptr) Export "Win32"
InitDLL()

Print "TestPassedString(~q"+String.FromCString(cString)+"~q)"
Return 1
End Function



'Delivers a String with DLL name And version info.
Function GetVersionText:Byte Ptr() Export "Win32"
InitDLL()

Return versionPtr
End Function



' =============================
' === DLL STARTUP FUNCTIONS ===
' =============================
' ensures to call InitBRL() at least _once_
' so Garbage Collector knows about stuff in the DLL
Global dllInitDone:Int = False
Function InitDLL:Int()
If dllInitDone Then Return True

InitBRL()
dllInitDone = True
Return True
End Function


sample.bmx:
SuperStrict
Framework Brl.StandardIO

Import pub.win32
Import brl.systemDefault 'Notify()

'load the DLL
Global dllName:String = "sample_dll.dll"
Global dllHandle:Byte Ptr = LoadLibraryW(dllName)
If Not dllHandle Then Notify dllName + " not loaded properly."; End


'define functions from the DLL
Global SampleDLL_Test:Int() "Win32" = GetProcAddress(dllHandle, "Test")
Global SampleDLL_TestPassedString:Int(cString$z) "Win32" = GetProcAddress(dllHandle, "TestPassedString")
'make it "__" and create a custom function so it returns a BlitzMax String...
Global __SampleDLL_GetVersionText:Byte Ptr() "Win32" = GetProcAddress(dllHandle, "GetVersionText")


'Delivers a String with DLL name And version info.
'$z = auto convert c-string to string (including memfree etc)
Function SampleDLL_GetVersionText:String()
Return String.FromCString(__SampleDLL_GetVersionText())
End Function



Print "DLL Test:"

Print "Calling GetVersionText()"
Print "Result: " + SampleDLL_GetVersionText()

Print "Calling Test()"
Print "Result: " + SampleDLL_Test()

Print "Calling TestPassedString()"
Print "Result: " + SampleDLL_TestPassedString("Hello World")



Output:
[100%] Linking:sample.exe
Executing:sample.exe
DLL Test:
Calling GetVersionText()
Result: Sample DLL. Version 1.0.
Calling Test()
TestMe() called.
Result: 1
Calling TestPassedString()
TestPassedString("Hello World")
Result: 1

Process complete


The important piece inside the DLL is to call "InitBRL()" as you else do not get things garbage collected inside of it. Of course this is not of importance if you use a "3rd party" DLL.

I also added the special handling of "strings" (to and from cstring ... and passing a byte ptr). For Integers and other primitives you do not need to do such stuff.

bye
Ron

_PJ_

Perfect!
Thanks Derron.
This works fine and gives me a great base to work from.