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 1 von 2  1 2      
blackdrake

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

Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 19:07
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:
procedure kann_lange_dauern;
begin
  repeat
    application.processmessages;
  until false;
end;
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).

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:
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;
Da hier Exceptions verschluckt werden und es interne Exceptions gibt, ist diese Variante extrem unprofessionell.

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:
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;
3. Idee: ExitProcess verwenden

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:
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;
Test mit ExitProcess:

Delphi-Quellcode:
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;
Da ich bei ExitProcess keine Memory-Leak-Warnung erhielt, dachte ich, dass diese WinAPI-Methode den Arbeitsspeicher automatisch aufräumt. Auch bei MSDN ( http://msdn2.microsoft.com/en-us/library/ms682658.aspx ) wird bei ExitProcess nicht vor eventuell auftretenden MemoryLeaks gewarnt. Laut MemProof soll mein Programm durch ExitProcess(0) über 1000 MemoryLeaks besitzen. Ob Windows nun automatisch bei ExitProcess den Arbeitsspeicher aufräumt, weiß ich leider nicht.

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:
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;
Hat irgendjemand eine bessere Idee, wie ich mein Programm abrupt ohne Memory Leaks beenden kann?

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 19:17
ü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.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
blackdrake

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

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 19:44
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 ( http://www.troubleshooters.com/codecorn/memleak.htm ). Deswegen bin ich bei ExitProcess recht vorsichtig. Man stelle sich vor, eine 100 MB Datei wird unter 9x gezippt, dann kann das Memory Leak tatsächlich 100 MB groß sein .

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#4

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 19:49
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.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
kalmi01
(Gast)

n/a Beiträge
 
#5

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 20:02
Zitat von SirThornberry:
Löglich das es noch Leute gibt die auf MemoryLeaks extrem achten.

hier noch einer
  Mit Zitat antworten Zitat
blackdrake

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

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 20:13
Zitat von SirThornberry:
über memory-leaks solltest du dir unter xp und vista keine Gedanken machen.
Eigentlich bin ich zwar mit meiner Idee #4 nicht besonders zufrieden, aber diese Information hilft mir zumindestens etwas weiter. Ich könnte die Betriebssystemversion überprüfen und die Frage bzgl. der Arbeitsspeicher-Rückstände nur stellen, wenn 95/98/ME/NT verwendet wird.

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 ( http://msdn2.microsoft.com/en-us/library/ms724834.aspx ) dieses Feature besitzen? Irgendwo im MSDN/TechNet könnte es vermerkt sein, ich weiß aber nicht, wie man es nennt.

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
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von Die Muhkuh
Die Muhkuh

Registriert seit: 21. Aug 2003
7.332 Beiträge
 
Delphi 2009 Professional
 
#7

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 20:59
Zitat von SirThornberry:
ü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.
Hi,

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.
  Mit Zitat antworten Zitat
blackdrake

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

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 21:13
Zitat von Die Muhkuh:
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.
Ist ja leider kaum möglich, da ich beispielsweise eine langandauernde ZIP-Aktion nur von außen, nicht von innen heraus (was eine Speicherfreigabe ermöglichen würde) beenden kann, da ich die Units komplex sind und ich sie nicht geschrieben habe.
Daniel Marschall
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

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

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 10. Sep 2007, 23:10
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:
type
  TForm1 = class(TForm, IDECProgress)
    procedure Process(const Min,Max,Pos: Int64); stdcall;
  end;
und kannst du dein Application.ProcessMessages reinbauen. Übrgeben bekommst du Min,Max und Pos das sind die Größen und Datezeiger/Streamtzeiger.

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.
http://www.michael-puff.de/Developer...agen_Reddmann/ neuste Version.

Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

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

Re: Programm abrupt beenden ohne Memory Leaks

  Alt 11. Sep 2007, 06:10
Zitat:
ich habe keine Lust, DEC, KAZIP und ZLIB für application.terminated
was auch ziemlich dämlich wäre da du damit diese Source abhängig machst von einer rießigen VCL. Der Zugriff auf Application bindet unit Forms ein und damit defakto die halbe VCL. Die Entwickler dieser Bibliotheken wissen warum sie das nicht gemacht haben.

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.
http://www.delphipraxis.net/internal...tream+progress

Gruß Hagen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 19:26 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