![]() |
Stringübergabe an DLL
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:
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.
// 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; Gibt es andere Möglichkeiten ohne dass ich einen festen Bereich reserviere Grüße Gerd |
AW: Stringübergabe an DLL
Zitat:
|
AW: Stringübergabe an DLL
Zitat:
Zitat:
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 ![]() 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
Delphi-Quellcode:
Interfaces, welches z.b. eine Methode
IString
Delphi-Quellcode:
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.
SetString(const S: String)
|
AW: Stringübergabe an DLL
WideString
Im Gegensatz zu String/AnsiString/UnicodeString, welche vom Delphi-Memory-Manager verwaltet werden, wird er von der OleAuth32.dll verwaltet. Intern wird ![]() ![]() ![]() ![]() 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. |
AW: Stringübergabe an DLL
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:
Was mache ich falsch? Ich verwende Delphi XE
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; |
AW: Stringübergabe an DLL
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.
|
AW: Stringübergabe an DLL
Man beachte die Entstehungszeit meiner Codes. Damals war Unicode bei Delphi noch in weiter Ferne.
|
AW: Stringübergabe an DLL
Zitat:
Zitat:
Zitat:
|
AW: Stringübergabe an DLL
Zitat:
Delphi-Quellcode:
Diese Lösung gefällt mir super. Ich könnte auch direkt
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;
Delphi-Quellcode:
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.
Result := SetCommandW(sCommand, iPrio);
Ist das korrekt? Zitat:
Delphi-Quellcode:
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?
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; Wäre froh wenn ich die Bestätigung bekäme dass beide Vorgehen ab Delphi XE korrekt sind Danke Gerd |
AW: Stringübergabe an DLL
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:57 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz