AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Stringübergabe an DLL

Ein Thema von norwegen60 · begonnen am 30. Nov 2017 · letzter Beitrag vom 2. Dez 2017
Antwort Antwort
Seite 1 von 2  1 2      
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#1

Stringübergabe an DLL

  Alt 30. Nov 2017, 23:45
Hallo,

ich habe mich mal näher damit beschäftigt wie man Strings sauber an eine DLL und wieder zurück gibt und habe festgestellt, dass ich damit wohl zu sorglos umgegangen bin wenn ich sie einfach nur als pChar deklariert habe und dann auf beiden Seiten geich wieder in Strings gewandelt habe.
In einem Artikel von Michale Puff, aber auch an anderen Orten fand ich die Lösung, dass man besser zuerst die Länge ermittelt, dann den Speicher reservier, den Wert abholt und den Speicher wieder freigibt. In etwa so
Delphi-Quellcode:
  // Funktion aufrufen, um Größe des Buffers zu ermitteln
  len := func1('bar', nil, 0);
  Str(len, s);
  Writeln('len: ' + s);
  try
    // Speicher anfordern
    GetMem(Buffer, len + 1);
    // Funktion mit Buffer aufrufen
    len := func1('bar', Buffer, len + 1);
    Str(len, s);
    writeln(String(Buffer)+ ' [' + s + ']');
  finally
    // Speicher wieder freigeben
    FreeMem(Buffer);
  end;
Damit habe ich aber ein Problem wenn ich damit eine Funktion ausführe und von der etwas zurückgeliefert bekomme. Die würde dann ja zwei mal ausgeführt werden. Erst nach dem Ausführen weiß ich die Länge der Antwort. Jetzt könnte ich das Ergebnis in einer globalen Variablen in der DLL zwischenspeichern und beim zweiten Aufruf nur noch den Wert abholen ohne die Funktion auszuführen. Wirklich elegant finde ich das aber nicht.

Gibt es andere Möglichkeiten ohne dass ich einen festen Bereich reserviere

Grüße
Gerd
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Stringübergabe an DLL

  Alt 30. Nov 2017, 23:55
Erst nach dem Ausführen weiß ich die Länge der Antwort.
Kleiner Hinweis am Rande: String-Operationen mit der Windows-API funktionieren in der Regel auch so. Zuerst gibt man der Routine einen Speicherbereich der Länge Null und bekommt außer dem (erwarteten) Fehler die Angabe, wie viel Speicher man bitte reservieren soll. Das tut man, und ruft die Funktion ein weiteres mal auf, diesmal mit einem Puffer der so groß ist wie gewünscht. Die Methode befüllt den Puffer und fertig.
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 02:43
ich habe mich mal näher damit beschäftigt wie man Strings sauber an eine DLL und wieder zurück gibt und habe festgestellt, dass ich damit wohl zu sorglos umgegangen bin wenn ich sie einfach nur als pChar deklariert habe und dann auf beiden Seiten geich wieder in Strings gewandelt habe
Um den String in eine Funktion reinzugeben, ist das absolut unproblematisch, da der Speicher ja erst dann freigegeben wird, nachdem der Programmfluss die Funktion wieder verlassen hat. Beim Rausgeben von Strings aus einer Funktion an den Aufrufer funktioniert das natürlich nicht.

Gibt es andere Möglichkeiten ohne dass ich einen festen Bereich reserviere
Nicht wirklich. Wenn du dieses Modell implementieren willst, dann würde ich für den ersten Aufruf jeweils schon einen Buffer "auf gut Glück" reservieren. Gegebenenfalls reicht Dieser dann aus und du sparst dir den zweiten Aufruf. Schön ist dieses Design allerdings generell nicht.

Die Windows API benutzt - wie schon erwähnt - auch gerne dieses Verfahren. Hier kann es aber unter Umständen auch passieren, dass sich die Daten zwischen zwei Aufrufen ändern (z.b. wenn man mit MSDN-Library durchsuchenNtQuerySystemInformation alle laufenden Prozesse oder Threads auflisten will, kann das schnell mal passieren). Hier kommt man dann sogar nicht drumrum die API in einer Schleife aufzurufen, bis der übergebene Buffer schließlich passt (die Anzahl der Durchläufe kann man natürlich ein wenig optimieren, indem man auf den initial zurückgelieferten Wert nochmal ein paar Kilo-/Byte aufaddiert).

Statt eines einfachen Aufrufs landest du somit bei einer Schleife und mindestens einer zusätzlichen Variable.

Eine Alternative, die mir spontan einfällt, wäre die Verwendung eines IString Interfaces, welches z.b. eine Methode SetString(const S: String) implementiert. Darin müsstest du dann zwar vermutlich trotzdem manuell einen Speicher erstellen und den String reinkopieren (die Referenzzählung funktioniert bei direkter Zuweisung nicht wirklich über DLL-Grenzen hinweg; es sei denn man verwendet Laufzeit Packages bzw. SharedMem), aber die Freigabe könnte automatisiert im Destructor erfolgen (der ja bei Interfaces sogar automatisch aufgerufen wird) und deine DLL-Funktion müsste auch nur einmalig ausgeführt werden.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#4

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 10:50
WideString

Im Gegensatz zu String/AnsiString/UnicodeString, welche vom Delphi-Memory-Manager verwaltet werden, wird er von der OleAuth32.dll verwaltet.
Intern wird Delphi-Referenz durchsuchenSysAllocStringLen, Delphi-Referenz durchsuchenSysReAllocStringLen, Delphi-Referenz durchsuchenSysFreeString und Delphi-Referenz durchsuchenSysStringLen verwendet, welches auch andere Programmiersprachen verstehen.


Also WideString auf beiden Seiten geht.
Genauso wie Delphi-Strings auf beiden Seiten gehen, wenn beide den selben Speichermanager nutzen (Shared Memory).
Bei Delphi muß nur aufgepasst werden, dass sich das Datenformat der Kontrollstruktur im Jahre 2009 geändert hat. (also Delphi 7 mit XE geht nicht)

Dann der ShortString, aber diesen Pascal/Delphi1-String will man nicht verwenden.
Oder die bekannten Char-Arrays, aber natürlich nur AnsiChar oder WideChar, damit das Datenformat nicht ändern kann und immer fest definiert ist.
Und dementsprechend auch PAnsiChar und PWideChar auf einseitig verwaltete Speicherbereiche.
$2B or not $2B
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#5

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 11:12
Danke für die Rückinfo. Bei den diversen Test die ich zwischenzeitlich gemacht habe, weiß ich wieder warum ich Pointern immer aus dem Weg gegangen bin.
Bei mir hat die Umsetzung des Beispiels von Michael Puff zu unregelmässigen Exceptions geführt.
Letztlich konnte ich es auf folgendes reduzieren. Komischerweise tritt hier der Exception aber konsequent auf
Delphi-Quellcode:
procedure TForm5.Button1Click(Sender: TObject);
var
  sValue : String;
begin
  sValue := 'Nur ein Test';
  TuWas(sValue);
end;

function TForm5.DoWhat(sValue):String;
var
  pBuffer: pChar;
begin
  GetMem(pBuffer, sizeof(sValue)); // length(sValue) + 1 ändert nichts
  pBuffer := pChar(sValue);
  Result := string(pBuffer); // Auch wenn das auskommentiert ist tritt Exception auf. So wäre es aber korrekt oder?
  FreeMem(pBuffer); // Creates an Access Violation
end;
Was mache ich falsch? Ich verwende Delphi XE
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.625 Beiträge
 
Delphi 12 Athens
 
#6

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 11:25
Die Bytegröße eines Strings ergibt sich aus Length(String) * SizeOf(Char). Unter Ansi ist das gleichbedeutend mit Length(String), unter Delphi-Unicode(UTF-16) ist das Length(String) * 2.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#7

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 11:32
Man beachte die Entstehungszeit meiner Codes. Damals war Unicode bei Delphi noch in weiter Ferne.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#8

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 11:40
Und da kann ich dann einfach auf beiden Seiten zuweisen und abholen ohne mich um Speicher zu kümmern?
Man beachte die Entstehungszeit meiner Codes. Damals war Unicode bei Delphi noch in weiter Ferne.
Ja schon, aber ich habe ähnliche Beispiele immer wieder gefunden. Wie macht man es denn richtig?
Die Bytegröße eines Strings ergibt sich aus Length(String) * SizeOf(Char). Unter Ansi ist das gleichbedeutend mit Length(String), unter Delphi-Unicode(UTF-16) ist das Length(String) * 2.
Egal ob ich es mit Length(String) * SizeOf(Char) oder (Length(String)+1) * SizeOf(Char)) probiere: Der Exception bleibt
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#9

AW: Stringübergabe an DLL

  Alt 1. Dez 2017, 20:10
Hier ein Aufruf der funktioniert.
Delphi-Quellcode:
function SetCommandW(wCommand: WideString; iPrio: Integer): WideString; stdcall; external 'uv.dll';

implementation

uses
  SysUtils;

function SetCommand(sCommand: string; iPrio: Integer): string;
// Wandelt die übergebenen String in WideString und wieder zurück
var
  wCommand,
  wResult :String;
begin
  wCommand := sCommand;
  wResult := SetCommandW(wCommand, iPrio);
  Result := wResult
end;
Diese Lösung gefällt mir super. Ich könnte auch direktResult := SetCommandW(sCommand, iPrio); schreiben oder? Damit könnte die Zwischen-Function ganz entfallen. Auf jeden Fall meldet der Compiler bei beiden Tests keine Warnung über riskante Stringwandlungen. Und da EXE und DLL imme mit dem gleichen Kompiler erstellt werden und auf denselben PC laufen sollten auch unterschiedliche CodePages kein Thema sein.
Ist das korrekt?

Man beachte die Entstehungszeit meiner Codes. Damals war Unicode bei Delphi noch in weiter Ferne.
Dieser Code funktioniert jetzt auch über pChar
Delphi-Quellcode:
function SetCommandP(pCommand, pResult: pChar; iLen, iPrio: Integer): Integer; stdcall; external 'uv.dll';

implementation

uses
  SysUtils;

function SetCommand(sCommand: string; iPrio: Integer): string;
// Wandelt die übergebenen String in pChar und wieder zurück
var
  iLen: Integer;
  pCommand, pBuffer: pChar;
  sResult : String;
begin
  pCommand := pChar(sCommand);
  // Speicher anfordern
  SetLength(sResult, SetCommandP(pCommand, nil, 0, 0));
  if Length(sResult) > 0 then
    SetCommandP(pCommand, pChar(sResult), 0, 0);
  Result := sResult;
end;
Wenn ich an Zeiger denke, bin ich immer unsicher, wann ich Speicher freigeben muss. In den Beispielen die ich fand wurde aber bei SetLength nie freigegeben. Ist in dem pChar Code alles korrekt?

Wäre froh wenn ich die Bestätigung bekäme dass beide Vorgehen ab Delphi XE korrekt sind

Danke
Gerd
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#10

AW: Stringübergabe an DLL

  Alt 2. Dez 2017, 03:24
Man beachte die Entstehungszeit meiner Codes. Damals war Unicode bei Delphi noch in weiter Ferne.
Dieser Code funktioniert jetzt auch über pChar
Ja solange man die Ansitypen explizit angibt. Nur ist String in aktuellen Delphi Versionen gleiche WideString. Hätte ich damals alles als AnsiString und AnsiChar deklariert, wäre es ohne Probleme auch unter Unicode-Delphi kompilierbar. Aber wie gesagt, damals war Unicode noch kein Thema. Soi nacgt Delphi aber aus String einen WideString und so weiter. Und dann kracht es natürlich.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:55 Uhr.
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