Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi DLL Verständnis frage... (https://www.delphipraxis.net/214881-dll-verstaendnis-frage.html)

DelTurbo 28. Mär 2024 13:11

DLL Verständnis frage...
 
Hi,
ich habe eine DLL die nur unter Delphi 10.4 erstellt werden kann. Strings/WideStrings in die DLL übergeben klappt alles. Allerdings gibt es eine Funktion in der DLL die Antwortet und einen langen String zurück gibt. Das bekomme ich einfach nicht hin. Nun hatte mich himitsu drauf aufmerksam gemacht das ich die Funktionen der DLL als cdecl exportiere. Nach langem überlegen werde ich die DLL wohl nur in dem einen Programm einsetzen was ich unter Delphi 2007 entwickle.
Soviel kurz zur Einleitung.


Also ich habe eine DLL die unter Delphi 10 erstellt werden muss, aber selber habe ich nur Delphi 2007. Nun habe ich mir überlegt ich schreibe das um auf stdcall. Ich dachte mir die DLL und das Programm sollte dann so aussehen.

Delphi 10:
Delphi-Quellcode:
library XDll;

uses
  SimpleShareMem,
  SysUtils,
  Classes;
{$R *.res}

procedure GetTxt(var t:WideString); stdcall;
var
  i    :Integer;
begin
    t:='B';
    for i:=0 to 3000 do begin
      t:=t+'A';
    end;
end;

exports
  GetTxt;

begin
end.
Im Programm würde es denn so aussehen.

Delphi 2007
Delphi-Quellcode:
type
  TGetTxt  = procedure (var t:WideString); stdcall;
var
  fMain    :TfMain;
  GetTxt   :TGetTxt;
  HelperHnd :THandle;

procedure TfMain.btnGetTxtClick(Sender: TObject);
var
  line      :WideString;
begin
    GetTxt(line);
end;   

procedure TfMain.FormCreate(Sender: TObject);
begin
    HelperHnd:=LoadLibrary('XDll.dll');
    if ( HelperHnd<>0 ) then begin
      @GetTxt:=GetProcAddress(HelperHnd,'GetTxt');
    end;
end;
Nun zur meiner Frage: Klappt das so? Ich habe es natürlich schon laufen lassen und es geht. Die frage die sich mir stellt ist, ob ich mir damit nirgends den Speicher zerknacke.

Vielen Dank im voraus

Der schöne Günther 28. Mär 2024 14:31

AW: DLL Verständnis frage...
 
Ich habe nur wenig Erfahrung mit DLLs, aber solange es nicht super zeitkritisch oder gigantische Datenmengen sind, weshalb macht man das nicht einfach so, wie man es z.B. aus der Windows-API auch kennt?

Dll fragen, wie viele Zeichen es sind, Speicher reservieren, Dll die Adresse des Speichers geben, Dll schreibt den String rein, fertig. Komplett sprach- und compiler-unabhängig und kennt man ja bei Interaktionen mit der Windows-API seit Jahrzehnten so.

DelTurbo 28. Mär 2024 14:38

AW: DLL Verständnis frage...
 
Hi,
so hatte ich es ja, also alles mit Pointern und das Programm hatte den Speicher geholt usw. Was nicht ging war, einen String aus der DLL zu holen. Weil ab Delphi 2009 Strings anders sind (mal platt ausgedrückt).

Der schöne Günther 28. Mär 2024 15:04

AW: DLL Verständnis frage...
 
Nein, du würdest den String dann z.B. als simples Byte-Array mit definiertem Encoding (z.B. ASCII, oder UTF-16 oder was auch immer) da reinschreiben. Das müsste man einmal festlegen, damit DLL und DLL-Benutzer alle nach den gleichen Regeln spielen.

Nicht 1:1 den den Speicher rüberkopieren, denn das ist ja spezifisch nach deiner aktuellen Delphi-Version. Sondern ein definiertes Byte-Array.

Beispiel für beide Seiten (String in DLL übertragen, und String wieder rausholen)

Delphi-Quellcode:
program Project1;

uses System.SysUtils;

var text_in_dll: String;

function createLongString(): String;
begin
   const NUMBER_OF_LINES = 10_000;
   const LINE_LENGTH = 7; // 5 for "Hello", 2 for line break
   const stringBuilder = TStringBuilder.Create(NUMBER_OF_LINES * LINE_LENGTH);
   try
      for var lineNumber := 0 to Pred(NUMBER_OF_LINES) do
         stringBuilder.AppendLine('Hello');
      Result := stringBuilder.ToString();
   finally
      stringBuilder.Destroy();
   end;
end;

// accepts UTF-8 encoded strings
procedure acceptString(const numberOfBytes: NativeInt; const firstByte: PByte); stdcall;
begin
   var bytes: TBytes;
   SetLength(bytes, numberOfBytes);
   System.Move(firstByte^, bytes[0], numberOfBytes);

   text_in_dll := TEncoding.UTF8.GetString(bytes);
end;

// Ist der Rückgaberwert False, dann war wohl "bufferLength" zu
// schmal und sollte so groß sein, wie "bytesWritten" angibt
function getString(
   out firstByte: PByte;
   const bufferLength: NativeInt;
   out bytesWritten: NativeInt
): Boolean; stdcall;
begin
   bytesWritten := 0;
   if(text_in_dll.IsEmpty()) then
      Exit(False);

   var bytes := TEncoding.UTF8.GetBytes(text_in_dll);
   bytesWritten := Length(bytes);

   if(bufferLength < bytesWritten) then
      Exit(False);

   System.Move(bytes[0], firstByte^, bytesWritten);
   Result := True;
end;

procedure sendStringToDll();
begin
   var longString := createLongString();
   var bytes := TEncoding.UTF8.GetBytes(longString);

   acceptString( Length(bytes), Addr(bytes[0]) );
end;

procedure receiveStringFromDll();
begin
   var buffer: TBytes;
   var bufferLength: NativeInt;
   SetLength(buffer, 5_000);
   var firstByte: PByte := Addr(buffer[0]);

   if(not getString({out} firstByte, Length(buffer), {out} bufferLength)) then
      WriteLn('Buffer war zu kurz, muss ', bufferLength, ' lang sein');

   SetLength(buffer, bufferLength);
   firstByte := Addr(buffer[0]);
   getString({out} firstByte, Length(buffer), {out} bufferLength);

   const asText = TEncoding.UTF8.GetString(buffer, 0, bufferLength);
   Writeln('Text received from DLL:' + sLineBreak + asText);
end;

begin
   sendStringToDll();
   receiveStringFromDll();
end.
Ich habe das Gefühl das ginge auch besser, aber so würde ich das machen.

jaenicke 29. Mär 2024 21:18

AW: DLL Verständnis frage...
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1535080)
Ich habe nur wenig Erfahrung mit DLLs, aber solange es nicht super zeitkritisch oder gigantische Datenmengen sind, weshalb macht man das nicht einfach so, wie man es z.B. aus der Windows-API auch kennt?

Warum sollte man den Aufwand betreiben, wenn man es nicht muss? WideStrings sind etwas langsamer, aber deutlich einfacher zu nutzen.

Die Lösung aus dem ersten Post passt schon so. Ich würde den WideString nur als Rückgabewert nehmen. Wofür soll das ein var Parameter sein? Es sei denn der Rückgabewert soll z.B. den Erfolg signalisieren.

TurboMagic 30. Mär 2024 07:17

AW: DLL Verständnis frage...
 
Naja, es stellt sich halt die Frage wer den Speicher wie reserviert.
Wenn die DLL das als Rückgabewert macht, dann hat die DLL einen Pointer anzulegen der groß
genug ist das die Daten rein passen.

Nur: wann wird das freigegeben?

Wenn man es als Var Parameter macht, dann müsste denke ich der Aufrufer den Speicher reservieren,
wozu er aber wissen müsste wieviel benötigt wird.

Grüße
TurboMagic

DelTurbo 30. Mär 2024 09:16

AW: DLL Verständnis frage...
 
Zitat:

Zitat von jaenicke (Beitrag 1535107)
Die Lösung aus dem ersten Post passt schon so. Ich würde den WideString nur als Rückgabewert nehmen. Wofür soll das ein var Parameter sein? Es sei denn der Rückgabewert soll z.B. den Erfolg signalisieren.

Danke, ja das ist eigentlich eine Function die ein True/False zurückgibt. Ich habe das ob nur so einfach wie möglich gemacht, das es nicht so viel wird.

Danke @all, ich habe es auch so am Laufen. Von Win7-11.

Frohe Ostern euch allen, auch an alle hier im Forum....

Mavarik 30. Mär 2024 09:58

AW: DLL Verständnis frage...
 
Ich nutze mittleiweile of XEx DLL's von D2007 aus.
Dein Beispiel ist correct.

WideStrings werden "von Windows" verwaltet, daher machen die kein Problem.

Strings und AnsiString gehen nicht, weil die in unterschiedlichen Speichern verwaltet werden.

Native type wie byte, integer und gehen natürlich auch. Das gleiche gilt für interfaces.

Für Streams musst Du einen IStream verwenden.

Mavarik :coder:

himitsu 30. Mär 2024 11:00

AW: DLL Verständnis frage...
 
Nicht nur unterschiedlich verwaltet, was sich via ShareMem lösen liese.

Im Jahre 2009 wurde auch die Struktur der LongStrings geändert.
* früher gab es vor den Chars nur Felder/Variablen für Length und Referenzzähler
* jetzt gibt es zusätzlich noch Felder für die CodePage und die CharSize.

Neue Delphis auf alten Strings, würden also auf beim Kopieren des Strings auf nichtexistierende Felder zugreifen
und die Speicherverwaltung würde jeweils mit einem falschen Offset den internen Anfang des Strings falsch bestimmen.

Hier mal ein böses Beispiel, was für einen Aufwand man bekommt, wenn man dennoch Delphi-Strings zwischen DLLs/EXE übergeben möchte.
https://www.delphipraxis.net/213736-...-fuer-neu.html



ShareMem verbindet nur den SpeicherManager, aber dennoch haben DLL und EXE weiterhin jeweils eigene Funktionen für die Verwaltung des Strings.

Delphi 2 bis 2007 sind bezüglich dem AnsiString somit noch kompatibel, was die Verwaltungsfunktionen der Strings angeht (ShareMem vorausgesetzt)
oder eben alles ab 2009 bis "jetzt", wo AnsiString sowie UnicodeString kompatibel wären.

Ja, sogar AnsiString und UnicodeString/String sind per se untereinander kompatibel, ABER seitdem das StringChecking wieder deaktiviert wurde, stimmt das nur bedingt.

Mavarik 30. Mär 2024 12:17

AW: DLL Verständnis frage...
 
Zitat:

Zitat von himitsu (Beitrag 1535122)
Nicht nur unterschiedlich verwaltet, was sich via ShareMem lösen liese.

ShareMem mit D2007 und einer D11.3 DLL?

Seit wann soll das funktionieren?

Mavarik


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:17 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz