AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Programm stürzt nach mehren Stunden Laufzeit ab.
Thema durchsuchen
Ansicht
Themen-Optionen

Programm stürzt nach mehren Stunden Laufzeit ab.

Ein Thema von gee21 · begonnen am 2. Sep 2021 · letzter Beitrag vom 22. Sep 2021
Antwort Antwort
venice2
(Gast)

n/a Beiträge
 
#1

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 6. Sep 2021, 15:11
Ich bin jetzt nicht so der Profi - aber müsste das ts:=tstringlist.create; nicht vor dem try stehen, damit das Sinn ergibt ?
Ja.
  Mit Zitat antworten Zitat
gee21

Registriert seit: 3. Jan 2013
199 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 6. Sep 2021, 15:51
Anscheinend verstehe ich ziemlich viel nicht.

Beispiel 3 habe ich ebenfalls im Internet in einem toutorial geshen und habe nun begonnen dies so abzuändern. Da es wohl so einfach richtig ist

Weshalb aber soll/muss ts:=tstringlist.create; vor den try stehen? Was wäre der nachteil wenn es nach dem try steht? Grössere ressourcen verbrauch? Oder unübersichtlicher da man bei einer exception mehr zu kontrollieren hat?

Ich bin am mittwoch wieder am arbeiten. Dann werde ich alles nach besten wissen und gewissen abändern und euch genauere infos über speicherverbrauch und handles geben können.

In meinem ersten Post ist der komplette quelltext!
Robert

Geändert von gee21 ( 6. Sep 2021 um 16:54 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.600 Beiträge
 
Delphi 7 Professional
 
#3

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 6. Sep 2021, 17:18
Beispiel 3 habe ich ebenfalls im Internet in einem toutorial geshen und habe nun begonnen dies so abzuändern. Da es wohl so einfach richtig ist

Weshalb aber soll/muss ts:=tstringlist vor den try stehen? Was wäre der nachteil wenn es nach dem try steht? Grössere ressourcen verbrauch? Oder unübersichtlicher da man bei einer exception mehr zu kontrollieren hat?
Weil ansonsten (zumindest theoretisch) die Möglichkeit besteht, dass ts innerhalb des Try / Except bzw. Try / Finally nicht existiert. Zumindest die Kompilerwarnung wird dies aussagen.

Du arbeitest sehr viel mit globalen Variabel, das macht den Quelltext nicht unbedingt leichter les- und verstehbar. Mir ist z. B. noch nicht so ganz klar geworden, wann ts_log nun von wo welche Daten bekommt, wann sie wo unter welchen Bedingungen in die Datei geschrieben werden, ob und ggfls. wann und wie lange ts_log und Memo_log (teilweise) identischen Inhalt haben ...

Viele Deiner Prozeduren könntest Du auch zu Prozeduren des Formulars machen, dann sparst Du Dir schonmal die vielen Form1...

Wahllos rausgegriffen mit der Bitte um "grundsätzliche" Korrektur:
Delphi-Quellcode:
if tdirectory.exists(programmpfad+'LS')=false then tdirectory.CreateDirectory(programmpfad+'LS');
if tdirectory.exists(programmpfad+'RE')=false then tdirectory.CreateDirectory(programmpfad+'RE');
if tdirectory.exists(programmpfad+'ET1')=false then tdirectory.CreateDirectory(programmpfad+'ET1');
if tdirectory.exists(programmpfad+'ET2')=false then tdirectory.CreateDirectory(programmpfad+'ET2');

if form1.CheckBox_autostart.checked=true then form1.Button_start_action.Click;

...

   if ts.Strings[14]='tray:offthen form1.CheckBox_tray.Checked:=false;
   if ts.Strings[15]='autostart:offthen form1.CheckBox_autostart.Checked:=false;
   if ts.Strings[16]='debug:offthen form1.CheckBox_DebugLog.Checked:=false;
   if ts.Strings[17]='hidecmd:offthen form1.CheckBox_verstecke_cmd.Checked:=false;

...

  if form1.CheckBox_tray.Checked=false then ts.Add('tray:off') else ts.Add('tray:on');
  if form1.CheckBox_autostart.Checked=false then ts.Add('autostart:off') else ts.Add('autostart:on');
  if form1.CheckBox_DebugLog.Checked=false then ts.Add('debug:off') else ts.Add('debug:on');
  if form1.CheckBox_verstecke_cmd.Checked=false then ts.Add('hidecmd:off') else ts.Add('hidecmd:on');

...

 if form1.idftp1.DirectoryListing[i].FileName='.'=false then
 if form1.idftp1.DirectoryListing[i].FileName='..'=false then begin
Lieber in dieser Form:
Delphi-Quellcode:
if not tdirectory.exists(programmpfad + 'LS') then tdirectory.CreateDirectory(programmpfad + 'LS');
if not tdirectory.exists(programmpfad + 'RE') then tdirectory.CreateDirectory(programmpfad + 'RE');
if not tdirectory.exists(programmpfad + 'ET1') then tdirectory.CreateDirectory(programmpfad + 'ET1');
if not tdirectory.exists(programmpfad + 'ET2') then tdirectory.CreateDirectory(programmpfad + 'ET2');

if form1.CheckBox_autostart.checked then form1.Button_start_action.Click;

...

form1.CheckBox_tray.Checked := ts.Strings[14] = 'tray:on';
form1.CheckBox_autostart.Checked := ts.Strings[15] = 'autostart:on';
form1.CheckBox_DebugLog.Checked := ts.Strings[16] = 'debug:on';
form1.CheckBox_verstecke_cmd.Checked := ts.Strings[17] = 'hidecmd:on';

...

  ts.Add(IfThen(form1.CheckBox_tray.Checked,'tray:on','tray:off'));
  ts.Add(IfThen(form1.CheckBox_autostart.Checked,'autostart:on','autostart:off'));
  ts.Add(IfThen(form1.CheckBox_DebugLog.Checked,'debug:on','debug:off'));
  ts.Add(IfThen(form1.CheckBox_verstecke_cmd.Checked,'hidecmd:on','hidecmd:off'));

...

 if (form1.idftp1.DirectoryListing[i].FileName <> '.')
 and (form1.idftp1.DirectoryListing[i].FileName <> '..') then begin
Und nein: Da ist jetzt kein Muss, sondern nur als Idee für die eventuelle, zukünftige Beachtung gedacht.
Und vermutlich haben andere dazu auch andere Ansichten.
  Mit Zitat antworten Zitat
gee21

Registriert seit: 3. Jan 2013
199 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 6. Sep 2021, 17:42
Zitat:
Weil ansonsten (zumindest theoretisch) die Möglichkeit besteht, dass ts innerhalb des Try / Except bzw. Try / Finally nicht existiert. Zumindest die Kompilerwarnung wird dies aussagen.
Achso. Ja ich verstehe nun. Vielen Dank. (ich bin einfach davon ausgegangen wenn ich als erstes im try / except block die stringliste generiere kann/wird schon nichts schief gehen. aber du hast natürlich recht.

Deine Vorschläge sind sicher viel korrekter als meine daher werde ich sie am Mittwoch alle abändern und hoffe das ich dann in Zukunft daran denke werde und es dann auch zum "Standart" für mich wird.

Beim Log habe ich mir folgendes überlegt:

Grundsätzlich wird das Memo einfach immer und immer wieder abgespeichert. (procedure addline und addline_debug)
wenn aber mehr als 5000 zeilen im memo sind, wird wenn vorhanden die Log_old.txt geladen (in die TS_Log) und die zeilen des memo werden hinzugefügt. (sofern die Log_old.txt (also ts_log) nicht bereits mehr als 20'000 zeilen hat).

Es ging mir einerseits darum das memo möglichst schnell speichern zu können. (weniger als 5000 zeilen)
anderrerseits darf das "komplette" Log (also log_old.txt) nicht zuviel Speicherplatz verbraten. daher auch da nochmals die begrenzung von 20'000 Zeilen.

Sicher auch nicht die perfekte Lösung. aber eigentlich sollte auch nicht viel im log stehen da Addline_Debug normalerweise deaktiviert ist. und Addline vieleicht 1-2x pro tag ein eintrag macht betreffend: Verbindungsproblem zum server (warum auch immer... zB wlan verbindung abgerissen, Server Down, oder sonstiges. )
Robert

Geändert von gee21 ( 6. Sep 2021 um 17:55 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.600 Beiträge
 
Delphi 7 Professional
 
#5

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 6. Sep 2021, 18:39
Prinzipiell deckt sich Deine Beschreibung mit meiner Vermutung für das gedachte Vorgehen. Nur bin ich mir nicht sicher, ob es auch so funktioniert
Delphi-Quellcode:

procedure addline (s: string);
var
  i : integer;
begin
  try
    form1.Memo_Log.Lines.Add(datetimetostr(now)+': ' + s);
    if form1.Memo_Log.Lines.Count > 5000 then begin
      // Wenn form1.Memo_Log.Lines mal über 5000 Zeilen hatte, kann diese Datei existieren.
      if tfile.Exists(programmpfad + 'log_OLD.txt') then begin
        // Wir laden sie dann.
        ts_log.loadfromfile(programmpfad+'log_OLD.txt');
        // Sollte sie mehr als 20000 Zeilen enthalten,
        if ts_log.Count > 20000 then begin
          // schmeißen wir den Inhalt weg
          ts_log.Clear;
          // und fangen neu an.
          ts_log.Add(Datetimetostr(now)+' Altes Log wurde automatisch zurückgesetzt da über 20 000 zeilen.')
        end;
        // Dann hängen wir den Inhalt von form1.Memo_Log.Lines an
        for I := 0 to form1.Memo_Log.Lines.Count-1 do ts_log.Add(form1.Memo_Log.Lines[i]);
        // und haben das alles im Speicher.
      end;
      // Memo hat mehr als 5000 Zeilen, dann schreiben wir das in log_OLD.txt
      // und überschreiben damit die Datei, die wir gerade eben mit ts_log.loadfromfile geladen haben.
      form1.Memo_Log.Lines.SaveToFile(programmpfad + 'log_OLD.txt');
      // und schmeißen unseren Memo-Inhalt weg.
      form1.Memo_Log.Clear;
      // Damit kann log_OLD.txt immer nur maximal den Inhalt von einem form1.Memo_Log.Lines haben.
      // Da ts_log nie gespeichert wird, halten wir alles, was per ts_log.Add(form1.Memo_Log.Lines[i]);
      // hinzugefügt wird, immer nur im Speicher vor. Da man die Stringlist aber eh nicht sehen kann
      // könnte man darauf auch verzichten :-(
    end;

    form1.Memo_Log.Lines.SaveToFile(programmpfad+'log.txt');

  except on e:exception do form1.Memo_Log.Lines.Add('Fehler beim speichern des Log Files: '+e.Message); end;
end;
Versuch einer Korrektur:
Delphi-Quellcode:
procedure addline(s: string);
var
  ts_log : TStringList; // Es gibt keinen Grund, warum wir hierfür eine globale Stringliste nehmen sollten
                        // und deren Inhalt immer im Speicher halten sollten.
begin
  ts_log := TStringList.Create;
  try
    try
      form1.Memo_Log.Lines.Add(Format('%s: %s',[datetimetostr(now),s]));
      if form1.Memo_Log.Lines.Count > 5000 then begin
        // Wenn form1.Memo_Log.Lines mal über 5000 Zeilen hatte, kann diese Datei existieren
        // und wir laden sie.
        if tfile.Exists(programmpfad + 'log_OLD.txt') then ts_log.loadfromfile(programmpfad + 'log_OLD.txt');
        // Sollte sie mehr als 20000 Zeilen enthalten,
        if ts_log.Count > 20000 then begin
          // schmeißen wir den Inhalt weg
          ts_log.Clear;
          // und fangen neu an.
          ts_log.Add(Format('%s: %s',[Datetimetostr(now),'Altes Log wurde automatisch zurückgesetzt da über 20 000 Zeilen.']));
        end;
        // Dann hängen wir den Inhalt von form1.Memo_Log.Lines an, das geht auch am Stück und nicht nur zeilenweise.
        ts_log.AddStrings(form1.Memo_Log.Lines);
        // und speichern es in der Datei
        ts_log.SaveToFile(programmpfad + 'log_OLD.txt');
        // form1.Memo_Log wird geleert.
        form1.Memo_Log.Clear;
        // und hängen die Meldung, die zum Aufruf von AddLines führte an.
        // Andernfalls bekämen wir grundsätzlich jede 5001. Meldung (im Memo) nie zu Gesicht.
        form1.Memo_Log.Lines.Add(Format('%s: %s',[datetimetostr(now),s]));
      end;
      form1.Memo_Log.Lines.SaveToFile(programmpfad + 'log.txt');
    except
      on e:exception do begin
        form1.Memo_Log.Lines.Add('Fehler beim Speichern der Logfiles: ' + e.Message);
      end;
    end;
  finally
    ts_log.free;
  end;
end;
Da Addline_Debug eigentlich genau das Gleiche macht, kann man da ja eventuell mal ein bisserl was an (redundantem) Quelltext sparen:
Delphi-Quellcode:
procedure Addline_Debug(s : String);
begin
  if form1.CheckBox_DebugLog.Checked then AddLine(Format('(Debug) %s',[s]));
end;
Das ist jetzt nur hingedaddelt, keine Ahnung, ob es jetzt wirklich das macht, was ursprünglich angedacht war.
  Mit Zitat antworten Zitat
gee21

Registriert seit: 3. Jan 2013
199 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 7. Sep 2021, 16:36
Also ich habe heute versucht den Code anzupassen.
Im Anhang noch ein Bild des Taskmanager betreffend Speicherauslastung.
Und ein Auszug des Log aus dem Programm.
Die Handles zählen sich hoch aber reduzieren sich dann zum teil auch wieder. Ich weiss jetzt gar nicht genau ob dies gut / schlecht ist.

Richtig testen werde ich morgen im Geschäft können.


Speicher Screenshot aus Taskmanager
http://gee.myasustor.com:8642/SpeicherScreenshot.png


Delphi Code angepasst:
http://gee.myasustor.com:8642/a_pas.txt

Log aus Programm
http://gee.myasustor.com:8642/a_log.txt
Robert

Geändert von gee21 ( 8. Sep 2021 um 10:32 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.600 Beiträge
 
Delphi 7 Professional
 
#7

AW: Programm stürzt nach mehren Stunden Laufzeit ab.

  Alt 7. Sep 2021, 17:33
In diesem Bereich würde ich mal weiterschauen, hier scheint es ja einen Handlezuwachs zu geben:
Code:
07.09.2021 17:09:27: (Debug) PrinServer_action fertig: Handles: 354
07.09.2021 17:09:30: (Debug) : LS Download von Server OK: FranceTest.pdf
07.09.2021 17:09:30: (Debug) : LS Download von Server OK: SwissTest.pdf
07.09.2021 17:09:31: (Debug) : LS Download von Server OK: GermanTest.pdf
07.09.2021 17:09:31: (Debug) Druckbefehl (LS) gesendet: GermanTest.pdf
07.09.2021 17:09:36: (Debug) Druckbefehl (LS) gesendet: SwissTest.pdf
07.09.2021 17:09:41: (Debug) Druckbefehl (LS) gesendet: FranceTest.pdf
07.09.2021 17:09:47: (Debug) : RE Download von Server OK: FranceTest.pdf
07.09.2021 17:09:47: (Debug) : RE Download von Server OK: SwissTest.pdf
07.09.2021 17:09:48: (Debug) : RE Download von Server OK: GermanTest.pdf
07.09.2021 17:09:48: (Debug) Druckbefehl (RE) gesendet: GermanTest.pdf
07.09.2021 17:09:54: (Debug) Druckbefehl (RE) gesendet: SwissTest.pdf
07.09.2021 17:09:59: (Debug) Druckbefehl (RE) gesendet: FranceTest.pdf
07.09.2021 17:10:04: (Debug) : ET1 Download von Server OK: FranceTest.pdf
07.09.2021 17:10:05: (Debug) : ET1 Download von Server OK: SwissTest.pdf
07.09.2021 17:10:05: (Debug) : ET1 Download von Server OK: GermanTest.pdf
07.09.2021 17:10:05: (Debug) Druckbefehl (ET1) gesendet: GermanTest.pdf
07.09.2021 17:10:10: (Debug) Druckbefehl (ET1) gesendet: SwissTest.pdf
07.09.2021 17:10:15: (Debug) Druckbefehl (ET1) gesendet: FranceTest.pdf
07.09.2021 17:10:21: (Debug) : ET2 Download von Server OK: FranceTest.pdf
07.09.2021 17:10:21: (Debug) : ET2 Download von Server OK: SwissTest.pdf
07.09.2021 17:10:22: (Debug) : ET2 Download von Server OK: GermanTest.pdf
07.09.2021 17:10:22: (Debug) Warte auf beendigung des PrintServer Vorgang...
07.09.2021 17:10:22: (Debug) PrinServer_action fertig: Handles: 475
07.09.2021 17:10:22: (Debug) Druckbefehl (ET2) gesendet: GermanTest.pdf
07.09.2021 17:10:28: (Debug) Druckbefehl (ET2) gesendet: SwissTest.pdf
07.09.2021 17:10:33: (Debug) Druckbefehl (ET2) gesendet: FranceTest.pdf
Protokolliere bitte in jeder Zeile auch die Handles, damit man sehen kann, bei welchem Schritt ein Zuwachs entsteht.

Das könnte z. B. in der Routine Start_Bat sein. Da bitte am Anfang und am Ende die Handles protokollieren, dann kann man erkennen, ob der Handlezuwachs durch den Aufruf von ShellExecute entsteht, was mich zwar verwundern würde, aber man weiß ja nie
Delphi-Quellcode:
procedure start_bat(s:string);
begin
  Addline_Debug(Format('Beginne start_bat(%s',[s]));
  if Form1.CheckBox_verstecke_cmd.Checked then
    ShellExecute(Application.Handle, 'open',PChar(S), nil, nil, SW_HIDE)
  else
    ShellExecute(Application.Handle, 'open',PChar(S), nil, nil, SW_NORMAL);
  Addline_Debug(Format('Beende start_bat(%s',[s]));
end;
Eventuell könntest Du aber auch Addline_Debug ergänzen, dann wird der Änderungsaufwand geringer:
Delphi-Quellcode:
// Je nach Delphi ist's auch schon in Winapi.Windows zu finden:
function GetProcessHandleCount(hProcess: THandle; var pdwHandleCount: DWORD): BOOL; stdcall; external 'kernel32.dll';

procedure Addline_Debug(s: String);
var
  HandleCount: DWORD;
begin
  if Form1.CheckBox_DebugLog.Checked then begin
    if GetProcessHandleCount(GetCurrentProcess, HandleCount) then begin
      addline(Format('(Debug) %s (Handles: %d)', [s, HandleCount]));
    end else begin
      addline(Format('(Debug) %s', [s]));
    end;
  end;
end;

procedure LogHandles(s : String);
var
  HandleCount: DWORD;
begin
  if GetProcessHandleCount(GetCurrentProcess, HandleCount) then
    Addline_Debug(Format('%s: Handles: %d',[s, HandleCount]));
end;
Ansonsten sehe ich momentan keine Stelle, an der ich mit einem Handlezuwachs rechnen würde. Aber das heißt ja nix.

Und dann ändere bitte noch die Zeile

ts_log.SaveToFile(programmpfad + 'log_OLD.txt');

in

ts_log.SaveToFile(Format('%s%s.log',[programmpfad,FormatDateTime('YYYYMMDD_HHNNSS.ZZZ',Now)]));

Damit bekommst Du dann eine Logfilehistorie und kannst dadurch das Geschehene auch über einen längeren Zeitraum protokollieren, musst allerdings dann die Logdateien manuell löschen, da sie nicht mehr überschrieben werden.

Geändert von Delphi.Narium ( 7. Sep 2021 um 19:56 Uhr) Grund: Schreibfehler
  Mit Zitat antworten Zitat
Antwort Antwort


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 16:52 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