![]() |
Programm abrupt beenden ohne Memory Leaks
Hallo.
Mein Programm benutzt Zipping und Verschlüsselungsroutinen. Dafür nutze ich die Units KAZip, DEC (enthält viele große Units) und ZLib (von Borland). Es kann vorkommen, dass der Endbenutzer auf die Idee kommt, ein großes Verzeichnis oder ein ganzes Laufwerk zu zippen und diese Aktion wieder abbrechen möchte, da sie zu lange dauert. Leider beachten die oben genannten Units nicht Application.Terminated, weswegen ich ziemliche Probleme bei der Terminierung der Applikation habe. Da mein Programm keine Enddateien überschreibt (sondern vorher im Tempverzeichnis puffert) und bei meinem Programm keine Benutzerdaten bzw. getane Arbeit verloren gehen können, würde ich gerne dem Benutzer die Möglichkeit geben, das Programm ohne Strg+Alt+Entf zu beenden. Zur Demonstration sei folgende Methode gegeben:
Delphi-Quellcode:
Diese Funktion soll durch die Unendlichschleife eine lang andauernde ZIP/Verschlüsselungsfunktion darstellen. Es sei gegeben, dass ich auf diese Funktion nicht zugreifen oder sie verändern kann (ich habe keine Lust, DEC, KAZIP und ZLIB für application.terminated umzuschreiben).
procedure kann_lange_dauern;
begin repeat application.processmessages; until false; end; Ich habe folgende Ideen gehabt und versucht: 1. Idee: Ressourcen freigeben und Exceptions verschlucken Im Prinzip hat es funktioniert, aber es ist sehr unprofessionell, da ich Exceptions verschlucke.
Delphi-Quellcode:
Da hier Exceptions verschluckt werden und es interne Exceptions gibt, ist diese Variante extrem unprofessionell.
var
mystream: TStream; procedure TForm1.Button1Click(Sender: TObject); begin try // Wird bei Programmende durch eine Exception beendet, da wir MyStream freigeben. kann_lange_dauern(mystream); except end; end; procedure TForm1.OnClose(Sender: TObject; var Action: TCloseAction); begin try mystream.free; except end; end; 2. Idee: Threads verwerden Jetzt bin ich auf die Idee gekommen, Threads zu verwenden. Ich habe für jede ZIP/DEC-Aktion eine Thread-Klasse geschrieben, die sich den Thread bei Programmende automatisch per ExitThread bzw ExitProcess killt. Leider entstehen durch das abrupte Abbrechen des Threads MemoryLeaks.
Delphi-Quellcode:
3. Idee: ExitProcess verwenden
type
TKaZipExtract = class(TThread) private FExecuteFinished: boolean; FKaZIP: TKaZIP; FZipFile: string; FDestination: string; protected procedure Execute; override; public constructor Create; virtual; function RunAndFree(const KaZIP: TKaZIP; const ZipFile, Destination: string): boolean; end; { --- --- --- TKaZipExtract --- --- --- } procedure TKaZipExtract.Execute; begin FKaZIP.close; if fileexists(FZipFile) then begin FKaZIP.Open(FZipFile); try FKaZIP.ExtractAll(FDestination); finally FKaZIP.Close; end; end; FExecuteFinished := true; end; constructor TKaZipExtract.Create; begin inherited create(true); end; function TKaZipExtract.RunAndFree(const KaZIP: TKaZIP; const ZipFile, Destination: string): boolean; begin FKaZIP := KaZIP; FZipFile := ZipFile; FDestination := Destination; FExecuteFinished := false; try if Suspended then Resume; repeat Application.ProcessMessages; until FExecuteFinished or Application.Terminated or Terminated; result := not (Application.Terminated or Terminated); if not result then ExitProcess(handle); // Vorher: ExitThread finally free; end; end; function KaZIPExtract(const KaZIP: TKaZIP; const ZipFile, Destination: string): boolean; var tmp: TKaZipAdd; begin tmp := TKaZipAdd.Create; result := tmp.RunAndFree(KaZIP, ZipFile, Destination); end; Ich kam auf die Idee, das gesamte Programm durch ExitProcess bei dem Event TForm1.OnClose zu beenden. Ich habe außerdem geprüft, ob MemoryLeaks dabei entstehen. Seltsamerweiße hat mir weder der BDS 2006 Memory Manager noch FastMM ein MemoryLeak gemeldet. Test mit halt(), das ExitProcess gleicht.
Delphi-Quellcode:
Test mit ExitProcess:
procedure TForm1.OnCreate(Sender: TObject);
begin ReportMemoryLeaksOnShutDown := true; // ab BDS 2006 TObject.Create; // Provozierter Memory Leak end; procedure TForm1.Button1Click(Sender: TObject); begin kann_lange_dauern(); end; procedure TForm1.OnClose(Sender: TObject; var Action: TCloseAction); begin halt; // Warnung bzgl. Memory Leaks end;
Delphi-Quellcode:
Da ich bei ExitProcess keine Memory-Leak-Warnung erhielt, dachte ich, dass diese WinAPI-Methode den Arbeitsspeicher automatisch aufräumt. Auch bei MSDN (
procedure TForm1.OnCreate(Sender: TObject);
begin ReportMemoryLeaksOnShutDown := true; // ab BDS 2006 TObject.Create; // Provozierter Memory Leak end; procedure TForm1.Button1Click(Sender: TObject); begin kann_lange_dauern(); end; procedure TForm1.OnClose(Sender: TObject; var Action: TCloseAction); begin ExitProcess(ExitCode); // KEINE Warnung bzgl. Memory Leaks end; ![]() 4. Idee: Den Benutzer fragen, ob er MemoryLeaks will Da ich nun absolut keine Ideen habe, muss ich wohl den Benutzer beim OnCloseQuery-Ereignis fragen, ob er den laufenden Prozess wirklich beenden möchte und das Risiko eingeht, dass Rückstände im Arbeitsspeicher bleiben könnten.
Delphi-Quellcode:
Hat irgendjemand eine bessere Idee, wie ich mein Programm abrupt ohne Memory Leaks beenden kann? :|
procedure TForm1.OnCloseQuery(Sender: TObject; var CanClose: Boolean);
var res: integer; begin if programm_arbeitet then begin res := Application.MessageBox('Warnung', 'Das Programm arbeitet derzeit. Möchten Sie den Prozess beenden und die Gefahr auf sich nehmen, dass Rückstände im Arbeitsspeicher verbleiben?', MB_YESNO + MB_ICONEXCLAMATION); CanClose := res = ID_YES; end else begin CanClose := true; end; end; procedure TForm1.OnClose(Sender: TObject; var Action: TCloseAction); begin ExitProcess(ExitCode); end; Gruß blackdrake |
Re: Programm abrupt beenden ohne Memory Leaks
über memory-leaks solltest du dir unter xp und vista keine Gedanken machen. denn sobald dein Prozess beendet ist/wird werden auch alle Ressourcen welche dein Programm in Anspruch genommen hat mit frei gegeben.
|
Re: Programm abrupt beenden ohne Memory Leaks
Hallo.
Habe ich mir schon gedacht. Aber das Programm sollte ja auch unter 9x laufen, ohne den Arbeitsspeicher zuzumüllen. Und Memory Leaks sollte man stets vermeiden ( ![]() Gruß blackdrake |
Re: Programm abrupt beenden ohne Memory Leaks
100MB bei Win9X im Arbeitspspeicher war aber ein riesen Kramp, ich glaub da hängt das Programm so schon sehr. Aber Löglich das es noch Leute gibt die auf MemoryLeaks extrem achten.
|
Re: Programm abrupt beenden ohne Memory Leaks
Zitat:
hier noch einer :zwinker: :cheers: :mrgreen: |
Re: Programm abrupt beenden ohne Memory Leaks
Zitat:
Wo finde ich heraus, welche Windows Versionen denn genau dieses Feature mit dem RAM-Freigeben unterstützen? Hat Windows Server 2003 dieses Feature? Und hat NT dieses Feature echt nicht gekannt? Ist die Behauptung korrekt, dass alle Windows-Versionen >= 5.x ( ![]() Natürlich ist es ein wenig unprofessionell, wenn das Programm bei 9x nachfragt, ob der Benutzer beim Beenden ein MemoryLeak in Kauf nehmen will. Würde ich eine solche Frage gestellt bekommen, würde ich das bedenklich finden. Gruß blackdrake |
Re: Programm abrupt beenden ohne Memory Leaks
Zitat:
auch wenn das eine schöne Sache ist, finde ich, dass es ein schönerer Stol ist, wenn man sein Zeugs, was man erstellt hat, wieder freigibt. |
Re: Programm abrupt beenden ohne Memory Leaks
Zitat:
|
Re: Programm abrupt beenden ohne Memory Leaks
alle im den DEC Klassen enthaltenen Methoden die länger dauern können, zb.
THash_XYZ.CalcStream(), .CalcFile() TCipher_XYZ.Encode/DecodeStream/File unterstützen einen Progress -> IDECProgress Interface. Du deklarierst in deinem TForm zb. sowas
Delphi-Quellcode:
und kannst du dein Application.ProcessMessages reinbauen. Übrgeben bekommst du Min,Max und Pos das sind die Größen und Datezeiger/Streamtzeiger.
type
TForm1 = class(TForm, IDECProgress) procedure Process(const Min,Max,Pos: Int64); stdcall; end; Wenn du dem Anwender die Möglichkeit geben möchtest eine Aktion zu unterbrechen dann musst du 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 Es ist also so das alle anderen deiner Tools das eventuell nicht unterstützen, aber DEC tut es mit Sicherheit ermöglichen und das sehr einfach indem du nur eine Methode eines Interfaces implementierst. Beim Aufruf einer der obigen DEC Klassen Methoden übergibts du dann einfach als letzten Parameter Self. ![]() Gruß Hagen |
Re: Programm abrupt beenden ohne Memory Leaks
Zitat:
Normalerweise wird ein Programmierer eben nicht über Application.ProcessMessages eine parallele Abarbeitung des Messagesques anstoßen und deshalb ist der Aufruf von Application.Terminated meistens auch überflüssig. Du musst halt bedenken, Application.ProcessMessages ist es die in deinem TForm beim Button1.Click die OnClick() Methode aufruft. Wird darin nun selber in einer Schleife Application.ProcesssMessages aufgerufen dann hast du defakto sowas wie ein fast fertige "Endlosrekursion" programmiert. Nur ist diese nicht so offensichtlich da hier VCL+Events+Messages von Windows involviert sind. Hast du zb. eben nicht deinen Button disabled so kann der Benutzer diese erneut drücken und damit landet man wieder im OnClick() obwohl du noch im OnClick() in der .ProcessMessages Loop drinnen bist. Es ist nicht reentrant in diesem Moment und das hat arge Seiteneffekte. Zb. du greifts in deiner OnClick() + .Processmessages Loop auch auf Objecte des TForms zu. Nun, wenn .ProcessMessages aufgerufen wird un der Benutzer drückt den Close-Button des Fensters in der Titelleiste dann wird das TForm zerstört. Aber der Programcounter ist immer noch in deiner Schleife die auf Daten dieses Forms zugreift, bumms Exception. Das was du also vorhast ist im Grunde schlecht, aber relativ gesehen noch Bedienersicher zu bekommen. 1.) eine OnClick() muß reentrant werden, zb. durch Disablen des Buttons oder durch ein Locking 2.) OnCloseQuery des Form benutzen und dort erst dann TRUE zurückliefern wenn deine Berechnungsfunktion/Loop beendet wurde. die beste Methode wären aber Threads und solche Callbacks wie in meinem DEC. Man startet einen Thread und alle längerdauernen Operationen rufen eine Callback wie im DEC auf. In dieser wird überprüft ob der Thread terminiert werden soll, also Self.Terminated, und wenn ja dann wird eine stille Exception EAbort ausgelösst. Im Mainpart des Threads wurde alles per try except gekapselt. Die ZLib, KAZIP Methoden die per TStream Descants arbeiten kannst du ohne Änderungen am Originalsource mit einer solchen Callback versehen. Dazu gibts hier im Forum einen TStreamProgressAdapter oä. Dieser wird quasi zwischen den eigentlichen Stream zu deinen Dateien zwischen-geschaltet und als Parameter diesen Bibliotheken übergeben. Beim DEC geht das auch ist aber nicht notwendig. ![]() Gruß Hagen |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:14 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 by Thomas Breitkreuz