Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Programm stürzt nach mehren Stunden Laufzeit ab. (https://www.delphipraxis.net/208661-programm-stuerzt-nach-mehren-stunden-laufzeit-ab.html)

gee21 2. Sep 2021 13:58

Programm stürzt nach mehren Stunden Laufzeit ab.
 
Hallo.

Ich habe ein Programm geschrieben das auf einem FTP Server PDF Dateien herunterlädt und an den richtigen Drucker senden soll.
Es gibt 4 Verzeichnisse auf dem ftp server. je nach dem in welchen Verzeichniss das pdf file liegt, gebe ich es dann an den "passenden" drucker weiter.

Grundsätzlich funktioniert alles so wie es soll... ABER nach 5-8 stunden stürzt das Tool meistens ab (friert ein). Im Log sehe ich meistens das es sich um ein verbindungsproblem zum server handelt (read timed out)... jedoch habe ich die ftp geschichte ins TRY / EXCEPT getan damit bei einem Fehler einfach eine neue Verbindung / ein neuer versuch gestartet wird. (wäre zumindest das ziel).

Wenn ich das programm laufen lasse und dann beispielsweise das LAN KABEL ausstecke... so erhalte ich ebenfalls die Meldung: Read Timed Out oder Connection reset by peer... wenn ich das LAN kabel dann wieder einstecke, funktioniert alles wieder normal... (also so beim Test passiert auch alles so wie es soll).


HAT VIELEICHT JEMAND EINE IDEE an was es liegen könnte das es mehrere stunden funktioniert und dann plötzlich einfriert? (Im Taskmanager wenn mein tool läuft, ist CPU, RAM und Festplatennutzung der project.exe "normal")
Würde es helfen die FTP geschichte in einem seperaten Thread laufen zu lassen?




Delphi-Quellcode:
unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdExplicitTLSClientServerBase, IdFTP,
  Vcl.StdCtrls, Vcl.ComCtrls, Vcl.ExtCtrls, system.IOUtils, shellapi, Vcl.Menus;

type
  TForm1 = class(TForm)
    IdFTP1: TIdFTP;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    Memo_Log: TMemo;
    Edit_Host: TEdit;
    Edit_User: TEdit;
    Edit_Password: TEdit;
    Edit_Port: TEdit;
    Edit_LS: TEdit;
    Edit_RE: TEdit;
    Edit_ET1: TEdit;
    Edit_ET2: TEdit;
    Edit_verbindungsDAUER: TEdit;
    Label1: TLabel;
    Timer1_dauer_Counter: TTimer;
    ProgressBar_VerbindungsZeit: TProgressBar;
    CheckBox_DebugLog: TCheckBox;
    Button_start_action: TButton;
    Edit_update_pause: TEdit;
    Label2: TLabel;
    timer_restarter: TTimer;
    Edit_pause_wiederverbindung: TEdit;
    Label3: TLabel;
    CheckBox_verstecke_cmd: TCheckBox;
    Label4: TLabel;
    Edit_druck_pause: TEdit;
    Button_STOP: TButton;
    Edit_rechnungen_max: TEdit;
    Edit_papierfach_max: TEdit;
    Label_rechnung_aktuell: TLabel;
    Label_Papier_aktuell: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    CheckBox_tray: TCheckBox;
    CheckBox_autostart: TCheckBox;
    TrayIcon1: TTrayIcon;
    PopupMenu1: TPopupMenu;
    ffneprogramm1: TMenuItem;
    Timer1autominimize: TTimer;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Button_zähler_auf_null: TButton;
    CheckBox_papierzähler: TCheckBox;
    PopupMenu2: TPopupMenu;
    Loglschen1: TMenuItem;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    edit_pause_zwischen_directories: TEdit;
    CheckBox_SSL: TCheckBox;
    procedure Timer1_dauer_CounterTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button_start_actionClick(Sender: TObject);
    procedure timer_restarterTimer(Sender: TObject);
    procedure Button_STOPClick(Sender: TObject);
    procedure ffneprogramm1Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure TrayIcon1Click(Sender: TObject);
    procedure TrayIcon1DblClick(Sender: TObject);
    procedure CheckBox_trayClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1autominimizeTimer(Sender: TObject);
    procedure Button_zähler_auf_nullClick(Sender: TObject);
    procedure Loglschen1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  stop_op,progress_running: boolean;
  programmpfad: string;
  papierfach, rechnungsfach: integer;
  ts_log: tstringlist;
implementation

{$R *.dfm}

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

      if tfile.Exists (programmpfad+'log_OLD.txt') then begin
      ts_log.loadfromfile (programmpfad+'log_OLD.txt');

       if ts_log.Count>20000 then begin
       ts_log.Clear;
       ts_log.Add(Datetimetostr(now)+' Altes Log wurde automatisch zurückgesetzt da über 20 000 zeilen.')
       end;

      for I := 0 to form1.Memo_Log.Lines.Count-1  do ts_log.Add(form1.Memo_Log.Lines[i]);

      end;
     form1.Memo_Log.Lines.SaveToFile(programmpfad+'log_OLD.txt');
     form1.Memo_Log.Clear;
     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;

 procedure Addline_Debug (S:String);
 var
 i: integer;
 begin
  if form1.CheckBox_DebugLog.Checked=true then begin
      form1.Memo_Log.Lines.Add(datetimetostr(now)+'_DEBUG: '+S);
      if form1.Memo_Log.Lines.Count>5000 then begin

       if tfile.Exists (programmpfad+'log_OLD.txt') then begin
       ts_log.loadfromfile (programmpfad+'log_OLD.txt');

        if ts_log.Count>20000 then begin
        ts_log.Clear;
        ts_log.Add(Datetimetostr(now)+' Altes Log wurde automatisch zurückgesetzt da über 100000 zeilen.')
        end;

        for I := 0 to form1.Memo_Log.Lines.Count-1  do ts_log.Add(form1.Memo_Log.Lines[i]);
       end;
      form1.Memo_Log.Lines.SaveToFile(programmpfad+'log_OLD.txt');
      form1.Memo_Log.Clear;
      end;
     form1.Memo_Log.Lines.SaveToFile(programmpfad+'log.txt');
     end;
  end;


procedure savesettings;
var
ts: tstringlist;
begin
  try
  ts:=tstringlist.Create;
  ts.Add(form1.Edit_Host.Text);
  ts.Add(form1.Edit_User.Text);
  ts.Add(form1.Edit_Password.Text);
  ts.Add(form1.Edit_Port.Text);
  TS.Add(Form1.Edit_LS.Text);
  TS.Add(Form1.Edit_RE.Text);
  TS.Add(Form1.Edit_ET1.Text);
  TS.Add(Form1.Edit_ET2.Text);
  TS.Add(Form1.Edit_verbindungsdauer.Text);
  TS.Add(Form1.Edit_update_pause.Text);
  TS.Add(Form1.Edit_pause_wiederverbindung.Text);
  TS.Add(Form1.Edit_druck_pause.Text);
  TS.Add(Form1.Edit_rechnungen_max.Text);
  TS.Add(Form1.Edit_papierfach_max.Text);
  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');
  ts.Add(inttostr(papierfach));
  ts.Add(inttostr(rechnungsfach));
  ts.Add(form1.edit_pause_zwischen_directories.text);
  ts.SaveToFile(programmpfad+'settings.txt');
  ts.Free;
  except on e:exception do
  addline('Fehler beim speichern der Einstellungen: '+e.message);
   end;
  end;


procedure loadsettings;
var
ts: tstringlist;
begin
ts:=tstringlist.Create;
if tfile.Exists(programmpfad+'settings.txt') then begin
   ts.LoadFromFile(programmpfad+'settings.txt');
   form1.Edit_Host.Text:=ts.strings[0];
   form1.Edit_User.Text:=ts.strings[1];
   form1.Edit_Password.Text:=ts.strings[2];
   form1.Edit_Port.Text:=ts.strings[3];
   form1.Edit_LS.Text:=ts.strings[4];
   form1.Edit_RE.Text:=ts.strings[5];
   form1.Edit_ET1.Text:=ts.strings[6];
   form1.Edit_ET2.Text:=ts.strings[7];
   form1.Edit_verbindungsdauer.Text:=ts.strings[8];
   form1.Edit_update_pause.Text:=ts.strings[9];
   form1.Edit_pause_wiederverbindung.Text:=ts.strings[10];
   form1.Edit_druck_pause.Text:=ts.strings[11];
   form1.Edit_rechnungen_max.Text:=ts.strings[12];
   form1.Edit_papierfach_max.Text:=ts.strings[13];
   if ts.Strings[14]='tray:off' then form1.CheckBox_tray.Checked:=false;
   if ts.Strings[15]='autostart:off' then form1.CheckBox_autostart.Checked:=false;
   if ts.Strings[16]='debug:off' then form1.CheckBox_DebugLog.Checked:=false;
   if ts.Strings[17]='hidecmd:off' then form1.CheckBox_verstecke_cmd.Checked:=false;
   papierfach:=strtoint(ts.Strings[18]);
   rechnungsfach:=strtoint(ts.Strings[19]);
   form1.Label_rechnung_aktuell.Caption:=ts.Strings[19];
   form1.Label_Papier_aktuell.Caption:=ts.Strings[18];
   try form1.edit_pause_zwischen_directories.text:=ts.Strings[20]; except end;
 end;
  ts.Free;
end;


procedure ftpverbinden ;
begin
 if form1.IdFTP1.Connected=false then begin
  try
  form1.ProgressBar_VerbindungsZeit.max:=strtoint(form1.Edit_verbindungsDAUER.Text);
  form1.ProgressBar_VerbindungsZeit.Position:=strtoint(form1.Edit_verbindungsDAUER.Text);
  form1.IdFTP1.Username:= form1.Edit_User.Text;
  form1.IdFTP1.Password:= form1.Edit_Password.Text;
  form1.IdFTP1.Port:= strtoint(form1.Edit_Port.Text);
  form1.IdFTP1.Host:= form1.Edit_Host.Text;
  form1.Timer1_dauer_Counter.enabled:=true;
  form1.IdFTP1.Connect;
  addline_debug('FTP Verbindung hergestellt');
  except on e:exception do addline('Fehler während Verbindungsaufbau: '+e.Message) end;
 end else addline('FTP Verbindung sollte aufgebaut werden ist aber bereits aufgebaut.');
end;


procedure ftp_trennen;
begin
 try
  if form1.IdFTP1.Connected=true then begin
  form1.IdFTP1.Disconnect;
  addline_debug('FTP Erfolgreich getrennt');
  end;

  except on e:exception do begin
   addline('Fehler beim trennen der FTP Verbidung: '+e.Message);
   try form1.IdFTP1.Disconnect except end;
  end;
 end;
end;


procedure ftp_directory_change (S:String);
begin
sleep(strtoint(form1.edit_pause_zwischen_directories.text)*1000);
try
form1.IdFTP1.ChangeDir(s);
addline_debug('ftp directory change ok: '+s);
except on e:exception do addline('Fehler beim Directory change: '+e.message) end;
end;

procedure PrinServer_action;
var
i: integer;
ts: tstringlist;
s: string;
begin
progress_running:=true;
ts:=tstringlist.Create;

 if form1.IdFTP1.Connected=false then begin
 form1.timer_restarter.enabled:=false;
 addline('Die Verbindung wurde getrennt? sollte aber offen sein für PrintServer Action. versuche neustart des ganzen Task');
 sleep(2000);
 form1.Button_start_action.Click;
 abort;
 end;

     try
//erstes verzeichniss LS
ftp_directory_change(form1.Edit_LS.Text);
//mache aktion
 form1.idftp1.List();
 for i := 0 to form1.idftp1.DirectoryListing.Count-1  do
 if form1.idftp1.DirectoryListing[i].FileName='.'=false then
 if form1.idftp1.DirectoryListing[i].FileName='..'=false then begin
  s:=form1.idftp1.DirectoryListing[i].FileName;
  ts.Add(s);
  form1.idftp1.Get(s,programmpfad+'LS\'+s, true);
  form1.idftp1.Delete(s);
  addline_debug(': LS Download von Server OK: '+s);
 end;

 if ts.Count>0 then begin
 s:='ACTUAL_PRINT.PDF';
   for I := ts.count-1 downto 0 do begin
 if tfile.exists(programmpfad+'LS\'+s) then begin
 tfile.Delete(programmpfad+'LS\'+s);
 addline('AlteDatei war vorhanden und wurde gelöscht: '+programmpfad+'LS\'+s); end;
 tfile.move(programmpfad+'LS\'+ts.strings[i],programmpfad+'LS\'+s );

   if tfile.Exists(Programmpfad+'LS.bat')=false then begin
   addline('BAT Fehler... '+ Programmpfad+'LS.bat nicht gefunden');
   end else begin
    if form1.checkbox_verstecke_cmd.Checked=true then
    ShellExecute(Application.Handle, 'open', PChar('LS.bat'), nil, nil, SW_HIDE)
    else
    ShellExecute(Application.Handle, 'open', PChar('LS.bat'), nil, nil, SW_NORMAL);
    papierfach:=papierfach+1;
    try
     addline_debug('Druckbefehl (LS) gesendet: '+ts.Strings[i]);
      try
      sleep(strtoint(form1.Edit_druck_pause.text)*1000);
      tfile.Delete(programmpfad+'LS\'+s);
      except on e:exception do
       addline('Fehler beim löschen der Lokalen Datei (LS)... evt zu kurze Wartezeit? '+e.Message)
      end;
     TS.Delete(i);
    except on e:exception do addline(e.Message) end;
   end;
  end;
 end;


ts.Clear;
//zweites verzeichniss RE
ftp_directory_change(form1.Edit_RE.Text);
//mache aktion
 form1.idftp1.List();
 for i := 0 to form1.idftp1.DirectoryListing.Count-1  do
 if form1.idftp1.DirectoryListing[i].FileName='.'=false then
 if form1.idftp1.DirectoryListing[i].FileName='..'=false then begin
  s:=form1.idftp1.DirectoryListing[i].FileName;
  ts.Add(s);
  form1.idftp1.Get(s,programmpfad+'RE\'+s, true);
  form1.idftp1.Delete(s);
  addline_debug(': RE Download von Server OK: '+s);
 end;

 if ts.Count>0 then begin
 s:='ACTUAL_PRINT.PDF';
   for I := ts.count-1 downto 0 do begin
 if tfile.exists(programmpfad+'RE\'+s) then begin
 tfile.Delete(programmpfad+'RE\'+s);
 addline('AlteDatei war vorhanden und wurde gelöscht: '+programmpfad+'RE\'+s); end;
 tfile.move(programmpfad+'RE\'+ts.strings[i],programmpfad+'RE\'+s );

   if tfile.Exists(Programmpfad+'RE.bat')=false then begin
   addline('BAT Fehler... '+ Programmpfad+'RE.bat nicht gefunden');
   end else begin

    if form1.checkbox_verstecke_cmd.Checked=true then
    ShellExecute(Application.Handle, 'open', PChar('RE.bat'), nil, nil, SW_HIDE)
    else
    ShellExecute(Application.Handle, 'open', PChar('RE.bat'), nil, nil, SW_NORMAL);
     rechnungsfach:=rechnungsfach+1;
    try
     addline_debug('Druckbefehl (RE) gesendet: '+ts.Strings[i]);
      try
      sleep(strtoint(form1.Edit_druck_pause.text)*1000);
      tfile.Delete(programmpfad+'RE\'+s);
      except on e:exception do
       addline('Fehler beim löschen der Lokalen Datei (RE)... evt zu kurze Wartezeit? '+e.Message)
      end;
     TS.Delete(i);
    except on e:exception do addline(e.Message) end;
   end;
  end;
 end;


ts.Clear;
//drittes verzeichniss ET1
ftp_directory_change(form1.Edit_ET1.Text);
//mache aktion
 form1.idftp1.List();
 for i := 0 to form1.idftp1.DirectoryListing.Count-1  do
 if form1.idftp1.DirectoryListing[i].FileName='.'=false then
 if form1.idftp1.DirectoryListing[i].FileName='..'=false then begin
  s:=form1.idftp1.DirectoryListing[i].FileName;
  ts.Add(s);
  form1.idftp1.Get(s,programmpfad+'ET1\'+s, true);
  form1.idftp1.Delete(s);
  addline_debug(': ET1 Download von Server OK: '+s);
 end;

 if ts.Count>0 then begin
 s:='ACTUAL_PRINT.PDF';
   for I := ts.count-1 downto 0 do begin
 if tfile.exists(programmpfad+'ET1\'+s) then begin
 tfile.Delete(programmpfad+'ET1\'+s);
 addline('AlteDatei war vorhanden und wurde gelöscht: '+programmpfad+'ET1\'+s); end;
 tfile.move(programmpfad+'ET1\'+ts.strings[i],programmpfad+'ET1\'+s );
   if tfile.Exists(Programmpfad+'ET1.bat')=false then begin
   addline('BAT Fehler... '+ Programmpfad+'ET1.bat nicht gefunden');
   end else begin

    if form1.checkbox_verstecke_cmd.Checked=true then ShellExecute(Application.Handle, 'open', PChar('ET1.bat'), nil, nil, SW_HIDE)
    else ShellExecute(Application.Handle, 'open', PChar('ET1.bat'), nil, nil, SW_NORMAL);

    try
     addline_debug('Druckbefehl (ET1) gesendet: '+ts.Strings[i]);
      try
     sleep(strtoint(form1.Edit_druck_pause.text)*1000);
      tfile.Delete(programmpfad+'ET1\'+s);
      except on e:exception do
       addline('Fehler beim löschen der Lokalen Datei (ET1)... evt zu kurze Wartezeit? '+e.Message)
      end;
     TS.Delete(i);
    except on e:exception do addline(e.Message) end;
   end;
  end;
 end;

  ts.Clear;
  //viertes verzeichniss ET2
  ftp_directory_change(form1.Edit_ET2.Text);
  //mache aktion
  form1.idftp1.List();
  for i := 0 to form1.idftp1.DirectoryListing.Count-1  do
  if form1.idftp1.DirectoryListing[i].FileName='.'=false then
   if form1.idftp1.DirectoryListing[i].FileName='..'=false then begin
   s:=form1.idftp1.DirectoryListing[i].FileName;
   ts.Add(s);
   form1.idftp1.Get(s,programmpfad+'ET2\'+s, true);
   form1.idftp1.Delete(s);
   addline_debug(': ET2 Download von Server OK: '+s);
   end;

 if ts.Count>0 then begin
 s:='ACTUAL_PRINT.PDF';
   for I := ts.count-1 downto 0 do begin
 if tfile.exists(programmpfad+'ET2\'+s) then begin
 tfile.Delete(programmpfad+'ET2\'+s);
 addline('AlteDatei war vorhanden und wurde gelöscht: '+programmpfad+'ET2\'+s); end;
 tfile.move(programmpfad+'ET2\'+ts.strings[i],programmpfad+'ET2\'+s );

   if tfile.Exists(Programmpfad+'ET2.bat')=false then begin
   addline('BAT Fehler... '+ Programmpfad+'ET2.bat nicht gefunden');
   end else begin

    if form1.checkbox_verstecke_cmd.Checked=true then
    ShellExecute(Application.Handle, 'open', PChar('ET2.bat'), nil, nil, SW_HIDE)
    else
    ShellExecute(Application.Handle, 'open', PChar('ET2.bat'), nil, nil, SW_NORMAL);

    try
     addline_debug('Druckbefehl (ET2) gesendet: '+ts.Strings[i]);
      try
      sleep(strtoint(form1.Edit_druck_pause.text)*1000);
      tfile.Delete(programmpfad+'ET2\'+s);
      except on e:exception do
       addline('Fehler beim löschen der Lokalen Datei (ET2)... evt zu kurze Wartezeit? '+e.Message)
      end;
     TS.Delete(i);
    except on e:exception do addline(e.Message) end;
   end;
  end;
 end;


  if form1.CheckBox_papierzähler.Checked=true then begin
   if papierfach>strtoint(form1.Edit_papierfach_max.text) then begin
    if form1.checkbox_verstecke_cmd.Checked=true then
    ShellExecute(Application.Handle, 'open', PChar('wartung.bat'), nil, nil, SW_HIDE)
    else
    ShellExecute(Application.Handle, 'open', PChar('wartung.bat'), nil, nil, SW_NORMAL);
    rechnungsfach:=0;
    papierfach:=0;
   end else begin
    if rechnungsfach>strtoint(form1.Edit_rechnungen_max.text) then begin
    if form1.checkbox_verstecke_cmd.Checked=true then
    ShellExecute(Application.Handle, 'open', PChar('wartung.bat'), nil, nil, SW_HIDE)
    else
    ShellExecute(Application.Handle, 'open', PChar('wartung.bat'), nil, nil, SW_NORMAL);
    rechnungsfach:=0;
    papierfach:=0;

    end;
   end;
  end;

  savesettings;
  addline_debug('PrinServer_Action fertig!');

      except on e:exception do begin
      addline('Fehler im "PrinServer_action" '+e.Message);
        try ftp_trennen except on e:exception do addline('Fehler beim versuch FTP zu trennen... '+e.Message); end;
      end;
     end;

form1.Label_rechnung_aktuell.Caption:=inttostr(rechnungsfach);
form1.Label_Papier_aktuell.Caption:= inttostr(papierfach);
ts.free;
progress_running:=false;
form1.timer_restarter.interval:=strtoint(form1.Edit_update_pause.Text)*1000;

 if form1.ProgressBar_VerbindungsZeit.Position>strtoint(form1.Edit_pause_wiederverbindung.text) then
 form1.timer_restarter.enabled:=true
 else begin
 form1.timer_restarter.enabled:=false;
 addline_debug('Restart Timer wurde nicht gestartet da weniger als '+ form1.Edit_pause_wiederverbindung.text+' sek übrig sind: '+inttostr(form1.ProgressBar_VerbindungsZeit.Position)+'Sek');
 ftp_trennen;
 end;

end;

procedure TForm1.Button_zähler_auf_nullClick(Sender: TObject);
begin
   papierfach:=0;
   rechnungsfach:=0;
   form1.Label_rechnung_aktuell.Caption:='0';
   form1.Label_Papier_aktuell.Caption:='0';
   savesettings;
end;

procedure TForm1.Button_start_actionClick(Sender: TObject);
begin
  if stop_op=true then begin
  stop_op:=false;
  ftp_trennen;
  abort;
  end
  else begin
  ftpverbinden;
  PrinServer_action;
  end;
 end;

procedure TForm1.Button_STOPClick(Sender: TObject);
begin
stop_op:=true;
end;

procedure TForm1.CheckBox_trayClick(Sender: TObject);
begin
if form1.CheckBox_tray.Checked=false then form1.TrayIcon1.Visible:=false else form1.TrayIcon1.Visible:=true;

end;

procedure TForm1.ffneprogramm1Click(Sender: TObject);
begin
form1.Visible:=true;
form1.WindowState:=wsnormal;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
savesettings;

addline('Close Tool');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

ts_log:=tstringlist.Create;
progress_running:=false;
stop_op:=false;
programmpfad:=extractfilepath(application.ExeName)+'\';
if tfile.Exists(programmpfad+'log.txt') then form1.Memo_Log.Lines.LoadFromFile(programmpfad+'log.txt');
addline('');
addline('Programm Start');

loadsettings;


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;

end;

procedure TForm1.FormResize(Sender: TObject);
begin
if form1.CheckBox_tray.Checked=true then if form1.WindowState=wsminimized then form1.Visible:=false;
end;

procedure TForm1.Loglschen1Click(Sender: TObject);
begin
form1.Memo_Log.Clear;
addline('Log cleaned');
end;

procedure TForm1.Timer1autominimizeTimer(Sender: TObject);
begin
form1.Timer1autominimize.Enabled:=false;
if form1.CheckBox_tray.Checked=true then form1.WindowState:=wsminimized;
end;

procedure TForm1.Timer1_dauer_CounterTimer(Sender: TObject);
begin
if stop_op=true then begin
 form1.ProgressBar_VerbindungsZeit.Position:=0;
 Timer1_dauer_Counter.Enabled:=false;
 ftp_trennen;
abort;
end;

if form1.ProgressBar_VerbindungsZeit.Position>0 then form1.ProgressBar_VerbindungsZeit.Position:=form1.ProgressBar_VerbindungsZeit.Position-1;
 form1.ProgressBar_VerbindungsZeit.Refresh;


 if progress_running=false then begin
  if form1.ProgressBar_VerbindungsZeit.Position=0 then begin

  if form1.IdFTP1.Connected=true then form1.IdFTP1.Disconnect;
  form1.Timer1_dauer_Counter.Enabled:=false;
  Addline_Debug('Beendigung des PrintServer Vorgang... ERFOLGREICH');
  form1.Button_start_action.Click;
  end;
 end else begin
  Addline_Debug('Warte auf beendigung des PrintServer Vorgang...');
 end;

end;




procedure TForm1.timer_restarterTimer(Sender: TObject);
begin
 if stop_op=false then PrinServer_action else begin
 form1.timer_restarter.Enabled:=false;
 ftp_trennen;
 end;
end;

procedure TForm1.TrayIcon1Click(Sender: TObject);
begin
form1.Visible:=true;
form1.WindowState:=wsnormal;
form1.BringToFront;
end;

procedure TForm1.TrayIcon1DblClick(Sender: TObject);
begin
form1.Visible:=true;
form1.WindowState:=wsnormal;
form1.BringToFront;
end;

end.

TurboMagic 2. Sep 2021 19:13

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Evtl. hilft MadExcept. Das hat einen Schalter zur Erkennung eingefrorener Hauptthreads und kann da evtl. sogar drauf reagieren oder zumindest weitere Diagnosedaten liefern.

Anderer Lösungsansatz: periodisch Verbindung trennen und neu aufbauen.

venice2 2. Sep 2021 19:19

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Das hört sich sehr nach nicht freigegebenen Handles an. (Den blöden Fehler hatte ich auch schon mal.)
Starte den Taskmanager.
Addiere zu dem Reiter "Details" die Spalte Handles und prüfe ob sich diese summieren.

Bedenke es gibt eine grenze der erlaubten Handles pro Anwendung.
Wird diese Überschritten stürzt die Anwendung ab bzw. wird sehr träge.

jaenicke 2. Sep 2021 20:35

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Ein Beispiel:
Du erstellst in PrinServer_action eine TStringList, hast aber keinen try..finally Block zur Freigabe drum. Tritt also ein Fehler auf oder springst du mit abort (ein paar Zeilen nach dem begin) raus, wird diese nicht wieder freigegeben.

Im Laufe vom Stunden summiert sich dann dieser nicht freigegebene Speicher und am Ende ist der voll und es geht gar nichts mehr. (Es kann auch sein, dass da noch mehr nicht freigegeben wird, ich habe nicht genau geschaut.) Und dann kann Indy auch nicht mehr richtig mit dem Server sprechen und es kann zu ganz komischen Fehlern kommen. Achte mal auf den Speicherverbrauch im Taskmanager, wenn der Fehler auftritt.

Davon unabhängig brauchst du das abort (sprich die stille Exception) in deinem Quelltext definitiv nicht. Zur Steuerung des Programm- und Schleifenablaufs gibt es mit Exit, Break, Continue usw. saubere Möglichkeiten. Das in Kombination mit Ressourcenschutzblöcken (try..finally) sollte Besserung bringen.

Delphi.Narium 3. Sep 2021 16:38

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Bei meinen HTTP-Klamotten kenn' ich das Problem in ähnlicher Form. Hier hat folgendes geholfen:
Delphi-Quellcode:
  // Grundsätzlich hinter jedes
  http.Disconnect(True);
  // noch ein.
  http.IOHandler.InputBuffer.Clear;
Behebt nicht 100% dieser Fehlersituationen, aber statt (teils mehrfach) täglich tritt der Fehler kaum noch monatlich auf.

Könnte daher einen Versuch wert sein.

gee21 6. Sep 2021 11:16

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Also vielen Dank für eure Hilfe. (ich habe heute mal die Handles und weitere Infos im Taskmanager anzeigen lassen).

@TurboMagic: Früher habe ich nach jedem überprüfen die Verbindung getrennt und wieder neu aufgebaut. damit aber die Ausdrucke möglichst schnell aus dem Drucker kommen, musste ich +/- alle 5 Sekunden neu zum server verbinden und dann gleich wieder trennen. damit also die ausdrucke noch schneller aus dem Drucker kommen und damit ich den Server nicht mit meinen Verbindung unnötig belaste, wollte ich die Verbindung so lange offen lassen bis knapp vor dem Server/Idle TimeOut erreicht ist. (NOOP und NAT KeepAlive haben immer Probleme gemacht, vermultich aber durch mein Unwissen ;) ).

@venice2: Ja du scheinst recht zu haben die Handles zählen aufwärts wie länger das Tool läuft.

@jaenicke: Vielen Dank für dein Beispiel. ich werde diese Sachen noch diese Woche anschauen und ausprobieren. Dann im Taskmanager vergleichen um zu sehen was wie hilft. Ich denke so sollte es dann klappen. :thumb:

@Delphi.Narium: Danke auch dir vielmals. Ich werde dein Tipp ganz am Schluss (in 1-2 Wochen) auch noch abändern um dann zu erkennen/lernen was diese Änderung hilft/bewirkt


VIELEN DANK bin jedes mal froh über eure Hilfe :thumb::thumb::thumb:

Delphi.Narium 6. Sep 2021 12:50

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Damit kannst Du feststellen, wieviele Handles Dein Prozess so "verbrät".
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 LogHandles(s : String);
var
  HandleCount: DWORD;
begin
  if GetProcessHandleCount(GetCurrentProcess, HandleCount) then
    Addline_Debug(Format('%s: Handles: %d',[s, HandleCount]);
end;
Wenn Du diesen Wert regelmäßig in Deinem Programm selbst prüfst und kontrollierst (Logfile oder sowas), kannst Du damit schonmal feststellen, ob es da einen Zuwachs gibt. (Und könnte einfacher sein (da auch unbeaufsichtigt möglich), als immer vorm Taskmanager zu sitzen und zuzuschauen ;-)

Bei 'nem Mitschreiben in 'ner Log-Datei kannst Du ggfls. sehen, welcher Wert hier vor dem Programmabsturz zuletzt protokolliert wurde.

Damit kannst Du dann ggfls. schonmal eine "Schmerzgrenze" ermitteln, ab der ggfls. mit 'nem Programmabsturz zu rechnen ist.

Wenn Du diesen Wert weißt, dann ziehe davon 'nen ordentlichen Puffer ab und steuere dann über diesen Wert einen automatischen Neustart des Programmes durch sich selbst. (Shellexecute (o. ä.) der eigenen Exe und die beendet sich dann.)

Achso:

Das ist natürlich nur ein Workaround, grundsätzlich wäre eine Lokalisierung und Bereinigung des Problemes vorzuziehen. Aber wie das so ist: Oft ist diese Anforderung leichter gesagt, als im realen Leben umgesetzt ;-)

Die obige Routine kannst Du in alle Routinen, bei denen ein (unerwarteter) Handlezuwachs möglich ist, einbauen:
Delphi-Quellcode:
// Am Anfang der Unit:
{$DEFINE HandleProtokoll}
// Am Anfang der relevanten Routinen:
{$IFDEF HandleProtokoll}LogHandles('Start Name der Funktion / Prozedure / Aufgabe ...');{$ENDIF}

// Am Ende der relevanten Routinen:
{$IFDEF HandleProtokoll}LogHandles('Ende Name der Funktion / Prozedure / Aufgabe ...');{$ENDIF}
Über den Kompilerschalter kannst Du das dann recht einfach aktivieren bzw. deaktivieren, ohne jeweils groß im Quelltext Änderungen vornehmen zu müssen.

Oder halt Dein Addline_Debug direkt um die Protokollierung der Handles ergänzen.

Ein Zuwachs im Speicherverbrauch ließe sich ähnlich protokollieren und ggfls. darauf reagieren.

gee21 6. Sep 2021 14:46

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Also ich hab jetzt heute bereits ein wenig getestet. Und werde wohl in den nächsten Tagen das ganze nocheinmal ganz genau anschauen um es dann in zukünftigen projekten direkt besser machen zu können.
(Vielen Dank delphi.narium für das beispiel mit dem handles auslesen, das wird mir bestimmt helfen in den nächsten tagen und ansonsten mache ich dann im notfall wirklich einen "workaround").

Heute sind mir folgende Fragen dazu durch den Kopf gegangen:
- Passiert bei Beispiel 1 ein Fehler oder auch nicht wird die Stringlist trotzdem immer freigegeben da sie nach dem except steht?
- ist Beispiel 2 korrekter / besser? da wird die Stringlist ebenfalls immer freigegeben da Finally immer ausgeführt wird egal ob fehler oder nicht?
- Weshalb sehe im im Taskmanager das mein Programm manchmal 2-7 Threads am laufen hat? Ich arbeite doch in diesem Tool gar nicht mit Threads? kommt das von IDFTP komponente? oder was macht den "automtisch" mehrere Threads. Ich sehe auch dass es dann manchmal von zb 7 Threads wieder runter auf 3 Threads geht.

Beispiel 1:
Delphi-Quellcode:
var
ts: tstringlist;
begin
try
ts:=tstringlist.create;
 mach das 1
 mach das 2
 mach das 3
 except
 // bei einem Fehler wird nichts gemacht. fährt aber nach except weiter? 
 end;
 ts.free;
end;
Beispiel 2:
Delphi-Quellcode:
var
ts: tstringlist;
begin
 try
 ts:=tstringlist.create;
 mach das 1
 mach das 2
 mach das 3
 finally
 //egal ob ein fehler passiert oder nicht stringlist wird wieder freigegeben.
 ts.free;
 end;

end;

venice2 6. Sep 2021 14:52

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Verwende bitte Delphi Tags dann kann man den Quelltext besser lesen.
Danke!

Du kannst except und finally combinieren.

Im Beispiel 1 wird ts.free; Nie Aufgerufen wenn ein anderer Fehler auftritt.

Sorry eine leere Fehlerbehandlung ist ein Nonsens.

Delphi-Quellcode:
except
// bei einem Fehler wird nichts gemacht. fährt aber nach except weiter?
end;

TomF 6. Sep 2021 15:09

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Ich bin jetzt nicht so der Profi - aber müsste das
Delphi-Quellcode:
ts:=tstringlist.create;
nicht vor dem try stehen, damit das Sinn ergibt ?

venice2 6. Sep 2021 15:11

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

Zitat von TomF (Beitrag 1494208)
Ich bin jetzt nicht so der Profi - aber müsste das
Delphi-Quellcode:
ts:=tstringlist.create;
nicht vor dem try stehen, damit das Sinn ergibt ?

Ja.

Delphi.Narium 6. Sep 2021 15:25

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Beispiel 3:
Delphi-Quellcode:
var
  ts : tstringlist;
begin
  ts := tstringlist.create;
  try
    try
      mach das 1
      mach das 2
      mach das 3
    except
      on e : Exception do begin
        MessageDlg(e.Message,mtError,[mbOk],0);
        // oder eine beliebige, aber garantiert funktionierende Variante der Fehlerprotokollierung.
        // aber nie ein -- wie venice2 schon sagt:
        //
        // Sorry eine leere Fehlerbehandlung ist ein Nonsens.
        // 
        // bei einem Fehler wird nichts gemacht. fährt aber nach except weiter? <-- Wenn das nicht klar ist, ist das zu verifizieren.
        //
        // Es ist immer sinnvoll zu wissen, ob an einer Stelle Fehler auftreten oder nicht.
        // Insbesondere dann, wenn nach nichtlokalisierbaren Fehlern gesucht wird.
        // Du hast schon ein
        Addline_Debug(Format('Nachvollziehbare Angabe zum Fehlerort: %s',[e.Message]));
        // Dann nutze es auch ;-)
      end;
    end;
  finally
    ts.free;
  end;
end;
Bezüglich des korrekten Hinweises von TomF zur Position des
Delphi-Quellcode:
ts:=tstringlist.create;
Kompilerwarnungen sind zu beachten und die Ursachen der Warnungen zu beheben, die kommen nicht "zum Spass". Es sind oft Kleinigkeiten, aber die summieren sich im Laufe von Stunden und momentan blickt niemand so recht, wo genau die Ursache des Problemes liegen könnte, mit dem Du Dich gerade rumschlagen musst. In so 'nem Fall kann Pingeligkeit durchaus zielführend sein ;-)

venice2 6. Sep 2021 15:29

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

wo genau die Ursache des Problemes liegen könnte, mit dem Du Dich gerade rumschlagen musst
Wenn man alles gelesen hat sollte einem Aufgefallen sein das sich die Handles summieren. ;)
Wie es mit dem Speicher aussieht darüber gab es noch keine Information.

Aber gut zusammen geschustert. :thumb:

Delphi.Narium 6. Sep 2021 15:42

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

Zitat von venice2 (Beitrag 1494211)
Zitat:

wo genau die Ursache des Problemes liegen könnte, mit dem Du Dich gerade rumschlagen musst
Wenn man alles gelesen hat sollte einem Aufgefallen sein das sich die Handles summieren. ;)
Wie es mit dem Speicher aussieht darüber gab es noch keine Information.

Aber gut zusammen geschustert. :thumb:

Wir wissen, dass sich die Handles aufsummieren!

Aber warum sie sich aufsummieren ist (zumindest mir) bisher nicht klar geworden.

Meiner Meinung nach kennen wir eine Folge eines Fehlers (oder möglicherweise mehrerer Fehler?), aber noch nicht die Ursache(n) ;-)

venice2 6. Sep 2021 15:49

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

Aber warum sie sich aufsummieren ist (zumindest mir) bisher nicht klar geworden.
Liegt an den Quelltext Schnipseln die wir zur Verfügung gestellt bekommen. (Er wird wohl seine gründe haben warum er nicht mehr veröffentlichen kann)
Du siehst es aber Richtig. Glaskugel :glaskugel: haben wir leider nicht.
Warten wir mal was da noch so kommt.

gee21 6. Sep 2021 15:51

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Anscheinend verstehe ich ziemlich viel nicht. :oops:

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
Delphi-Quellcode:
ts:=tstringlist.create;
vor den
Delphi-Quellcode:
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!

Delphi.Narium 6. Sep 2021 17:18

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

Zitat von gee21 (Beitrag 1494214)
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:off' then form1.CheckBox_tray.Checked:=false;
   if ts.Strings[15]='autostart:off' then form1.CheckBox_autostart.Checked:=false;
   if ts.Strings[16]='debug:off' then form1.CheckBox_DebugLog.Checked:=false;
   if ts.Strings[17]='hidecmd:off' then 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.

gee21 6. Sep 2021 17:42

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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. )

Delphi.Narium 6. Sep 2021 18:39

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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.

gee21 7. Sep 2021 16:36

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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

Delphi.Narium 7. Sep 2021 17:33

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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

Delphi-Quellcode:
ts_log.SaveToFile(programmpfad + 'log_OLD.txt');


in

Delphi-Quellcode:
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.

venice2 7. Sep 2021 19:32

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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.

Delphi-Quellcode:
procedure PrinServer_action;

soll bestimmt
Delphi-Quellcode:
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

gee21 8. Sep 2021 09:30

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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

Uwe Raabe 8. Sep 2021 10:19

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Nur eine Bitte: Kannst du uns die Scroll-Orgien ersparen und einfach die PAS-Datei als Anhang mitgeben?

Delphi.Narium 8. Sep 2021 10:41

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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;

UntoterGeist 15. Sep 2021 19:11

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
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.

gee21 22. Sep 2021 14:10

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

Zitat von UntoterGeist (Beitrag 1494925)
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.

Ja also ich habe den Speicherort Lokal (c:\) weil ich früher auch schon Probleme bekommen habe als ich es aus dem NetzLaufwerk startete. Und ich habe ebenfalls die .exe Manuell durch doppelklick gestartet da ich auch schon diverse Probleme / unterschiede / verzögerungen im Debug Modus von Delphi hatte.

Ich habe nun einiges abgeändert und zudem die ganze FTP Geschichte in einen eigenen Thread gesetzt. Dieser Thread startet sich am ende von selbst neu und falls er das nicht mehr tut (warum auch immer) so startet ein Timer den Thread manuell neu.

Ich VERMUTEes hing vorher irgendwie damit zusammen das ich die Dateien unbenannt habe und dann schnellstmöglich löschen wollte. Den diese beiden Sachen habe ich nun weggelassen trotz das sie eigentlich im try/except standen... Dateien werden nun nicht mehr unbenannt und werden auch erst bei Programmende gelöscht. Seitdem läuft das Programm nun :thumb:

Obwohl meine Lösung / Programmierung bestimmt sehr amateurhaft ist, poste ich hier noch der funktionierende Code um dieses Thema hier abschliessen zu können.

VIELEN DANK AN ALLE für eure Hilfe. :thumb:

http://gee.myasustor.com:8642/mainFinal.pas

venice2 22. Sep 2021 15:57

AW: Programm stürzt nach mehren Stunden Laufzeit ab.
 
Na ja die ganzen Form1 sind immer noch drin.. Warum?
Du befindest dich doch schon innerhalb der Classe Tform1
Lösche den Kram doch einfach. ;)

das
Delphi-Quellcode:
ts.Add(Form1.Edit_Host.Text);

zu
Delphi-Quellcode:
ts.Add(Edit_Host.Text);

usw..
Überprüfe den gesamten Quelltext nochmal

Das tform1 zu TForm1 um eine einheitliche Schreibweise zu erhalten (aber nicht Lebenswichtig nur als Tip nebenbei)
Zumindest macht es den Quelltext lesbarer.

Delphi-Quellcode:
procedure tform1.savesettings;
var
ts: tstringlist;
begin
  ts := tstringlist.Create;
  try
        try
            ts.Add(Form1.Edit_Host.Text);
            ts.Add(Form1.Edit_User.Text);
            ts.Add(Form1.Edit_Password.Text);
            ts.Add(Form1.Edit_Port.Text);
            ts.Add(Form1.Edit_LS.Text);
            ts.Add(Form1.Edit_RE.Text);
            ts.Add(Form1.Edit_ET1.Text);
            ts.Add(Form1.Edit_ET2.Text);
            ts.Add(Form1.Edit_verbindungsDAUER.Text);
            ts.Add(form1.Edit_pause_pro_durchgang.Text);
            ts.Add(Form1.Edit_pause_wiederverbindung.Text);
            ts.Add(Form1.Edit_druck_pauseDEA.Text);
            ts.Add(Form1.Edit_rechnungen_max.Text);
            ts.Add(Form1.Edit_papierfach_max.Text);
            ts.Add('tray:off');
            ts.Add(IfThen(CheckBox_autostart.Checked, 'autostart:on','autostart:off'));
            ts.Add(IfThen(CheckBox_DebugLog.Checked, 'debug:on','debug:off'));
            ts.Add(IfThen(CheckBox_verstecke_cmd.Checked, 'hidecmd:on','hidecmd:off'));
            ts.Add(inttostr(papierfach));
            ts.Add(inttostr(rechnungsfach));
            ts.Add('');
            ts.SaveToFile(programmpfad + 'settings.txt');
        except
            on e: exception do form1.addline('Fehler beim speichern der Einstellungen: ' +e.Message);
        end;
    finally
        ts.free;
    end;
end;


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