Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

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

AW: Stringübergabe in eine Delphi Win32-DLL

  Alt 19. Aug 2012, 13:29
// EDIT:
Um hier keine unbewiesenen Behauptungen zu posten... kleines Beispiel:
Delphi-Quellcode:
function Test(const Value: string): PChar;
begin
  Result := PChar(DupeString(Value, 10));
end;

procedure TForm240.FormCreate(Sender: TObject);
var
  TestResult1, TestResult2: PChar;
begin
  TestResult1 := Test('a');
  TestResult2 := Test('b');
  ShowMessage(TestResult1);
  ShowMessage(TestResult2);
end;
Du wirst sehen, dass (zumindest unter XE) zweimal 10xb ausgegeben wird. Einfach weil die selbe Speicheradresse wiederverwendet wird bevor der erste String ausgegeben wird... Und das ist ja auch vollkommen korrekt, da der Speicherbereich schon wieder freigegeben ist. Dass darauf noch ein Pointer existiert, darf nicht passieren. Deshalb ist der Code so schlicht fehlerhaft.
Das aber auch nur, wenn der Speicher in der Zwischenzeit nicht wieder (komplett) freigegeben oder überschrieben wird.
Denn FastMM behält sich Speicher noch eine Weile, um ihn schneller wiederverwenden zu können und nicht jedesmal langwierig bei Windows beantragen zu müssen.

DupeString liefert eine Stringvariable zurück, wofür Delphi eine temporäre Variable anlegt, welche bei Funktionsende wieder freigegeben wird.
Delphi-Quellcode:
function Test(const Value: string): PChar;
var
  Temp: string;
begin
  Temp := DupeString(Value, 10);
  Result := PChar(Temp);
end; // und im FunktionsENDe steht quasi ein Temp := '';
Und es ist Zufall, wenn hier wieder die selbe PChar-Adresse zurückgegeben wird.
Nämlich nur dann, wenn FastMM zufällig den selben Speicherbereich für den Result-String von DupeString wiederverwendet. (was bei einer so einfachen Funktion und bei SingleThreadding zufällig etwas öfters passieren kann, da die Berechnung/Ermittling des nächst freien Speicherblocks ja gleich abläuft)
Tipp: Versuch es mal mit einem größeren String, wie z.B. DupeString(Value, 1000000000); , denn da ist es wahrscheinlich, daß FastMM den "großen" Speicher sofort wieder an Windows zurückgibt.

Sowas wäre eher möglich, aber das ist natürlich nicht für Multithread, also nicht für mehrere gleichzeitige Aufrufe dieser Funktion, da es nur einen StringSpeicher gibt.
Delphi-Quellcode:
var
  Test_Result: string; // den String auch noch nach dem Funktionsende erhalten

function Test(const Value: string): PChar;
begin
  Test_Result := DupeString(Value, 10);
  Result := PChar(Test_Result);
end;

procedure TForm240.FormCreate(Sender: TObject);
var
  TestResult1, TestResult2: string; // Rckgabewerte sofort in einen String umwandeln, also kopieren
begin
  TestResult1 := Test('a');
  TestResult2 := Test('b');
  ShowMessage(TestResult1);
  ShowMessage(TestResult2);
end;
Einfacher ist ShareMem und direkte Stringübergabe, oder die Verwendung von WideString.
WideString ist eine Kaspelung von String-APIs der OleAut32.dll (MSDN-Library durchsuchenSysAllocStringLen MSDN-Library durchsuchenSysStringLen MSDN-Library durchsuchenSysFreeString) und ist somit auch immer gleich und unabhängig vom Delphi-Speichermanager.

Schlecht ist es auch, wenn keine generischen Typen verwendet werden.
PChar und String können sich je nach Compiler (z.B. vor und nach ab Delphi 2009) verändern und schon verändert sich auch die Schnittstelle (PAnsiChar <> PWideChar | AnsiString <> UnicodeString), aber Schnittstellen müssen statisch/unveränderlich sein.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (19. Aug 2012 um 13:44 Uhr)
  Mit Zitat antworten Zitat