How to pass dynamic array of string to a dll library (dll and client written in d7) without ShareMem Unit?
Asked Answered
B

1

8

I've read on this page that dynamic arrays need ShareMem unit to work properly.

However I would like to write a dll open for other languages.

Could anyone tell me how can I declare the function and its parameters to pass array of String?

Isn't really something like this not allowed without ShareMem?

var
  templates : array of WideString;
begin
  SetLength(templates, 2); 
  templates[0] := 'template1';
  templates[1] := 'template2';
end
DLLFunction(@templates[0]); 

Thanks for help!

Brandling answered 5/5, 2011 at 15:31 Comment(2)
@user740144, Don't forget to declare your exported functions/procs as stdcall if you want to use it the dll with non-delphi clients.Natalianatalie
@Natalianatalie You can use either stdcall or cdecl. stdcall will be somewhat standard in Windows, but cdecl could also make sense. The __fastcall convention is not so standard.Iridescence
I
6

A dynamic array of strings is already an array of PWideChar/PAnsiChar (for WideString or UnicodeString/AnsiString).

This dynamic array can be directly mapped as is, with no memory copy, from most languages, as an array of PWideChar/PAnsiChar:

From caller:

DLLFunction(length(templates),pointer(templates)); 

From dll:

type
  TPAnsiCharArray = array[0..MaxInt div SizeOf(PAnsiChar)-1] of PAnsiChar;
  PPAnsiCharArray = ^TPAnsiCharArray;
  TPWideCharArray = array[0..MaxInt div SizeOf(PWideChar)-1] of PWideChar;
  PPWideCharArray = ^TPWideCharArray;

procedure DLLFunction(argc: integer; argv: PPWideCharArray);
var i: integer;
begin
  for i := 0 to argc-1 do
    writeln(argv[i]);
end;

From a C dll for instance, you can use char **argv instead of PPAnsiCharArray and void **argv instead of PPWideCharArray.

Then you can easily convert back the PWideChar/PAnsiChar into the native string type of the language.

If you need only to write a Delphi dll, you can use

type
  TAnsiStringArray = array[0..MaxInt div SizeOf(AnsiString)-1] of AnsiString;
  PAnsiStringArray = ^TAnsiStringArray;
  TWideStringArray = array[0..MaxInt div SizeOf(WideString)-1] of WideString;
  PWideStringArray = ^TWideStringArray;

procedure DLLFunction(argc: integer; argv: PWideStringArray);
var i: integer;
begin
  for i := 0 to argc-1 do
    writeln(argv[i]);
end;

or even

DLLFunction(templates);

procedure DLLFunction(const templates: array of WideString);
var i: integer;
begin
  for i := 0 to high(templates) do
    writeln(templates[i]);
end;
Iridescence answered 5/5, 2011 at 15:57 Comment(7)
So if I understand, I can use with no worries the code from my post, and retrieve the array just like you wrote in a dll?Brandling
I can see now that my answer was for sending the data the other way. But I'm not keen on your reliance on implementation details of open arrays. That's a bit hacky in my view.Lundgren
@David, indeed. Using "@Templates[0]" would be safer (or more forward compatible) than "Pointer(Templates)", or so I've been told.Rivers
@Ken I'm not sure you were told so well, and made a confusion between dynamic pointers and strings... pointer(Templates) is exactly the same as @Templates[0] from the asm generated code point of view. Even the compiler doesn't make any difference from the type: you can use a @Templates[0] as a valid parameter for a TObject... pointer() is only more generic, and will work also for strings, whereas a @aString[1] will not be the same as pointer(aString) because there will be an hidden call to the | UniqueString low-level function.Iridescence
@A.Bouchez, both are "exactly the same [...] from the asm generated code point of view" in the current implementation. "@Templates[0]" is guaranteed to point to the first element of the array. "Pointer(Templates)" points to the first element of the array because, like David said, the "implementation details of open arrays" says so. If, for any reason, the implementation details need to change, Pointer(Templates) might end up being different than @Templates[0]. But right now, I agree, they are the same. Pointer(Templates) is just slightly more likely to break in the future.Rivers
@Ken We're both guessing. But I wonder what could be returned by a pointer(Templates)... since a dynamic array should be allocated, so it would stay a pointer. It will break IMHO only in case of a garbage collection introduction. And in this case, I guess we'll have bigger issues than pointer(Templates).Iridescence
@A.Bouchez, Of course, it would still be a pointer. The thing is, the pointer isn't forced to point to the first element of the array (It could point to the reference count, size of the memory block etc). Ok, the odds that this will change might be non existent... But what were the odds that SizeOf(Char) <> 1, 10 years ago? Pointer(Templates) assume the pointers this reference type point to the first element of the array AND that item in this array are contiguous. @Templates[0] only assume items are contiguous. For that reason, I'd say the latter is safer than the former.Rivers

© 2022 - 2024 — McMap. All rights reserved.