0

How can I receive a string from a FPC DLL? I would like to send two pointers concat them and receive the result in another string in Delphi.

library Test;

{$mode Delphi}

uses
  Classes;

function Concat(const S1, S2: PWideChar): String; cdecl;
begin
  Result := S1 + S2;
end;

exports
  Concat name 'Concat';

begin
end.

1 Answers1

3

In Delphi, a String is a complex, structured type with many details managed for you by the compiler and RTL 'magic' that hides these details. In particular, for 'long strings' there is a reference count and a length and, depending on the Delphi version involved, possibly other information.

Any DLL cannot know the details of precisely what information is required to be returned (or may be present in) any 'string' variables (or results) that an application may require. The DLL may not even be called by a Delphi program at all, in which case the 'string' type will be quite different again.

For this reason, a DLL will usually choose to deal with strings as simple 'C'-style pointer to char types. That is, some pointer to a null terminated region of memory. The caller of the DLL must then also ensure to exchange 'string' values with the DLL accordingly.

In the case of some function returning a value, the issue is complicated by the fact that allocation of the area of memory required to hold the result must be performed by the caller, with the function in the DLL taking appropriate steps to ensure that the memory supplied is sufficient. Applying these principles in this case results in a DLL routine that might look similar to this:

function Concat(const S1, S2, DEST: PWideChar; const aMaxLen: Integer): Boolean; cdecl;
begin
  // left as exercise
end;

This is a simple implementation that returns TRUE if aMaxLen is sufficient to accommodate the concatenated result. You should also consider other behaviours of the function under a variety of conditions (eg. S1 or S2 or both are NIL, aMaxLen is too big, etc).

Whatever implementation choices are made for performing the concatenation (left as an exercise for you), the result of the function call must be to place the result in the buffer pointed to by DEST.

The caller must then also ensure that a buffer of sufficient length is provided and the correct length indicated in the call:

var
  a, b, ab: WideString;  // Or: String or UnicodeString in Delphi 2009 and later
begin
  a := 'foo';
  b := 'bar';

  // Make sure 'ab' is big enough to hold the concatenated result of a + b
  SetLength(ab, Length(a) + Length(b));

  if Concat(PWideChar(a), PWideChar(b), PWideChar(ab), Length(ab)) then
     // success:  ab == 'foobar'
  else
     // something went wrong
end;

The question has to be asked though: Why are you doing this in an FPC DLL when Delphi already handles the concatenation of strings quite comfortably ? O.o

Deltics
  • 22,162
  • 2
  • 42
  • 70
  • Uh I am afraid that using `WideString` will result in a terrible performance. Why not stick with regular `UnicodeString`? – Tom Finster Dec 07 '14 at 19:33
  • Do you want it to work? – David Heffernan Dec 07 '14 at 19:36
  • @DavidHeffernan I want it to work and be fast. `WideString` and its c++ counterpart `BSTR` are something like 1000x slower than Delphi `String`. There is a solution [here](http://stackoverflow.com/questions/7925023/need-simple-demo-call-delphi-dll-in-c) thanks to Remy. – Tom Finster Dec 07 '14 at 19:48
  • 1
    @Tom - your original code uses WideChar's so the most 'portable' example uses WideStrings. But a UnicodeString is essentially a special case of WideString and will work just as well (in Delphi versions that support it). Since you are using XE7 just replace "WideString" with "UnicodeString" (or just "String") in the example **caller** code. The FPC DLL function is not affected since it is dealing with pointers to wide chars and doesn't (and shouldn't) care whether those chars are in a buffer that is part of a Delphi WideString type or a UnicodeString (or a 'C' LPCWSTR etc). – Deltics Dec 07 '14 at 20:04
  • 1
    @Tom It doesn't matter how fast it is if it doesn't work. Also, your benchmarking is wrong. Factor of 1000 is wrong. – David Heffernan Dec 07 '14 at 20:17
  • @DavidHeffernan I've benchmarked it :) – Tom Finster Dec 07 '14 at 20:20
  • 1
    Your benchmark is no good. – David Heffernan Dec 07 '14 at 20:28
  • 2
    You would only use WideString at the module boundary. Work internally using UnicodeString and then use WideString for thee interop to take advantage of the COM shared heap. Don't ever make claims about performance without publishing benchmark. We've had a lot of experience of erroneous benchmarking. If we can't reproduce it, it didn't happen. – David Heffernan Dec 07 '14 at 20:40
  • Incidentally, when I benchmarked string performance in Delphi 2009 the absolute worst case of WideString vs ANSIString (coincidentally concatentation) was only 250%, or a factor of 2.5 x, nowhere close to the 1000% claimed for the OP's unpublished benchmark. Details of my benchmark and the results here: http://www.deltics.co.nz/blog/posts/375 – Deltics Dec 07 '14 at 23:21
  • 1
    The beginning of your answer isn't really accurate. Despite being complex Delphi's string memory layout is [documented](http://docwiki.embarcadero.com/RADStudio/XE6/en/Internal_Data_Formats#Long_String_Types) well enough. The [same](http://wiki.freepascal.org/Character_and_string_types#String) for FPC. The main problem @TomFinster will face is a two heaps managers knowing nothing about each other. – Free Consulting Dec 08 '14 at 03:40
  • Which part ? NB: `Documented` is not an antonym for `complex`. ;) Indeed complexity often *demands* documentation so arguably the necessity for/existence of documentation proves that which is documented to be complex. ;) The entirely accurate point (lest anyone be confused) is that a DLL should assume nothing - documented or otherwise - about a pointer which could point to a variety of things (complex, documented or otherwise). NB: Woth mentioning is that the documentation you reference is not correct for **all** Delphi versions (Something not made clear in that 'documentation'). – Deltics Dec 08 '14 at 03:57
  • I couldn't help but be amused by the irony of a complaint of inaccuracy which includes a reference to documentation (XE6) which still refers to UnicodeString as a "*New String Type*" some 7 (SEVEN!) releases and 5 years after that type was introduced (Delphi 2009). :) – Deltics Dec 08 '14 at 04:02
  • @Free is right though. The primary issue is that of two heaps. – David Heffernan Dec 08 '14 at 07:03
  • @deltics asker claims 1000x rather than 1000% – David Heffernan Dec 08 '14 at 07:04
  • 1
    @DavidHeffernan Bottom line it is slower. I got around that by exporting Allocators from the executable and now I use it in the DLL. – Tom Finster Dec 08 '14 at 12:51
  • You've changed your tune. It's certainly true that COM heap allocation is slower than native language allocators. It's not true that it is 1000 times slower. Performance discussions are also somewhat tricky when we don't know what else is happening. We don't know what your code is really doing. Clearly you are not concatenating strings in the real code because you would not use interop for that. It's quite plausible that the real code in the DLL was so many orders of magnitude more expensive that string allocation that the perf impact of BSTR would be negligible. – David Heffernan Dec 08 '14 at 12:55
  • @Deltics, in the first paragraph you talk about complexity of Delphi's strings, ignoring the similar issue of FPC. In the second paragraph you've extended complexity discussion on general case, which was already ruled out by OP. That was kinda misleading. – Free Consulting Dec 08 '14 at 15:57
  • @Free and David - the issue of heaps is in play, but not the most important one in this case. This issue is easily deal with by agreeing on who (caller/callee) allocates the memory. But reaching agreement on that (which I **do** address albeit without labouring the point, by making it clear that the caller should) doesn't help address the issue of determining whether a pointer is to a simple NT array of widechar, the first element in a WideString (length at neg offset) or in a UnicodeString (much more info at neg offsets) or a Delphi vs FPC flavour of same (variations in neg offset info). – Deltics Dec 08 '14 at 19:14
  • @Free in particular. I fail to see how your complaint amounts to "inaccuracy". It matters not one jot that FPC has similar complexity issues as Delphi. And these complexities are also documented (and therefore not complex? Which seemed to be your first complaint). I fail to see where you think the OP "ruled out" these complications as an issue. Complexity on **either** side demands that the interface be simplified to the lowest common denominator. Your comments about inaccuracy are misplaced and unhelpful and imho could give rise to confusion that is simply unnecessary. – Deltics Dec 08 '14 at 19:18
  • @David in particular. 'x' ... '%'... Pesky small browser font.... ahem.... :squints at screen: :) – Deltics Dec 08 '14 at 19:20
  • @Deltics, then you've managed to fail to see all my points. Sorry about that. – Free Consulting Dec 09 '14 at 00:14