Speicherlecks in TIniFile? [gelöst]

Rund um die LCL und andere Komponenten
and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Speicherlecks in TIniFile? [gelöst]

Beitrag von and4more »

Hallo liebes Forum, ich hätte da mal interessehalber eine Frage. Und zwar bekomme ich über heaptrc jedesmal tonnenweise Meldungen der Art "Call trace block" inkl. haufenweise Speicheradressen wenn ich eine Ini-Datei mit TIniFile initialisiere. Wenn ich zu den am Rand stehenden Codezeilen gehe kann ich nix auffälliges entdecken. Und jedes Mal wenn ich TIniFile.Create weglasse läuft heaptrc normal durch. Also entweder mach' ich da was falsch

Code: Alles auswählen

 
IniF:=TIniFile.Create(pfad2prg+prgname);
 
oder das Problem liegt in der Unit TIniFile. Auch funktioniert das Programm korrekt und zur Laufzeit fehlerfrei (inkl. TIniFile). Aber irgendwie finde ich das schon etwas seltsam. Ist das normal? Kennt sich da jemand aus?

Liebe Grüße und danke schon mal für Beiträge
Zuletzt geändert von and4more am Di 14. Feb 2017, 17:49, insgesamt 1-mal geändert.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

Mathias
Beiträge: 7064
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Speicherlecks in TIniFile?

Beitrag von Mathias »

Wie sieht es aus, nach einem IniF.Free ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

..die Meldungen kommen ja immer zum Schluss beim beenden des Programms und da laufen die ganzen Aufräumarbeiten automatisch, also auch IniF.Free etc. Nachtrag: Automatisch heißt ich rufe alles explizit auf.

Code: Alles auswählen

 
procedure TFrmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  // Abfrage, ob Datensatz zuvor noch gespeichert werden soll
  case QuestionDlg('Vor Beenden Datensatz speichern?',strExit,mtInformation,
  [mbYes,'Ja',mbNo,'Nein',mbIgnore,'Doch nicht beenden'],0) of
    mrYes:    begin
                savedbf;  // Speichern der Daten
                CopyFile(dbfPfad+'archiv.dbf',dbfPfad+'archiv.dbf.bkp');
                CopyFile(dbfPfad+'archiv.mdx',dbfPfad+'archiv.mdx.bkp');
                iniF.WriteInteger('FrmMainPos','Left',Self.Left);
                iniF.WriteInteger('FrmMainPos','Top',Self.Top);
                iniF.WriteString('DB','dbPfad',dbfArchiv.FilePathFull);
                iniF.UpdateFile;
                InhaltListe.Free;
                dbfArchiv.Exclusive:=False;
                dbfArchiv.Close;
                dbfArchiv.Active:=False;
                dbfArchiv.Free;
                iniF.Free;
              end;
 ...
 
Zuletzt geändert von and4more am Fr 3. Feb 2017, 19:07, insgesamt 1-mal geändert.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

wp_xyz
Beiträge: 5304
Registriert: Fr 8. Apr 2011, 09:01

Re: Speicherlecks in TIniFile?

Beitrag von wp_xyz »

Hast du IniF.Free aufgerufen?

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...also für alles was so erzeugt wird rufe ich am Schluss auch .Free auf
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

wp_xyz
Beiträge: 5304
Registriert: Fr 8. Apr 2011, 09:01

Re: Speicherlecks in TIniFile?

Beitrag von wp_xyz »

and4more hat geschrieben:...also für alles was so erzeugt wird rufe ich am Schluss auch .Free auf
Offenbar nicht, denn sonst hättest du kein Speicherleck. Und an den Interna von TIniFile liegt es sicher nicht.

Der Satz am anfang "Und jedes Mal wenn ich TIniFile.Create weglasse läuft heaptrc normal durch." ist seltsam: Denn dann müsste dein Programm spätestens in FormClose abstürzen, weil du iniF.WriteInteger aufrufst - iniF existiert aber gar nicht. Hast du vielliecht iniF zweimal erzeugt?

Überhaupt: Eine Ini-Datei irgendwo zu erzeugen, in einer oder mehreren anderen Routinen zu lesen und zu beschreiben, und in einer nochmals anderen Routine freizugeben, ist sehr fehlerträchtig. Dabei ist gerade der Zugriff auf eine Ini-Datei eines der besten Beispiele dafür, wie man das kompakt in einer Methode erledigen kann, mit einem sauberen try-finally Block:

Code: Alles auswählen

function GetIniName: String;
begin
  Result := ChangeFileExt(GetAppConfigFile(true), '.ini');
end;
 
procedure TForm1.FormActivate(Sender: TObject);
begin
  ReadIni;
end;
 
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if CanClose then 
    try
      WriteIni;
    except
      // Platte voll? ini-Datei durch anderen Prozess gesperrt? Diskette nicht eingelegt?
      if MessageDlg('Fehler beim Schreiben der ini-Datei. Trotzdem beenden?', mtConfirmation, [mbYes, mbNo, mbcancel], 0) <> mrYes then
        CanClose := false;
    end;
end;
 
procedure TForm1.ReadIni;
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(GetIniname);
  try
    Left := iniF.ReadInteger('FrmMainPos', 'Left' Left);
    // etc.
  finally
    ini.Free; 
  end;
end;
 
procedure TForm1.WriteIni;
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(GetIniName);
  try
    iniF.WriteInteger('FrmMainPos', 'Left', Left);
    // etc.
  finally
    ini.Free;
  end;
end;
 

shokwave
Beiträge: 476
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie (L 3.0 FPC 3.2.2)
CPU-Target: i386, x64
Wohnort: Gera

Re: Speicherlecks in TIniFile?

Beitrag von shokwave »

and4more hat geschrieben:Nachtrag: Automatisch heißt ich rufe alles explizit auf.

Code: Alles auswählen

 
procedure TFrmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  // Abfrage, ob Datensatz zuvor noch gespeichert werden soll
  case QuestionDlg('Vor Beenden Datensatz speichern?',strExit,mtInformation,
  [mbYes,'Ja',mbNo,'Nein',mbIgnore,'Doch nicht beenden'],0) of
    mrYes:    begin
                ....
                iniF.Free;
              end;
 ...
 
Bei "Ja" räumst du auf und bei "Nein"?
mfg Ingo

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

@wp_xyz: Klar, dass ich natürlich die abhängigen Routinen auch auskommentiere, sonst würde das Programm höchstwahrscheinlich nicht übersetzen, aber Dein Codebeispiel ist natürlich echt super und schön modular. Als Anfänger hat man sowas (noch) nicht so richtig drauf, ist halt mein erstes größeres Programm. Wenn ich mir meinen Code so durchschaue wird aber IniF nur einmal in der Hauptform erzeugt und alles andere bezieht sich auf diese Instanz. Aber auch ich erkenne die Logik in Deinem Post, irgendwo muss wahrscheinlich was faul sein. Ich werde mal Deine Anregungen zur Codestrukturierung ausprobieren sieht einfach besser aus als meiner.
@shokwave: Auch bei "Nein" wird alles genauso aufgeräumt nur eben nicht die Daten gespeichert, bei "Doch nicht beenden" wird nur das Fenster geschlossen und das Programm läuft weiter, eigentlich genau wie man's vermuten würde :wink:
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

Nachtrag: @wp_xyz: hab' grad gesehen, es geht doch nicht so modular, da die INI-Datei zur Laufzeit von verschiedenen Fenstern bzgl. verschiedener Dinge abgefragt wird, wie z. B. an welcher Position das betreffende Fenster zuletzt geöffnet wurde, dann Pfad und Name der Datenbank und verschiedene andere Werte mit denen einzelne Units arbeiten, sodass Read und Write-Funktionen nicht in dieser Form modularisierbar sind. Verwenden könnte man allerdings die GetIniName Funktion, aber macht das Sinn wenn der Name der Ini-Datei feststeht und sich eh nicht ändert? Die Dateiendung läuft bei mir aus Kompatibilitätsgründen zu Linux als Variable "ext" (.ini bzw..conf mit Pragma {$MSWINDOWS bzw. UNIX)
Die Ini-Datei wird nur in der Main-Unit erzeugt und dort auch wieder freigegeben, alle anderen Units beziehen sich auf die dort erzeugte Instanz.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...also ich hab' jetzt auf Eure Hinweise hin nochmal den gesamten Code überprüft und wirklich alle Verweise auf TIniFile.Create auskommentiert und habe das Programm nochmal kompiliert, mit und ohne .Free am Ende und immer meldet sich heaptrc mit seitenweise "Call trace block...". Erst wenn ich IniF:=TIniFile.Create auskommentiere läuft heaptrc sauber durch. Irgendwie finde ich das seltsam. Auf den Hinweis mit Codebeispiel von wp_xyz stellt sich mir die Frage, ob es ein Fehler ist nur eine Instanz von TIniFile (-> public) zu erzeugen und von anderen Units darauf zuzugreifen oder muss/sollte man für jede Funktion mit Zugriff auf die Ini-Datei eine neue Instanz erzeugen und diese dann wieder mit .Free freigeben. Ich hab es deshalb bisher nicht so gemacht, weil es ja jedesmal ein Funktionsaufruf ist der Rechenzeit verbraucht. Aus diesem Grund arbeite ich auch mit Variablen und rufe die entsprechenden Funktionen ("Pfad", "Dateiname(n)") nur beim Create-Prozess der Hauptform auf. Das Programm läuft so ab, dass beim Start/Create der Hauptform Dateipfad und -name von Ini-Datei und Datenbank in einer Variablen abgelegt werden, ebenso wird eine (public) Hilfsvariable initialisiert, wenn alles gefunden wurde (-> blisDa (Boolean)). Darauf greifen die Funktionen in den anderen Units zu um zu wissen ob alles gefunden wurde und um ggf. einen Erzeugungsprozess zu starten. Dies habe ich so gemacht um nicht jedesmal eine neue Instanz erzeugen zu müssen.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...habe jetzt mal versuchsweise auch in einem anderen Programm mal TIniFile getestet und dort funktioniert alles mit heaptrc. So richtig werde ich daraus nicht schlau?!?
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...ich hab' mir jetzt nochmal beide Programme angeschaut: Könnte es sein, dass der Grund für das seltsame Verhalten in der Deklaration liegt?

Code: Alles auswählen

 
...
  private
    { private declarations }
  public
    { public declarations }
      IniF:TIniFile;
      blDBisDa:Boolean;
...
 
und unter TFrmMain.Create der Aufruf

Code: Alles auswählen

 
...
  IniF:=TIniFile.Create(IniPfad+'ORC'+ext);
...
 
Muss ich das evtl. anders deklarieren?
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...weil dann wär ja klar warum man jedesmal eine neue Instanz erzeugen muss, wenn man es nicht "public" deklarieren kann/darf.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

wp_xyz
Beiträge: 5304
Registriert: Fr 8. Apr 2011, 09:01

Re: Speicherlecks in TIniFile?

Beitrag von wp_xyz »

and4more hat geschrieben:Nachtrag: @wp_xyz: hab' grad gesehen, es geht doch nicht so modular, da die INI-Datei zur Laufzeit von verschiedenen Fenstern bzgl. verschiedener Dinge abgefragt wird, wie z. B. an welcher Position das betreffende Fenster zuletzt geöffnet wurde, dann Pfad und Name der Datenbank und verschiedene andere Werte mit denen einzelne Units arbeiten, sodass Read und Write-Funktionen nicht in dieser Form modularisierbar sind. Verwenden könnte man allerdings die GetIniName Funktion, aber macht das Sinn wenn der Name der Ini-Datei feststeht und sich eh nicht ändert? Die Dateiendung läuft bei mir aus Kompatibilitätsgründen zu Linux als Variable "ext" (.ini bzw..conf mit Pragma {$MSWINDOWS bzw. UNIX)
Die Ini-Datei wird nur in der Main-Unit erzeugt und dort auch wieder freigegeben, alle anderen Units beziehen sich auf die dort erzeugte Instanz.
Ich würde jedem Formular ein eigenes Readini und Writeini geben, so dass sich jedes Formular lokal eine TIniFile erzeugt, sich die benötigten Informationen rausholt (bzw. reinschreibt) und dann wieder freigibt. So bleibt alles kompakt und übersichtlich. Der Mehraufwand, die Ini-Datei jedesmal neu einzulesen, ist vernachlässigbar. Ich habe zahllose Programme. die nach diesem Schema mit Konfigurationsdaten umgehen. Wenn Parameter von mehreren Formularen gebraucht werden, deklariere ich üblicherweise einen globalen Record TSettings, der vom Hauptformular aus der Ini-Datei gelesen wird. TSettings ist in einer "Mehrzweck"-Unit mit allgemeinen Deklarationen, Konstanten und Variablen deklariert, die von allen in Frage kommenden Formularen eingebunden wird. Dadurch wird verhindert, dass ein Formular ein anderes benötigt, was früher oder später Probleme macht.

and4more
Beiträge: 207
Registriert: Do 15. Nov 2012, 19:13
OS, Lazarus, FPC: Windows 10, Manjaro Linux, Lazarus 1.6.4 (32/64 Bit)
CPU-Target: 32 Bit / 64 Bit

Re: Speicherlecks in TIniFile?

Beitrag von and4more »

...das macht Sinn, dann findet der Zugriff auf die Ini-Datei nur beim Start und beim Beenden statt, alles Weitere zur Laufzeit wird mit dem Record gemacht und der Record wird beim Beenden wieder in der Ini-Datei gespeichert.
Lazarus 1.6.4 32-Bit + 64-Bit, Windows 10 64-Bit, Manjaro Linux 64-Bit

Antworten