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
Delphi.Narium

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

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
 
#2

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
 
#3

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
venice2
(Gast)

n/a Beiträge
 
#4

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

  Alt 7. Sep 2021, 19:32
Ich würde mal die ganzen Proceduren und Functionen zu deiner Class addieren dann kannst du dir die ganzen Aufrufe Form1 ersparen.
Delphi-Quellcode:
    private
      procedure addline(s: string);

Delphi-Quellcode:
procedure TForm1.addline(s: string);
var
    ts_log: tstringlist;
begin
    ts_log := tstringlist.Create;
    try
        try
            Memo_Log.Lines.Add(Format('%s: %s', [datetimetostr(now), s])); // vorher Form1.Memo_Log.Lines.Add
Nur ein Vorschlag.
Das gleiche gilt für die globalen Variablen.

procedure PrinServer_action;
soll bestimmt
procedure PrintServer_action;
heißen.

Zitat:
ob der Handlezuwachs durch den Aufruf von ShellExecute entsteht, was mich zwar verwundern würde
Mich auch denn er startet ja nur Batch Dateien und bekanntlich haben Konsolen kein eigenes Handle und ShellExecute gibt nur die HInstance (Fehlercodes) zurück also ob der Start erfolgreich war oder nicht.
Da ist nix mit Freigeben.

Nebenbei er könnte sich ja mal das anschauen

Geändert von venice2 ( 7. Sep 2021 um 20:47 Uhr)
  Mit Zitat antworten Zitat
gee21

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

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

  Alt 8. Sep 2021, 09:30
Ok, Habe heute morgen versucht eure Vorschläge einzubauen/anzupassen.

Zitat:
Ich würde mal die ganzen Proceduren und Functionen zu deiner Class addieren dann kannst du dir die ganzen Aufrufe Form1 ersparen.
Habe ich versucht. ist jedoch das erste mal das ich dies so mache (bin daher nicht ganz sicher ob ich es richtig gemacht habe)

Zitat:
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
Habe ich versucht. Hoffe es ist nun ungefähr so wie es sein müsste.



LogFiles des Programm:
http://gee.myasustor.com:8642/log.txt
http://gee.myasustor.com:8642/20210908_095628.032.log
http://gee.myasustor.com:8642/20210908_100613.433.log

Gestern ist das Programm im Geschäft 2x Abgestürtzt (jedoch noch die alte version)
Handles gemäss Taskmanager während das programm eingefroren war: 328 und 324.

Ich frage mich langsam ob es wirklich an den Handles liegt oder ob evt ein Computer spezifisches Problem vorliegt? ---> falls jetzt die neue Version heute oder morgen wieder abstürtzt, werde ich es einmal auf einem anderen Computer laufen lassen.

In der .Bat steht übrigens folgendes: (falls das noch ein problem sein könnte?)
Code:
ET1\PDFtoPrinter ACTUAL_PRINT.pdf "Zebra Etiketten"
wait
exit
http://gee.myasustor.com:8642/main.pas
Robert

Geändert von gee21 ( 8. Sep 2021 um 10:26 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.785 Beiträge
 
Delphi 12 Athens
 
#6

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

  Alt 8. Sep 2021, 10:19
Nur eine Bitte: Kannst du uns die Scroll-Orgien ersparen und einfach die PAS-Datei als Anhang mitgeben?
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  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 8. Sep 2021, 10:41
Beim Logging sehe ich das Problem, dass ja nur alle 5000 Zeilen gespeichert wird.

Bei einem Programmabsturz kann man also schlimmstenfalls die letzten 5000 Zeilen im Protokoll nicht nachvollziehen. Die letzten Infos vor dem Auftreten eines Fehlers gehen also verloren.

Daher ein Vorschlag für die Änderung der Fehlerprotokollierung (nur hingedaddelt und nicht getestet):
Delphi-Quellcode:
procedure TForm1.AddLine(s : String);
var
          f : TextFile;
          iIOResult : Integer;
          sLogFile : String;
begin
    // Der Name der LOG-Datei ändert sich stündlich.
    // Wir schreiben die Meldungen sofort in eine Datei und speichern diese sofort.
    // Dadurch sollte auch die letzte Meldung vor dem Auftreten eines Fehlers
    // in der Datei einhalten sein.
    sLogFile := Format('%s%s.log',[programmpfad,FormatDateTime('YYYYMMDD_HH',Now)]);
    iIOResult := 0;
{$I-}
    AssignFile(f,sLogFile);
    try
      try
        if not FileExists(sLogFile) then begin
          ReWrite(f);
          iIOResult := IOResult;
        end else begin
          Append(f);
          iIOResult := IOResult;
        end;
        if iIOResult = 0 then begin
          WriteLn(f,FormatDateTime('yyyy.mm.dd hh.nn.ss,zzz',now),': ',s);
          Flush(f);
          iIOResult := IOResult;
          CloseFile(f);
          iIOResult := IOResult;
          iIOResult := 0;
        end;
      except
        on e : EOsError do MessageDLG(e.Message + #13#13 + SysErrorMessage(GetLastError),mtError,[mbOk],0);
        on e : Exception do MessageDLG(e.Message,mtError,[mbOk],0);
      end;
    finally
      if (iIOResult <> 0) and (iIOResult <> 32) then begin
        MessageDLG(Format('Fehler %d in AddLine.',[iIOResult]),mtError,[mbOK],0);
      end;
    end;
{$I+}
end;
Memo_Log wird zur Fehlerprotokollierung nicht mehr benötigt.
Absonsten kann ich momentan beim besten Willen nicht erkennen, woher da ein Programmabsturz ... kommen könnte.

Achso: Die Handles können ein Indikator für ein grundlegendes Problem sein, müssen es aber nicht.
Ein computerspezifisches Problem würd' ich hier eher nicht vermuten. Eher im Umfeld mit der FTP-Kommunikation.

ShellExecute ist 'ne Funktion, deren Rückgabewert man auswerten kann (https://docs.microsoft.com/en-us/win...hellexecutea):
Delphi-Quellcode:
procedure Tform1.start_bat(s:string);
var
  iShellEx : Integer;
  iSW : Integer;
begin
  LogHandles('start_bat_begin');
  case CheckBox_verstecke_cmd.Checked of
    true : iSW := SW_HIDE;
    false : iSW := SW_NORMAL;
  end;
  iShellEx := ShellExecute(Application.Handle, 'open', PChar(S), nil, nil, iSW);
  AddLine_Debug(Format('ShellExecute: %s Rückgabewert: %d',[s,iShellEx]));
  LogHandles('start_bat_ende');
end;
  Mit Zitat antworten Zitat
UntoterGeist

Registriert seit: 18. Sep 2019
25 Beiträge
 
#8

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

  Alt 15. Sep 2021, 19:11
Ein ähnliches Problem hatte ich auch mal. Allerdings hat er dann bei dem Versuch irgendwas in der GUI zu machen eine Exception geworfen. Im ersten Beitrag steht ja das die relevante Funktion die als letztes aufgerufen wird in einem try except Block steht. Wenn ich das richtig verstanden habe. Das würde glaube jede Exceptionmeldung natürlich unterdrücken. Die Exe im Debug mode zu kompilieren macht ja auch noch ein Unterschied.

Ich hatte auch alles nach Speicherlecks durchsucht. Aber die Ursache war letztendlich der Speicherort von dem aus das Programm gestartet wurde. Der Fehler ist nur aufgetreten, wenn das Programm aus dem Netzwerk gestartet wurde. Das selbe könnte für Wechseldatenträger gelten.
  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 11:13 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