AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Programm abrupt beenden ohne Memory Leaks
Thema durchsuchen
Ansicht
Themen-Optionen

Programm abrupt beenden ohne Memory Leaks

Ein Thema von blackdrake · begonnen am 10. Sep 2007 · letzter Beitrag vom 15. Sep 2007
Antwort Antwort
Seite 2 von 2     12   
blackdrake

Registriert seit: 21. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#11

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 06:47
Hallo.

Zitat von negaH:
was auch ziemlich dämlich wäre da du damit diese Source abhängig machst von einer rießigen VCL
Ja, ich weiß, dass eine Unit nicht auf Forms/Application zugreifen sollte. Ich habe ja nicht gesagt, dass die Units dieses Feature bräuchten oder dass es sinnvoll sei, dass unabhängige Units application.terminated berücksichtigen.

Zitat von negaH:
1.) ein Variable FAbort:Boolean in dein TForm einbauen
2.) deine Methode die alles macht per try except Block schützen und auf EAbort reagieren
3.) einen Button auf's TForm der FAbort auf TRUE setzt
4.) in deiner Methode .Process() bei FAbort = TRUE die Prozedure Abort aufrufen
Danke für die Tipps. Die Progress-Klasse verwende ich bereits folgendermaßen, um den Fortschritt in einer Gauge darzustellen:

Delphi-Quellcode:
TProgress = class(TInterfacedObject, IDECProgress)
  procedure Process(const Min, Max, Pos: Int64); stdcall;
  constructor Create;
  destructor Destroy; override;
end;
Ich verstehe aber nicht ganz, was du mit Punkt #2 meinst. Wie soll ich auf FAbort reagieren? Wie bekomme ich es hin, dass FAbort dazu führt, dass TCipher_xyz.EncodeFile etc. abbricht? Die einzigste Methode die mir einfällt, ist den Stream freizugeben und die Exceptions zu verschlucken (siehe Idee #1 ganz oben). Das scheint mir aber ziemlich unprofessionell, weil ich Speicher freigebe, der noch genutzt wird, um so die laufende Aktion zu terminieren. Außerdem verschlucke ich Exceptions (siehe http://www.delphipraxis.net/internal...=206667#206667 , Sünde #1). Wie soll ich anders auf FAboirt reagieren?

Das mit den TStream Descants verstehe ich auch nicht ganz. Das es eine Callback-Funktion gibt, verstehe ich. Aber wie gehe ich bei diesem Descants bei einer Termination vor? Gebe ich dann auch den Stream einfach frei und verschlucke die Exceptions, wenn ich eine Termination des Programmes wünsche?

Sobald ich wieder zuhause bin, probiere ich mal mit Delphi das ganze aus.

Nachtrag:

Zitat von negaH:
1.) eine OnClick() muß reentrant werden, zb. durch Disablen des Buttons oder durch ein Locking
Verstehe leider nicht, was du meinst. Im ersten Beitrag habe ich Button1OnClick als Beispiel einer Benutzeraktion verwendet.

Zitat von negaH:
2.) OnCloseQuery des Form benutzen und dort erst dann TRUE zurückliefern wenn deine Berechnungsfunktion/Loop beendet wurde.
Erstmal muss ich es schaffen, die laufende DEC/KaZIP/ZLIB Aktion zuende zu führen. Dann muss ich gucken, wie ich ermitteln kann, ob das Programm gerade beschäftigt ist, oder nicht.

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#12

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 07:10
Die Sache ist wirklich sehr einfach.

Delphi-Quellcode:
type
  TForm1 = class(TForm, IDECProgress)
    procedure ButtonClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    FAction: Integer;
    procedure Process(const Min,Max,Pos: Int64); stdcall;
  end;


procedure TForm1.ButtonClick(Sender: TObject);
begin
  if FAction = 0 then
  try
    Inc(FAction);
    Button.Caption := 'Abort';
    try
      with TCipher_Blowfish.Create do
      try
        Init(...);
        EncodeFile(FileName, FileName, Self);
      finally
        Free;
      end;
    except
      on E: Exception do
        if E is EAbort then ShowMessage('aborted by User')
          else reraise;
    end;
  finally
    FAction := 0;
    Button.Caption := 'Start';
  end else Inc(FAction);
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FAction = 0;
  if not CanClose then Inc(FAction);
end;

procedure TForm1.Process(const Min,Max,Pos: Int64);
begin
  Application.ProcessMessages;
  if FAction > 1 then Abort;
end;
Wenn FAction == 0 ist dann kann der Anwender beim KLick auf Button eine Datei verschlüsseln. In diesem Moment wird FAction == 1 sein. Über die .Process() Methode, quasi eine Callback, ruft nun der Cipher periodisch auf. Darin wird Application.ProcessMessages; aufgrufen, der User kann also den Button nochmals drücken, dessen Caption zeigt ja Abort an. Drück er nochmals drauf dann landet man im .OnClick() auf Grund der Anfrag if FAction = 0 then eben nicht in der Verschlüsselung sondern beim Inc(FAction). Aus FAction == 1 wird dann FAction > 1. Sobald nun FAction > 1 ist wird in der .Process() Callback eine Exception ausgelösst, eben Abort eine stille Exception. Da der Cipher intern dieses .Process() aufruft und wir dadrinnen eine Exception auslössen wird Cipher.EncodeFile() verlassen, abgebrochen. Man landet dann im finally end Block und von dort im except end Block in dem wir auf EAbort gezielt reagieren.

So sollte man mit Schutzblöcken und gezielter Exceptionbehandlung in Delphi arbeiten.

In OnCloseQuery fragen wir noch ab ob gerade FAction = 0 ist, also keine Aktion gerade läuft. Wenn nicht dann lösen wir auch hier einen Abort indirekt aus. Der Benutzer muß dann meistens 2 mal den Closebutton der Titelzeile klicken.

Ziemlich simpel die Sache, oder ?

Wenn du jetzt statt dem TForm ein TThread benutzt dann müsste nur FAction entfernt werden und .Process() kein Application.ProcessMessages drinnen stehen. Dafür aber if Terminated then Abort; .

Gruß Hagen
  Mit Zitat antworten Zitat
blackdrake

Registriert seit: 21. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#13

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 07:17
Hallo Hagen.

Danke für die schnelle und ausführliche Antwort. Ich probiere es zuhause gleich aus. Anschließend versuche ich es noch mit den restlichen ZIP/Compress-Funktionen mittels der TStream-Descants und melde mich, ob es funktioniert hat oder nicht.

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#14

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 07:25
Also DEC hält sich strikt intern daran und verwendet immer schön try finally end; Schutzblöcke wenn es Resource alloziert hat. Es ist jetzt für dich nur wichtig nachzuschauen ob das die beiden anderen Libs auch so handhaben. Wenn ja, können bei dieser Methode keine Memoryleaks entstehen, der User hat die Kontrolle die Aktion abzubrechen, und die Anwendung wird ganz sauber beendet so wie vorgesehen.

Keine Holzhammer Methode mehr notwendig indem du einen Thread oder Prozess abschießt. Auch die Anwendung bleibt weiter quasi bedienbar. Einzigstes Problem, sollte der Anwender zb. in die Titelzeile klicken und die Maustaste gedrückt lassen so wird der aktuelle Verschl. Vorgang temporär angehalten. Das kannst du nur mit Threads umgehen.

Übrigens durch die

if FAcxtion = 0 then

Abfrage wird die OnClick Methode eben reentrant, sie kann quasi rekusiv merhfach afgerufen werden.

Gruß Hagen
  Mit Zitat antworten Zitat
blackdrake

Registriert seit: 21. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#15

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 22:10
Hallo.

Bei DEC hat es mit abort; und dem Progress-Interface sehr gut funktioniert.

Aber bereits bei ZLib klappt es gar nicht:

Delphi-Quellcode:
procedure TForm1.zlib_progress(Sender: TObject; tag: Integer; Percent: Integer);
begin
  pbr_progress.position := Percent * pbr_progress.max div 100;
  application.processmessages;
  if application.Terminated then SysUtils.abort;
end;

procedure CompressFile(InputFileName, OutputFileName: string; Progress: TOnProgress);
var
  CompressInputStream: TStreamProgressAdapter; // TFileStream
  CompressOutputStream: TFileStream;
  CompressionStream: ZLib.TCompressionStream;
begin
  try

  CompressInputStream := TStreamProgressAdapter.Create(TFileStream.Create(InputFileName, fmOpenRead), 0);
  CompressInputStream.OnProgress := Progress;
  try
    CompressOutputStream := TFileStream.Create(OutputFileName, fmCreate);
    try
      CompressionStream := TCompressionStream.Create(clMax, CompressOutputStream);
      try
        CompressionStream.CopyFrom(CompressInputStream, CompressInputStream.Size);
      finally
        CompressionStream.Free;
      end;
    finally
      CompressOutputStream.Free;
    end;
  finally
    CompressInputStream.Free;
  end;

  except
    on E: Exception do
      if not (E is EAbort) then raise;
  end;
end;

begin
  CompressFile('...', '...', Form1.zlib_progress);
end.
Ich habe bereits versucht, den Adapter alternativ für CompressOutputStream oder CompressionStream zu verwenden. Bei CompressionStream gab es eine Stream-Operation-Exception. CompressInputStream müsste jedoch laut des Beispielprogrammes die korrekte Wahl sein. Wenn ich jetzt meine Anwendung schließe, scheint es einen Dead-Lock zu geben. Die Progressbar bewegt sich nicht mehr, aber die Methode CompressFile() wird nicht über die Exception verlassen, sondern bleibt aktiv.

Die Methode CompressFile() würde erst durch die EAbort-Exception verlassen werden, wenn ich direkt vor CompressionStream.CopyFrom ein "abort;" setzen würde. Irgendwie wird das Abort nicht weitergereicht.

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#16

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 23:06
Ach mann, nimm das blöde Application.Terminate raus und baue die ButtonOnClick() Methode wie in meinem obigen Beispiel auf. Statt .Process() vom IDECPrograess Interface dann das Gleiche mit dem OnProgress Event des Streams. Im ButtonClick() dann statt meinem TCipher Object eben deine CompressFile() Funktion aufrufen.

Gruß Hagen
  Mit Zitat antworten Zitat
blackdrake

Registriert seit: 21. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#17

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 15. Sep 2007, 22:12
Hallo.

Habe jetzt endlich wieder Zeit gefunden, mich mit dem Problem zu beschäftigen und konnte es lösen. Außerdem verstehe ich nun die genaue Funktionsweise von Abort(). Der Aufruf von Abort() führt zu der Exception EAbort, die dann durchgeschleift wird und alle Aktionen der Methoden in der Aufrufhierarchie beendet bzw. zu deren except Teil der try..except's führt. In meinem letzten Post war die Progress-Methode nicht innerhalb der Hierarchie und deswegen schlug Abort() fehl (glaube ich).

@negaH: Bei der Entwicklung vermeide ich es in der Regel, zu viele Baustellen auf einmal aufzumachen. Deswegen war in meinem vorherigen Code noch Application.Terminated anstatt WartenForm.FAbort. Bei meinem Programm hat der Verschlüsselungsbutton keine Re-Entranz, da das Form beim Klicken unsichtbar wird und ein Wartenformular mit einem Abbrechenbutton erscheint. Da Start/Stop in sofern auf 2 Buttons aufgeteilt ist, ist FAction unnötig.

Für die, die eventuell das selbe Problem haben, füge ich die Lösung nun an.

KaZIP selbst stellt keine Streams zur Verfügung. Doch nach kurzer Überlegung mit ich auf die Idee gekommen, Abort() in die On(De)Compress/OnAdd-Methoden von KaZIP zu packen:

Delphi-Quellcode:
procedure TMainForm.kazip_add(Sender: TObject; ItemName: string);
begin
  application.processmessages;
  if WartenForm.FAbort then sysutils.Abort;
end;
Um die ZLib Aktionen abzubrechen, habe ich die Progress-Methode in eine eigene Klasse gepackt:

Delphi-Quellcode:
type
  TZLIBProgress = class(TObject) // Ist die Ableitung von TObject eigentlich hierfür sinnvoll?
  public
    procedure Progress(Sender: TObject; tag: Integer; Percent: Integer);
    constructor Create;
    destructor Destroy; override;
  end;

{ ************************ TZLIBProgress ************************ }

procedure TZLIBProgress.Progress(Sender: TObject; tag: Integer; Percent: Integer);
begin
  WartenForm.pbr_progress.Position := Percent * WartenForm.pbr_progress.max div 100;
  Application.ProcessMessages;
  if WartenForm.FAbort then SysUtils.abort;
end;

constructor TZLIBProgress.Create;
begin
  inherited Create;
end;

destructor TZLIBProgress.Destroy;
begin
  inherited Destroy;
end;
Die Anwendung:

Delphi-Quellcode:
function DeCompressFile(InputFileName, OutputFileName: string): boolean;
var
  CompressInputStream: TFileStream;
  CompressOutputStream: TStreamProgressAdapter;
  DeCompressionStream: ZLib.TDeCompressionStream;
  CProgress: TCommonProgress;

  Buf: array[0..4095] of Byte;
  Count: Integer;
begin
  result := true;
  CompressInputStream := TFileStream.Create(InputFileName, fmOpenRead);
  try
    CompressOutputStream := TStreamProgressAdapter.Create(TFileStream.Create(OutputFileName, fmCreate), 0);

    try
      DecompressionStream := TDecompressionStream.Create(CompressInputStream);
      try
        CProgress := TCommonProgress.Create;
        try
          try
            CompressOutputStream.OnProgress := CProgress.Progress;
            while true do
            begin
              Count := DeCompressionStream.Read(Buf[0], SizeOf(Buf));
              if Count = 0 then
                break
              else
                CompressOutputStream.Write(Buf[0], Count);
            end;
          except
            on E: Exception do
            begin
              result := false;
              if not (E is EAbort) then raise;
            end;
          end;
        finally
          CProgress.Free;
        end;
      finally
        DecompressionStream.Free;
      end;
    finally
      CompressOutputStream.Free;
    end;
  finally
    CompressInputStream.Free;
  end;
end;

function CompressFile(InputFileName, OutputFileName: string): boolean;
var
  CompressInputStream: TStreamProgressAdapter;
  CompressOutputStream: TFileStream;
  CompressionStream: ZLib.TCompressionStream;
  CProgress: TCommonProgress;
begin
  result := true;
  CompressInputStream := TStreamProgressAdapter.Create(TFileStream.Create(InputFileName, fmOpenRead), 0);
  try
    CompressOutputStream := TFileStream.Create(OutputFileName, fmCreate);
    try
      CompressionStream := TCompressionStream.Create(clMax, CompressOutputStream);
      try
        CProgress := TCommonProgress.Create;
        try
          try
            CompressInputStream.OnProgress := CProgress.Progress;
            CompressionStream.CopyFrom(CompressInputStream, CompressInputStream.Size);
          except
            on E: Exception do
            begin
              result := false;
              if not (E is EAbort) then raise;
            end;
          end;
        finally
          CProgress.Free;
        end;
      finally
        CompressionStream.Free;
      end;
    finally
      CompressOutputStream.Free;
    end;
  finally
    CompressInputStream.Free;
  end;
end;
Danke für euere Ratschläge!

Gruß
blackdrake

[edit=SirThornberry]Code-Highlighting korrigiert - Mfg, SirThornberry[/edit]
Daniel Marschall
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


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 01:36 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