PDA

View Full Version : Logging a DLL call that returns a string causes ThinBASIC to crash



xLeaves
25-05-2022, 07:57
I often implement DLLs to extend the functionality of thinBasic. These DLLs were originally written using FreeBasic, and I later started using TCC to implement my DLLs.

I find that when exchanging data between thinBasic and DLLs implemented in other programming languages, strings are always difficult to use as return values, unless my return value type is BSTR.

So I often needed wrappers such as


Declare Function Process_ListChild_Ori Lib "xLib.dll" Alias "Process_ListChild" (ByVal pHdr As Long) As ASCIIZ Ptr

Function Process_ListChild(ByVal pHandle As Long) As String
Dim sPtr As Long = Process_ListChild_Ori(pHandle)
Dim sRet As String = Peek$(ASCIIZ, sPtr)
Return sRet
End Function

Then I got used to using BSTR, but found that my scripts often crashed and were very random. After a few days of troubleshooting, I came across this passage in a search engine

BSTR is always free by the caller, the called party does not need to free the BSTR.

It seems that ThinBASIC is the same way, so I removed the code for GC to reclaim BSTR memory, and now they work fine and run for a long time without crashing, sharing it to avoid people who have the same problem as me to suffer difficulties.

Translated with www.DeepL.com/Translator (free version)

Petr Schreiber
27-05-2022, 19:51
Hi xLeaves,

thanks a lot for sharing the solution for the problem!

I would say exchanging text between languages is an challenge in general. For example - std::string in C++ is even more interesting, as the place of the data depends on the string length (if interested, see second reply here (https://stackoverflow.com/questions/42049778/where-is-a-stdstring-allocated-in-memory)).

ThinBASIC has an ideal symbiosis with PowerBASIC for STRING data type, as it is the same internally.

You correctly picked ASCIIZ to change the data between FreeBASIC and thinBASIC. The same would apply for example for pure C and thinBasic.

If I may note one thing - your approach is perfectly safe, yet may pose a performance issue.

You basically perform extra allocation:
- your DLL allocates the memory for result - text allocated at memory location 1
- thinBasic duplicates the memory of result - text allocated at memory location 2
- you return the duplicated value and possibly assign it to some variable - text allocated at memory location 3

In many cases it is okay, in other cases you could run into speed issues:
- if the amount of data is large
- especially if you would perform such operation in a loop


If you can use ASCIIZ directly, without conversion, and if you see ahead the maximum possible size for the string the returned pointer points to, you can shorten your code to:


Dim azMyResult As asciiz * 512 at Process_ListChild_Ori(pHandle) ' Note I expect the string from freebasic to not be larger than 512 bytes, feel free to change that number

' Now azMyResult can be used to further print the value, but assigning the data from FreeBasci to azMyResult did not create a new allocation, azMyResult maps over the original memory


Alternatively, if you are okay with the copy, your code can also use the PeekZ$:


Function Process_ListChild(ByVal pHandle As Long) As String
Return PeekZ$(Process_ListChild_Ori(pHandle)) ' Note PEEKZ$, so you don't have to specify ASCIIZ
End Function




P

xLeaves
28-05-2022, 05:38
Thank you Petr Schreiber, these methods you shared were very useful for me.

ErosOlmi
06-06-2022, 20:44
Depending on what is the programming language you use for your DLL, you can pass a thinBasic string BYREF to your DLL function, manage the string as a BSTR and the rest will be done by thinBasic.
Your external function will just return a status code while the string will be changed by your function
When you return from external function the string will be visible by thinBasic script with the chages you performed inside external function
Allocation and deallocation will be performed by thinBasic automatically

In this way you do not have to convert anything, that is a big CPU consuming.

Just a another idea to test.

Ciao
Eros




Declare Function Process_ListChild_Ori Lib "xLib.dll" Alias "Process_ListChild" (byref thinBasicString as string) As Long


Function MythinBasicFunction(...) As String
dim MyString as string
dim lReturnCode as long

'---MyString is allocated by thinBasic
'---Pass it to external function
'---Change MyString in external function
lReturnCode = Process_ListChild_Ori(MyString)



'---Here thinBasic will de-allocate MyString
End Function