![]() |
Re: Programm abrupt beenden ohne Memory Leaks
Hallo.
Zitat:
Zitat:
Delphi-Quellcode:
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
TProgress = class(TInterfacedObject, IDECProgress)
procedure Process(const Min, Max, Pos: Int64); stdcall; constructor Create; destructor Destroy; override; end; ![]() 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:
Zitat:
Gruß blackdrake |
Re: Programm abrupt beenden ohne Memory Leaks
Die Sache ist wirklich sehr einfach.
Delphi-Quellcode:
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.
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; 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
Delphi-Quellcode:
.
if Terminated then Abort;
Gruß Hagen |
Re: Programm abrupt beenden ohne Memory Leaks
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 |
Re: Programm abrupt beenden ohne Memory Leaks
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 |
Re: Programm abrupt beenden ohne Memory Leaks
Hallo.
Bei DEC hat es mit abort; und dem Progress-Interface sehr gut funktioniert. Aber bereits bei ZLib klappt es gar nicht:
Delphi-Quellcode:
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. :(
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. 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 |
Re: Programm abrupt beenden ohne Memory Leaks
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 |
Re: Programm abrupt beenden ohne Memory Leaks
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:
Um die ZLib Aktionen abzubrechen, habe ich die Progress-Methode in eine eigene Klasse gepackt:
procedure TMainForm.kazip_add(Sender: TObject; ItemName: string);
begin application.processmessages; if WartenForm.FAbort then sysutils.Abort; end;
Delphi-Quellcode:
Die Anwendung:
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;
Delphi-Quellcode:
Danke für euere Ratschläge! :-D
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; Gruß blackdrake [edit=SirThornberry]Code-Highlighting korrigiert - Mfg, SirThornberry[/edit] |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:46 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