Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   ReportMemoryLeaksOnShutDown (https://www.delphipraxis.net/204717-reportmemoryleaksonshutdown.html)

venice2 21. Jun 2020 10:32


ReportMemoryLeaksOnShutDown
 
Ist true
Und die Meldung siehe Anhang.

Nur was soll ich damit anfangen?
Jemand eine Idee wie man die besser Analysieren kann?

EurekaLog taugt auch nichts.

Ich exportiere einen String (PAnsiChar) aus einer DLL
Die Rückgabe kann ich ja schlecht löschen.

Delphi-Quellcode:
ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
Result := ExportPtr;
ExportPtr wird mit
Delphi-Quellcode:
StrDispose(ExportPtr);

freigegeben wenn die Anwendung(DLL) beendet wird.
EurekaLog zeigt immer nur auf die Funktion die ExportPtr zurück gibt aber nicht wo und warum der Leak entsteht.

Den String kann ich nicht freigeben solange er in der Anwendung selbst verwendet wird.

dummzeuch 21. Jun 2020 10:53

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von venice2 (Beitrag 1467911)
Ich exportiere einen String (PWideChar) aus einer DLL
Die Rückgabe kann ich ja schlecht löschen.

Delphi-Quellcode:
ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
Result := ExportPtr;
ExportPtr wird mit
Delphi-Quellcode:
StrDispose(ExportPtr);
freigegeben wenn die Anwendung(DLL) beendet wird.

Welcher Datentyp ist Result? Falls das ein String ist, erzeugt die Zuweisung von ExportPtr eine Kopie und dann sollte man ExportPtr direkt danach freigeben können.

venice2 21. Jun 2020 11:07

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von dummzeuch (Beitrag 1467912)
Zitat:

Zitat von venice2 (Beitrag 1467911)
Ich exportiere einen String (PAnsiChar) aus einer DLL
Die Rückgabe kann ich ja schlecht löschen.

Delphi-Quellcode:
ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
Result := ExportPtr;
ExportPtr wird mit
Delphi-Quellcode:
StrDispose(ExportPtr);
freigegeben wenn die Anwendung(DLL) beendet wird.

Welcher Datentyp ist Result? Falls das ein String ist, erzeugt die Zuweisung von ExportPtr eine Kopie und dann sollte man ExportPtr direkt danach freigeben können.

Zitat:

Ich exportiere einen String (PAnsiChar) aus einer DLL
Kann man nicht denn dann ist Result nothing.

Delphi-Quellcode:
Result := GetExportPtr(Path, Delimiter); // PWideChar

EurekaLog springt immer in diese Zeile nur was nutzt mir das der String wird ja in dieser einen Zeile nicht verarbeitet (zugewiesen)

StrDispose(Result) oder StrDispose(ExportPtr) direkt nach Result würde ja die Rückgabe aus der DLL löschen.

Die erste Zeile ist
x.dll SysUtils AnsiStrAlloc
und dann kommt
x.dll MyExport GetExportPtr

Damit finde ich aber den Leak nicht.

dummzeuch 21. Jun 2020 11:19

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von venice2 (Beitrag 1467913)
Zitat:

Ich exportiere einen String (PWideChar) aus einer DLL
Kann man nicht denn dann ist Result nothing.

Delphi-Quellcode:
Result := GetExportPtr(Path, Delimiter); // PWideChar

Das beantwortet meine Frage irgendwie nicht: Welcher Datentyp ist Result?

venice2 21. Jun 2020 11:23

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von dummzeuch (Beitrag 1467914)
Zitat:

Zitat von venice2 (Beitrag 1467913)
Zitat:

Ich exportiere einen String (PAnsiChar) aus einer DLL
Kann man nicht denn dann ist Result nothing.

Delphi-Quellcode:
Result := GetExportPtr(Path, Delimiter); // PAnsiChar

Das beantwortet meine Frage irgendwie nicht: Welcher Datentyp ist Result?

PAnsiChar
PWideChar war falsch.

strExport ist ein String der nach Ordnern sucht diese werden dann addiert in diesen Format.
Delphi-Quellcode:
strExport := strExport + FolderFound + Delemiter;
ergibt dann

'Folder1, Folder2, Folder3' usw.

dummzeuch 21. Jun 2020 12:04

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von venice2 (Beitrag 1467915)
Zitat:

Zitat von dummzeuch (Beitrag 1467914)
Zitat:

Zitat von venice2 (Beitrag 1467913)
Zitat:

Ich exportiere einen String (PAnsiChar) aus einer DLL
Kann man nicht denn dann ist Result nothing.

Delphi-Quellcode:
Result := GetExportPtr(Path, Delimiter); // PAnsiChar

Das beantwortet meine Frage irgendwie nicht: Welcher Datentyp ist Result?

PAnsiChar
PWideChar war falsch.

strExport ist ein String der nach Ordnern sucht diese werden dann addiert in diesen Format.
Delphi-Quellcode:
strExport := strExport + FolderFound + Delemiter;
ergibt dann

'Folder1, Folder2, Folder3' usw.

Also sowas wie:

Delphi-Quellcode:
var
  strExport: string;
  FolderFound: string;
begin
  // [...]
  strExport := 'Folder1'+ Delimiter;
  FolderFound := DeineFunktionDieDieDllAufruft;
  strExport := strExport + FolderFound + Delimiter;
  // [...]
end;
Also liefert die Funktion ein PAnsiChar, welches dann mittels String-Verknüpufung weiterverwendet wird?

Dann würde ich die Funktion so umschreiben, dass sie einen String zurückliefert. Nach Zuweisung des PAnsiChars auf Result kannst Du den PAnsiChar freigeben.

venice2 21. Jun 2020 12:19

AW: ReportMemoryLeaksOnShutDown
 
Delphi-Quellcode:
function ExpFindFolders(Path: PAnsiChar; Delimiter:AnsiChar=','): PAnsiChar; stdcall;
begin

  Result := GetExportPtr(Path, Delimiter); // Hier bleibt EurekaLog stehen damit kann ich aber nichts anfangen.
end;
Delphi-Quellcode:
function GetExportPtr(Path, Delimiter): PAnsiChar;
var
  strExport: string;
begin

  // [...]
  ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
  CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
  Result := ExportPtr;
end;
Was verursacht den Leak "AnsiStrAlloc" ?

Ich kann keinen string zurückgeben die DLL kann auch mit anderen Developer Sprachen verwendet werden.
Strings zwischen DLL und Anwendung hin und her schieben ist nicht das wahre.
Es muss ein 'PAnsiChar' oder PWideChar sein.

MyRealName 21. Jun 2020 12:45

AW: ReportMemoryLeaksOnShutDown
 
Ist die EXE und die DLL von Dir ? Dann schreib Dir die Freigabe-Routinen. Oder übergib halt einen schon erstellten und genullten Speicher, wenn Du sicher sagen kannst, wieviel Speicher ausreicht und er wird einfach nur gefüllt.

Generell als Faustregel gilt, dass Speicher dort freigegeben werden sollte, wo er erstellt wird. Also sagen wir, die DLL erstellt den Speicher, dann gibt die DLL den auch frei.

venice2 21. Jun 2020 12:47

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von MyRealName (Beitrag 1467922)
Ist die EXE und die DLL von Dir ? Dann schreib Dir die Freigabe-Routinen. Oder übergib halt einen schon erstellten und genullten Speicher, wenn Du sicher sagen kannst, wieviel Speicher ausreicht und er wird einfach nur gefüllt.

Generell als Faustregel gilt, dass Speicher dort freigegeben werden sollte, wo er erstellt wird. Also sagen wir, die DLL erstellt den Speicher, dann gibt die DLL den auch frei.

Das tue ich doch.
Delphi-Quellcode:
StrDispose(ExportPtr);


Wenn die DLL beendet wird.
Sehe schon muss mich da selbst durchbeißen wie auch immer.
Danke.

Frage mich nur was das ReportMemoryLeaksOnShutDown bringen soll wenn solche Informationen alles ist was dabei herumkommt.
Ein leerer Dialog würde es dann auch tun oder keine Meldung.

MyRealName 21. Jun 2020 12:55

AW: ReportMemoryLeaksOnShutDown
 
Vielleicht solltest Du mal allen relevanten Code zeigen. Was ich hier sehe ist nur ein Schnipsel aus der DLL (vermute ich) und ein StrDispose, wo nirgends gesagt wird, wann und unter welchen Umständen etwas freigegeben wird.
Die Fehlermeldung zeigt mehrere Speicherlecks, nicht nur eins.

venice2 21. Jun 2020 12:58

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von MyRealName (Beitrag 1467925)
Vielleicht solltest Du mal allen relevanten Code zeigen. Was ich hier sehe ist nur ein Schnipsel aus der DLL (vermute ich) und ein StrDispose, wo nirgends gesagt wird, wann und unter welchen Umständen etwas freigegeben wird.
Die Fehlermeldung zeigt mehrere Speicherlecks, nicht nur eins.

Mehrere aber immer in der gleichen Funktion (angeblich) man wird nicht schlau draus weil man mit solchen Informationen einfach nichts anfangen kann.
Kann den Original Quelltext leider nicht zeigen.
Trotz allem nochmals Danke für den Versuch zu helfen.

dummzeuch 21. Jun 2020 13:07

AW: ReportMemoryLeaksOnShutDown
 
OK, also hatte ich recht und das Leak kann einfach geschlossen werden, indem man die Funktion eien String zurückliefern lässt und den PAnsiChar noch in der Funktion frei gbit.

venice2 21. Jun 2020 13:08

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von dummzeuch (Beitrag 1467928)
OK, also hatte ich recht und das Leak kann einfach geschlossen werden, indem man die Funktion eien String zurückliefern lässt und den PAnsiChar noch in der Funktion frei gbit.

Nochmal! Ich kann keinen string zurückgeben die DLL kann auch mit anderen Developer Sprachen verwendet werden.
Selbst wenn du recht hast was ich dir nicht absprechen will.

dummzeuch 21. Jun 2020 13:23

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von venice2 (Beitrag 1467929)
Zitat:

Zitat von dummzeuch (Beitrag 1467928)
OK, also hatte ich recht und das Leak kann einfach geschlossen werden, indem man die Funktion eien String zurückliefern lässt und den PAnsiChar noch in der Funktion frei gibt.

Nochmal! Ich kann keinen string zurückgeben die DLL kann auch mit anderen Developer Sprachen verwendet werden.
Selbst wenn du recht hast was ich dir nicht absprechen will.

Ich rede nicht davon, einen String aus der DLL zurückzuliefern, das kann weiterhin ein PAnsiChar sein, sondern aus der Funktion, die die DLL aufuft.

Delphi-Quellcode:
function GetExportPtr(Path, Delimiter): string;
var
  strExport: string;
begin

  // [...]
  ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
  CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
  Result := ExportPtr;
  StrDispose(ExportPtr);
end;
Probier's aus, das Leak wird weg sein.

venice2 21. Jun 2020 13:32

AW: ReportMemoryLeaksOnShutDown
 
Delphi-Quellcode:
Result := ExportPtr;
ExportPtr ist PAnsiChar und kann so nicht als String zurück gegeben werden.

gruss

dummzeuch 21. Jun 2020 13:35

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von venice2 (Beitrag 1467935)
Delphi-Quellcode:
Result := ExportPtr;
ExportPtr ist PAnsiChar und kann so nicht als String zurück gegeben werden.

Ausprobiert? Es geht.

Der Compiler erzeugt den notwendigen Code.

venice2 21. Jun 2020 13:37

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von dummzeuch (Beitrag 1467936)
Zitat:

Zitat von venice2 (Beitrag 1467935)
Delphi-Quellcode:
Result := ExportPtr;
ExportPtr ist PAnsiChar und kann so nicht als String zurück gegeben werden.

Ausprobiert? Es geht.

Der Compiler erzeugt den notwendigen Code.

[DCC Warning] W1057 Implicit string cast from 'AnsiChar' to 'string'
Und ich sehe nur Kauderwelsch nicht wirklich noch irgendetwas mit einem identifizierbaren string zu tun.

Delphi-Quellcode:
Result := string(AnsiString(ExportPtr));

dummzeuch 21. Jun 2020 14:45

AW: ReportMemoryLeaksOnShutDown
 
Stopp, ich muss mich entschuldigen, ich habe Blödsinn geschrieben, weil ich was falsch verstanden hatte.

Du hast eine DLL, die Du in Delphi schreibst und die diese Funktion exportiert:

Delphi-Quellcode:
function GetExportPtr(Path, Delimiter): PAnsiChar;
var
  strExport: string;
begin

  // [...]
  ExportPtr := AnsiStrAlloc(Length(strExport) + 1);
  CopyMemory(ExportPtr, PAnsiChar(AnsiString(strExport)), Length(strExport) + 1);
  Result := ExportPtr;
end;
Korrekt?

Und diese Funktion soll nicht nur von Delphi aus sondern auch aus anderen Programmiersprachen heraus aufgerufen werden können.

(Irgendwas stimmt da nicht, die Parameter haben keinen Typ.)

Prinzipiell ist es bei der Übergabe von Strings an DLLs so, dass man sich entscheiden muss, wer den Speicher alloziert und wer ihn frei gibt. Üblicherweise macht das der Aufrufer:

Delphi-Quellcode:
var
  Res: integer;
  Buffer: Array[0..255] of AnsiChar;
  s: AnsiString;
begin
  Res := MyDllFunction(@Buffer, SizeOf(Buffer));
  s := PAnsiChar(@Buffer);
end;
In der DLL wird dann der übergebene Speicherbereich gefüllt:

Delphi-Quellcode:
function MyDllFunction(_Buffer: PAnsiChar): integer; stdcall;
var
  s: string;
  as: AnsiString;
begin
  s := IrgendwasDasDenStringLiefert;
  as := s;
  StrCopy(PAnsiChar(as[1]), _Buffer);
  Result := Length(as);
end;
Die Alternative wäre, dass die DLL eine Funktion exportieren muss, die Speicher frei gibt. An die übergibt man dann den aus der DLL zurückgelieferten PAnsiChar zur Freigabe. Das ist aber in der Regel zu aufwändig.

MyRealName 21. Jun 2020 19:07

AW: ReportMemoryLeaksOnShutDown
 
Ich habe auch schon Windows-API Funktionen gesehen, die dann ihre Freigabe-Routine mitgeben. Man macht das im Aufruf dann so :

Code:
const
  CurrentServerHandle = 0;
  WTSEnumerateSessions : TWTSAPI32_WTSEnumerateSessions = NIL;
  WTSFreeMemory : TWTSAPI32_WTSFreeMemory = NIL;

procedure ShowSessions;
var
  Count : DWord;
  pSessionInfo : pTWtsSessionInfo;
begin
  Lib := LoadLibrary('WTSAPI32.DLL');
  @WTSEnumerateSessions := GetProcaddress(Lib, pChar('WTSEnumerateSessionsW'));
  @WTSFreeMemory := GetProcaddress(Lib, pChar('WTSFreeMemory'));
  if WTSEnumerateSessions(CurrentServerHandle, 0, 1, pSessionInfo, Count) then
  begin
    // DoSometing
  end;
  WTSFreeMemory(pSessionInfo);
end;
Wie Du siehst, Die DLL stellt dir 2 Funktionen zur Verfügung, da sie den Speicher alloziert und deswegen auch wieder freigeben muss.
Ist es eine fixe Länge kannst Du auch den schon allozierten Speicher als Parameter übergeben und auf Deiner Seite dann auch wieder freigeben (was dummzeuch ja auch schon geschrieben hatte).
Was du NICHT machen kannst ist Speicher in der DLL allozieren und beim Aufrufer in der Exe freigeben, weil das unterschiedliche MemoryManager sind.

himitsu 21. Jun 2020 19:27

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Delphi-Quellcode:
PAnsiChar(as[1])

Einen AnsiChar (ordinaler Typ) in einen PChar zu casten, wenn das nicht schief läuft.

PS: Bei Übergabe einer "echten" Konstante (nicht typisiert) an PChar, kann man den Cast weglassen, und Delphi sich den richtigen Typ aussuchen lassen.
Delphi-Quellcode:
GetProcaddress(Lib, 'WTSEnumerateSessionsW');
Wobei ich selbst keine LoadLibrary/GetProcAddress mehr verwende, sondern entweder statische Links oder die statischen über Delayed-Loading, wenn es sich um APIs handelt, die es nicht immer gibt, oder es DLLs sind, die nicht beim Start direkt geladen werden sollen.

TiGü 21. Jun 2020 22:21

AW: ReportMemoryLeaksOnShutDown
 
Ihr könntet auch mal ruhig auf die Ursprungsfrage eingehen und mitteilen, dass das setzen von ReportMemory...usw. auf True zwar ganz nett ist, aber sich zum Richtigen FastMM4 wie ein Dreirad zu einem Traktor verhält.

Venice2, nehme dir doch bitte 10 Minuten Zeit und schaue dir an, wie man den FastMM4 extern einbindet und das entstehende Log lesen lernt:
https://youtu.be/o0yZgQoV8MA

venice2 21. Jun 2020 22:25

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von TiGü (Beitrag 1467966)
Ihr könntet auch mal ruhig auf die Ursprungsfrage eingehen und mitteilen, dass das setzen von ReportMemory...usw. auf True zwar ganz nett ist, aber sich zum Richtigen FastMM4 wie ein Dreirad zu einem Traktor verhält.

Venice2, nehme dir doch bitte 10 Minuten Zeit und schaue dir an, wie man den FastMM4 extern einbindet und das entstehende Log lesen lernt:
https://youtu.be/o0yZgQoV8MA

Werde ich tun.
Danke für den Rat.

venice2 21. Jun 2020 23:28

AW: ReportMemoryLeaksOnShutDown
 
Habe mir das Video angesehen und eingerichtet.
Ändert aber nichts an der Ausgabe ausgenommen das Log ist etwas detaillierter und gibt mir genau das aus was ich schon wusste.

Delphi-Quellcode:
Result := GetExportPtr(Path, Delimiter); // Hier bleibt EurekaLog stehen damit kann ich aber nichts anfangen.


Das wird so auch in das Log geschrieben das in dieser Funktion etwas nicht freigegeben wird.
Was das ist, ist mir ebenfalls bekannt.
Ich alloziere Speicher der nicht freigegeben wird (angeblich).
Ich bekomme hier 14 Speicher Lecks weil ExportPtr 14 mal aufgerufen wird innerhalb einer schleife und die größe des Speichers anhand des Strings strExport neu zugewiesen wird.

Delphi-Quellcode:
ExportPtr := AnsiStrAlloc(Length(strExport) + 1);


ExportPtr

Seltsam ist nur das ich ihn freigebe wenn die DLL beendet wird und zwar in Destroy.

Delphi-Quellcode:
destructor TMyLib.Destroy;
begin
  StrDispose(ExportPtr);
end;
EDIT:
OK. Das ist mein Fehler.
Zitat:

Ich bekomme hier 14 Speicher Lecks weil ExportPtr 14 mal aufgerufen wird innerhalb einer schleife und die größe des Speichers anhand des Strings strExport neu zugewiesen wird.
Ich gebe den String frei.
Aber!
Wenn ich diesen jedesmal innerhalb der Schleife neu Alloziere dann Alloziere ich 14x mal neuen Speicher und der vorherige wird nicht freigegeben.

Ich habe die Zuweisung jetzt aus der Schleife herausgenommen und weise den Speicher erst zu wenn strExport sein Maximum(Length) erreicht hat.
Also einmalig direkt nach der Schleife!

Siehe da die Speicherlecks sind weg.

TiGü 22. Jun 2020 07:45

AW: ReportMemoryLeaksOnShutDown
 
Oft ist ja gar nicht kaputt, sondern man selbst das Problem! :stupid:

venice2 22. Jun 2020 08:54

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von TiGü (Beitrag 1467979)
Oft ist ja gar nicht kaputt, sondern man selbst das Problem! :stupid:

Nun ist nicht jeder so erhaben wie du.
Einer muss ja den absoluten Top Level haben wenn nicht ich dann du vielleicht.
Aber was weis ich schon.

TiGü 22. Jun 2020 10:04

AW: ReportMemoryLeaksOnShutDown
 
Na na, Humor ist, wenn man trotzdem lacht.

Das kennt doch jeder von uns.
Man debuggt sich nen Wolf, macht und tut ewig und drei Tage und am Ende ändert man nur ein, zwei, drei Zeilen und schon geht’s.
Hinterher ist man immer schlauer und wundert sich, warum man nicht gleich drauf gekommen ist.
Aber ist halt so, dass ist halt menschlich. Wichtig ist, dass man dran bleibt und nicht aufgibt.

venice2 22. Jun 2020 10:08

AW: ReportMemoryLeaksOnShutDown
 
Zitat:

Zitat von TiGü (Beitrag 1467990)
Na na, Humor ist, wenn man trotzdem lacht.

Das kennt doch jeder von uns.
Man debuggt sich nen Wolf, macht und tut ewig und drei Tage und am Ende ändert man nur ein, zwei, drei Zeilen und schon geht’s.
Hinterher ist man immer schlauer und wundert sich, warum man nicht gleich drauf gekommen ist.
Aber ist halt so, dass ist halt menschlich. Wichtig ist, dass man dran bleibt und nicht aufgibt.

Ja. Dem kann ich nur zustimmen.
Ist manchmal zum Haare raufen. Alles gut.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:27 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