1

I´m migrating my Delphi 5 source code to Delphi 10 Berlin. I´ve got many DLLs in my project which export functions. These functions are called from other DLLs. There are two DLLs which I can not migrate to Delphi 10 but I still want to use them in my program. Here an example:

function DoSomething( aList: TStringList ): Boolean; external 'Delphi5.dll';

I want to call "DoSomething" from my Delphi 10 Project. But the problem is, that TStringList in Delphi 5 is not compatible to TStringList in Delphi 10 Berlin (unicode). It would work, when DoSomething would have a parameter like "aString: AnsiString" because AnsiString is compatible to "string" in Delphi 5.

Is there a way to send a List between these two Delphi-Versions? Perhaps a TList or something else? Of course I could send a AnsiString with a separator between the strings to simulate a list, but I want a clean solution, because I´ve got many of these export-functions.

Thanks!

Sam
  • 139
  • 1
  • 12
  • 1
    The design is majorly flawed. You should never ever ever ever pass objects across DLL boundaries. If it were up to me, I'd completely re-design this to pass the raw data. Perhaps even something along the lines of `FindFirst` / `FindNext`. – Jerry Dodge Oct 12 '16 at 17:17
  • If your programs ever worked it was only by chance. You'll need to redesign the interface. – David Heffernan Oct 12 '16 at 17:43
  • Not to mention, you're using the wrong calling convention. – Jerry Dodge Oct 12 '16 at 17:48
  • @Jerry So long as both sides use the same calling convention, it's fine – David Heffernan Oct 12 '16 at 17:54
  • The original code and design is not from me. It´s a very big project and I try to migrate it to Delphi 10. I have to live with some design errors because it would take too much time to change them. Passing objects across DLLs works fine in the project. What would be the best way to communicate between DLLs or EXE and DLL. COM? – Sam Oct 12 '16 at 21:06
  • Interface, I get it :-) – Sam Oct 12 '16 at 21:56

1 Answers1

4

One should NEVER pass an object reference from an EXE to a DLL if it is meant to be used inside the DLL, or vice versa. An object reference can safely be passed to a DLL only if all the DLL does is pass the object back to the EXE (ro vice versa), such as through a callback function.

As you experienced, an object reference is not valid if the EXE and DLL aren't compiled with the same version of Delphi. Even if they are compiled with the same version, I suspect some compiler options could make them incompatible ({$Align} comes to mind, though I have never verified it). And even then, some incompatibilities might still occur (such as "Cannot assign TStringList to TStringList" errors due to RTTI mismatches).

Something that could fix your issue with minimal changes to your code would be to change the declaration of your functions to pass an interface to the DLL, and create a wrapper around TStringList that supports that interface. Said interface would need to support all the functionality you need from TStringList.

function DoSomething( aList: IStringList ): Boolean

Interfaces can be passed between DLL/EXE without most of the problems related to the object reference (as long as they use the exact same interface definition when they are compiled). (Edit: You still need to ensure the data passed to the interface's method are safe to pass to/from a DLL.)

That said, the interface should explicitly use AnsiString use a null-terminated PAnsiChar, or even a WideString (which can safely be sent to/from DLL - Reference).

function DoSomething( aListText: PAnsiChar ): Boolean

function DoSomething( aListText: WideString ): Boolean

Do not use String, which is AnsiString in Delphi 5 but is UnicodeString is Delphi 10. And don't use AnsiString, as it is not compatible between Delphi 5 and Delphi 10 due to internal structure differences.

Community
  • 1
  • 1
Ken Bourassa
  • 6,363
  • 1
  • 19
  • 28
  • 3
    `AnsiString` in Berlin is NOT compatible with `AnsiString` in Delphi 5. Its internal structure was changed in Delphi 2009. It is not safe to pass any non-POD data type over the DLL boundary. That includes all Delphi-managed types, including `AnsiString` and `UnicodeString`, but not `WideString` (which is managed by the OS and thus safe to use). – Remy Lebeau Oct 12 '16 at 18:37
  • @RemyLebeau Good catch... I updated my answer accordingly. – Ken Bourassa Oct 12 '16 at 19:25
  • `AnsiString` is not necessarily compatible between two modules built with the same compiler. That would also require a shared memory manager. – David Heffernan Oct 12 '16 at 19:50
  • Never? What about when the DLL is a Delphi package? – MartynA Oct 12 '16 at 20:21
  • 1
    Well, I'm not sure, but I believe you could technically pass (for example) a TStringList between DLL/EXE with no problem if both the DLL and the EXE are both built using the same runtime package containing the definition of TStringList (And all other dependant packages). But I haven't worked with runtime package since I was a junior dev. We had so many problems with them that we ended up not building DLLs with them anymore. Haven't used them in over 10 years so I might have a few points wrong about this. Also, with that many restrictions on your DLL, you'd be better off building a BPL instead. – Ken Bourassa Oct 12 '16 at 20:46
  • If it´s the same IDE you can pass TStringList without problems in the same runtime package. We are doing this since years and it works without problems. But it´s not possible when using different IDEs (Delphi 5 and Delphi 10.1) – Sam Oct 12 '16 at 21:11
  • True for a package Sam but not true for other types of modules – David Heffernan Oct 12 '16 at 21:18