Hi,
i have a math procedure in a DLL that uses 3 arrays (double*) as arguments (passed ByRef). This works perfectly in VBA. In LO Basic I have debugged with VS2022 and seen that only the first element of each array is the expected one. Let’s call these arrays A, B and C, normally the function shoud be invoked as f(A(0), B(0), C(0), len) , as a test I’ve tried invoking it with f(A(0), A(1), A(2), len): the debugger sees the 3 expected values, but the 3 pointers of them (double*) are definitely not consecutive. In other words, the internal representation of the array is not what C/C++ expects. Apparently there’s no way to pass/receive arrays from DLLs: data are scattered in memory. I’ve tried also invoking with f(A, B, C, len), hoping to pass double** (array of pointers of double), but i got an error in Basic.
The search of the subject gave no clues. Is there any chance to pass arrays to DLL in Basic?
Thanks
marshalArray
(x64, x32) does the conversion of Basic arrays to the C calling convention representation. The resulting data is stored into a raw memory (represented in the function by the by-ref blob argument).
Possibly this would allow you to debug the LibreOffice (soffice) process and find out where the problem is.
Hi Mike,
first I’ve checked I’m working with L.O. 24.2.4.2(X86_64), so I assume 64 byte code. My DLL is also compiled for X64. I’ve tryed passing also Single, Long and Byte arrays getting the same issue (array elements scattered in memory), so it is not related to Double.
If i’ve well understood, the marshalArray is the piece of code that should attend the conversion before passing the control to the external DLL. Probably some unmarshal code for the opposite direction.
I’m able to attach the debugger to the process but this gives me control on DLL code only. I do not know the tecnique to debug soffice as well.
Anyway i have a logic question: how Basic knows that I’m passing a full array instead of a simple variable? Looking at what DLL is seeing in the memory: marshalArray is not invoked, in other words a single variable is passed. Is there some syntax I’m missing in Basic declaration?
Declare Function GetDiag Lib “C:_SW\Visual Studio … .dll” _
Alias “GetDiag” (ByVal varName As String, _
ByRef A As Double, _
ByVal len As Long, _
ByVal FunctionName As String, _
ByRef B As Double, _
ByRef C As Double) As Long
Thanks
You need to build LibreOffice in debug mode.
Of course. You use this syntax:
Declare Function GetDiag Lib “C:_SW\Visual Studio … .dll” _
Alias “GetDiag” (ByVal varName As String, _
ByRef A() As Double, _
ByVal len As Long, _
ByVal FunctionName As String, _
ByRef B As Double, _
ByRef C As Double) As Long
… just guessing here: Have you tried passing a CreateUnoValue("[]double", your array)?
Hi all,
- I’ve modified the syntax of the the DLL function declaration, replacing “A” with “A()” and so for B and C. No change. Still, only element 0 is passed. Also, any alternatives to A(0), B(0), C(0) in the calling syntax gives Basic compiling/runtime errors.
- I’ve tried with CreateUnoValue(). Even if it generates an object that cannot be managed inside the Basic code (I cannot initialize it for instance), I tried just to see if it could be passed. No way to pass it with any kind of type through the DLL call.
- I have gone through the files in “xref: /core/basic/source/runtime/”, just in case some comments could suggest something. Unfortunately very few comments. I have found some Array related stuff it might be useful for debugging. Anyway creating the environment for compiling LO doesn’t seem to be a walk in the park (it’s not just VS2022), so debugging the issue within LO is currently beyond my goals.
- I try to see if i can open a bug. Thanks so much for your support.
Just in case - try if ByRef
/ByVal
makes a difference. I seem to recall something unintuitive around these.
This may be completely unrelated, but…
I only created 1 (one) dll. That was in 2020 and compiled from a very short (1438 Bytes) Pascal (FreePascal/Lazarus) source. The .dll
itself, however, was 231 KiByte.
In LibreOffice Basic I used it only for one purpose and declared 2 (two) functions based on it to do so. The data transfer between Basic and the .dll
used arrays. Everything worked (and still works under V24.2.4) as expected.
All that was “just for better understanding”, not in pursuit of an actual need.
I since forgot most of what I once knew about Pascal, but if you are interested you can get the source, the .dll
and the relevant example .ods
applying it.
This may be even more unrelated, but…
The Basic objects declared as Collection accept arrays as items, but return only the first element. There is a reliable workaround.
Hi Mike, yes, forgot to mention. I’ve tried with ByVal … just in case … but as expected the array pointer in the DLL is initialized with A(0) value, therefore pointing to a non-existing memory area.
Thanks
Hi Lupp,
can you please provide the link to your basic example and pascal code? My pascal usage is dated 40 years ago but i guess I should be able to translate in C++.
Thanks
When I investigated the old files I found more versions than I had expected. I decided to provide the oldest tested version and reworked the example .ods
slightly because it contained local paths.
Read READ_FIRST.txt
first.
The files are contained in a zip file now (using 7zip), but the archive needed to be renamed for being accepted by the upload rules of this forum.
The used Pascal
variant is FreePascal
with Lazarus
as editor.
asRequested.zip.fake.odg (162.1 KB)
Hi Wolfgang,
thanks so much!! I could finally understand the problem thanks to your example.
Once I could see your .ods and .dll works perfectly, i wrote a copy of your dll in C++ and it worked too.
At the point i’ve reduced step by step the deltas and discovered what the trick is:
In the declaration of the functon or sub linked to a DLL, for every argument intended to be an array: NO TYPE MUST BE SPECIFIED. Specifying a type, Basic requires to pass a scalar, not an array (not clear why). For instance:
…
Declare Sub Test Lib “…DllPath…” Alias “Test” ( A as Double) ’ using A() does not solve the issue)
…
dim A(n) as Double
Test(A) ==> you got an error from Basic, if you follow the same array convention as VBA ( i.e. TestA(0) to pass the full array) you do not get the Basic error but you pass a scalar.
The right syntax is:
…
Declare Sub Test Lib “…DllPath…” Alias “Test” ( A ) ’ A() works as well
…
dim A(n) as Double
Test(A) ==> you do not get any error from Basic and pass the full array.
Thanks again.
Issue closed
I dont think this is correct. Fact ist that arrays as parameters are generally not typed in StarBasic. There also is no array type you can assign to a function result.
The following fancy code will actually work:
Sub Main()
Dim someStrange As Variant
someStrange = Array(1, "aWord", Array("$",Pi()))
Redim Preserve someStrange(1 To 5)
someStrange(5) = Array(Array(Array("%$")))
End Sub
All the elements are typed Variant
ang get assigned their “working types” automatically.
If we call this a “concept” it actually allows for high flexibility and can be useful in special cases. It is, however not comptaible with “strict typing” even if “strict” can be reduced to SimpleValue
where SimpleValue
still is a kind of variant.
Truly strict typing (as required for Pascal e.g.) would thwait the usage of arrays for the contents of SheetCellRange
s.
If you mean that is not correct that a strictly-typed array declaration requires to pass a scalar, i agree with you. I’ve got a workaround, not a rule. This seems a sort of bug or undefined syntax side effect. With “A() as Double” or “A as Double” you are not able to pass ‘A’ but only A(n), a scalar. If you instead declare “A” or “A as Variant” you can pass the array A of doubles or whatever. There’s no logic. I understand the concept that Basic is weakly typed, but here it seems more spiteful than weak.
Thanks