Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Wichtigkeit von Try...Finally...End (https://www.delphipraxis.net/61314-wichtigkeit-von-try-finally-end.html)

sir-archimedes 19. Jan 2006 19:20


Wichtigkeit von Try...Finally...End
 
Hallo Ihr alle,

ich wollte euch einmal mitteilen, wie dermaßen wichtig der richtige Gebrauch von try...finally-Blöcken seien kann. Bei mir im Programm sind beim Öffnen eines Dialoges plötzlich Zugriffsverletzungen aufgetreten.

Aber erstens nur auf einem System, auf dem BDS2006 nicht installiert ist - in der IDE hat sich nichts getan, daher war debuggen nicht möglich und außerdem nur manchmal. Zunächst unerklärlich. Bis ich dahinter gekommen bin, dass sie immer dann auftreten, wenn ich vorher eine bestimmte Aktion gemacht habe.

Nach stundenlanger (ich habe seit heute Nachmittag 15 Uhr danach gesucht) Suche habe ich folgendes böses Stück Code gefunden: :coder2:

Delphi-Quellcode:
    ADODataSet := pLogicDataModule.GetADODataSet('SELECT ID FROM MD_InvoiceRules WHERE Active = 1 ORDER BY Mandator', true);
    try
      ProgressDialog := TJvProgressDialog.Create(NIL);
      ProgressDialog.ScreenPosition := poMainFormCenter;
      ProgressDialog.Caption := 'Rechnungserstellung';
      ProgressDialog.Text := 'Verarbeite Regeln (insgesamt '+IntToStr(ADODataSet.RecordCount)+')';
      ProgressDialog.Show;
      ProgressDialog.Position := 0;
      ProgressDialog.Min := 0;
      ProgressDialog.Max := ADODataSet.RecordCount;
      ProgressDialog.ShowCancel := False;
      while not ADODataSet.Eof do
        begin
          ProcessInvoiceRule(ADODataSet.FieldByName('ID').AsInteger, AccountingPeriod);
          ADODataSet.Next;
          ProgressDialog.Position := ProgressDialog.Position+1;
        end;
    finally
      ProgressDialog.Free;
      ADODataSet.Free;
    end;
Ich bin davon ausgegangen, dass dieser Progress-Dialog wohl keinen Schaden anrichten wird. Pustekuchen! Irgendwas hat er bewirkt. Ich denke, dass das ADODataSet nicht zerstört wurde, was dann irgendwann (immernoch unerklärlich :gruebel: ) zu Schutzverletzungen geführt hat.

Eine Änderung in folgendes, hat den Fehler eliminiert:
Delphi-Quellcode:
      ADODataSet := pLogicDataModule.GetADODataSet('SELECT ID FROM MD_InvoiceRules WHERE Active = 1 ORDER BY Mandator', true);
      try
        ProgressDialog := TJvProgressDialog.Create(NIL);
        try
          ProgressDialog.ScreenPosition := poMainFormCenter;
          ProgressDialog.Caption := 'Rechnungserstellung';
          ProgressDialog.Text := 'Verarbeite Regeln (insgesamt '+IntToStr(ADODataSet.RecordCount)+')';
          ProgressDialog.Show;
          ProgressDialog.Position := 0;
          ProgressDialog.Min := 0;
          ProgressDialog.Max := ADODataSet.RecordCount;
          ProgressDialog.ShowCancel := False;
          while not ADODataSet.Eof do
            begin
              ProcessInvoiceRule(ADODataSet.FieldByName('ID').AsInteger, AccountingPeriod);
              ADODataSet.Next;
              ProgressDialog.Position := ProgressDialog.Position+1;
            end;
          ProgressDialog.Hide;
        finally
          ProgressDialog.Free;
        end;
      finally
        ADODataSet.Free;
      end;
Eines habe ich daraus gelernt: Spare nicht noch einmal an Try-Finally-Blöcken. :spin2:

Das wollte ich einfach mal erzählt haben :dancer:

Viele Grüße,
Dominik

tommie-lie 19. Jan 2006 19:31

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von dominik.boehm
Spare nicht noch einmal an Try-Finally-Blöcken.

Weiser Schluss. So viel gips da ja auch nicht zu sparen.

FlatCoder 19. Jan 2006 19:40

Re: Wichtigkeit von Try...Finally...End
 
Das was Dominik hier erwähnt ist in der Tat keine "läppische" Angelegenheit!
Das Auffangen von Fehlern mit Try..Except bzw. Try..Finally ist besonders wichtig wenn man wirklich sicher gehen will Objekte, die man erstellt hat, auch wieder freizugeben! Denn sonst läuft Speicher voll und bereits gelöscht geglaubte Daten werden von neuen Objekten in Anspruch genommen und schwupps hat man einen Fehler oder noch schlimmer: einen Absturz! :o

Ich kenne einige Programmierer die Objekte erstellen und dann unten irgendwann mal mit FREE wieder freigeben.
Ohne sie im TRY..EXCEPT oder TRY..FINALLY Konstrukt verpackt zu haben.
Wenn ich sie frage, warum sie das nicht machen heißt es: "Das ist nur unnötige Tipparbeit!..." oder "Warum soll da was passieren? Das läuft garantiert durch und wird später wieder freigegeben..."

Naja jeder wie ers will. Aber für mich gilt: Egal ob ein Formular dynamisch mit Application.CreateForm, oder ein Objekt erstellt wird was später wieder DESTROY't oder GEFREE't werden soll, TRY..FINALLY muss mindestens mit! :angel:

Gruß

FlatCoder :bounce1:

Elvis 19. Jan 2006 19:46

Re: Wichtigkeit von Try...Finally...End
 
Könnte auch ein Argument sein, Factories (wie getAdoDataSet) Interface instanzen zurückgeben zu lassen. Dadurch kümmert sich der Compiler um Try-finally und man spart sich Tipperei. :zwinker:

btw, dein Problem verstehe ich trotzdem nicht ganz. Das Freigeben des DataSets hätte doch nur duch eine Exception im Destructor vom ProgressDialog verhindert werden können. Die wiederum hätte dir doch auffallen müssen.... :gruebel:

3_of_8 19. Jan 2006 19:49

Re: Wichtigkeit von Try...Finally...End
 
Bitte mal auf Deutsch! Ich verstehe gar nix.

tommie-lie 19. Jan 2006 19:59

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von Elvis
btw, dein Problem verstehe ich trotzdem nicht ganz. Das Freigeben des DataSets hätte doch nur duch eine Exception im Destructor vom ProgressDialog verhindert werden können. Die wiederum hätte dir doch auffallen müssen.... :gruebel:

Die einzige Konstellation, bei der ich mir vorstellen könnte, daß etwas in die Hose geht, wäre, wenn der Konstruktor von TJvProgressDialog Mist baut, eine Exception wirft *und* Mist zurückgibt. Dann steckt hinter ProgressDialog alles mögliche, nur kein gültiges Objekt. Durch die Exception springt's in den finally-Abschnitt, wo die Methode Free aufgerufen wird, aber da nix Vernünftiges bei rauskommt. Allerdings deutet das auf einen gehörigen Fehler in der RTL hin, wenn ein Konstruktor eine Exception wirft, bleibt zumindest die Instanz des Objektes gültig und ein FreeInstance() sollte nicht fehl schlagen. Aber vielleicht war es ja auch der Code des Destruktors von TJvProgressDialog, der auf unsaubere Felder zugegriffen hat.
Ich bin mir zwar nicht sicher, aber ich meine, bei try..finally wird bei einer Exception im finally-Block auch aus dem finally-Block herausgesprungen, bevor er vollständig abgearbeitet ist. Das wäre die einzige plausible Erklärung, die ich so ohne weiteres wüsste, ohne mich nochmal damit beschäftigen zu müssen, was Delphi da anstellt.


Und Exception-Handling ist performancetechnisch so billich, da kann man lieber mal eins zu viel nehmen als eins zu wenig.

Khabarakh 19. Jan 2006 20:05

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von 3_of_8
Bitte mal auf Deutsch! Ich verstehe gar nix.

http://de.wikipedia.org/wiki/Factory-Pattern
Interface ist dir hoffentlich geläufig :wink: .

sir-archimedes 19. Jan 2006 20:26

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von Elvis
Könnte auch ein Argument sein, Factories (wie getAdoDataSet) Interface instanzen zurückgeben zu lassen. Dadurch kümmert sich der Compiler um Try-finally und man spart sich Tipperei. :zwinker:

Ich gebe dir Recht - das wäre ein guter Ort, um Interfaces einzusetzen. Allerdings müsste ich dann ja nicht nur ein Interface erstellen, sondern auch noch eine von ADODataSet abgeleitete Klasse, die dann auch das Interface implementiert. Dazu müsste ich dann auf der anderen Seite auch noch eine Menge im sonstigen Quelltext ändern und das ist mir zu aufwändig. Dann lieber Old-School-Factories mit "echten Objekten" :-)

Zitat:

Zitat von Elvis
btw, dein Problem verstehe ich trotzdem nicht ganz. Das Freigeben des DataSets hätte doch nur duch eine Exception im Destructor vom ProgressDialog verhindert werden können. Die wiederum hätte dir doch auffallen müssen.... :gruebel:

Genau das verstehe ich auch nicht. Wenn etwas schief ginge und eine Exception ausgelöst würde, dann wäre sie später aufgefallen. Habe weiter unten ein
Delphi-Quellcode:
...
  except
    // Rollback the transaction on error
    pLogicDataModule.DBConnection.RollbackTrans;
    raise;
  end;
stehen. Das sollte ja eigentlich die Exception rauslassen. Das komische ist halt, dass es in der Delphi-Umgebung alles funktioniert. Daher erhalte ich da auch keine Exception. Die kommt aber "wie erwartet" auf einem System ohne Delphi.

Einen Fehler in der RTL schließe ich eigenltich aus - der Konstruktor funktioniert einwandfrei: Mein ProgressDialog wird wie gewollt angezeigt. Evtl. geht es schief, wenn ich diesen Dialog freee, ohne ihn vorher versteckt zu haben. Aber das möchte ich nicht auch noch testen - der Fehler hat mich genug Zeit gekostet ;-)

tommie-lie 19. Jan 2006 20:37

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von dominik.boehm
Mein ProgressDialog wird wie gewollt angezeigt.

Ach so?!?
Dann würde es mich ja schon mal interessieren, was denn da schief läuft. Schade, daß du keinen Nerv zu hast, das zu debuggen, aber vielleicht findest du die Neugier ja am Wochenende wieder ;-)

sir-archimedes 19. Jan 2006 20:51

Re: Wichtigkeit von Try...Finally...End
 
Zitat:

Zitat von tommie-lie
Zitat:

Zitat von dominik.boehm
Mein ProgressDialog wird wie gewollt angezeigt.

Ach so?!?
Dann würde es mich ja schon mal interessieren, was denn da schief läuft. Schade, daß du keinen Nerv zu hast, das zu debuggen, aber vielleicht findest du die Neugier ja am Wochenende wieder ;-)

Naja, die Neugier hat mal wieder gesiegt *g* In der TJvProgressDialog Klasse ist ein kleiner Fehler: So sieht nämlich der Destruktor aus:
Delphi-Quellcode:
destructor TJvProgressDialog.Destroy;
begin
  FreeAndNil(FImage);
  FreeAndNil(FIImage);
  inherited Destroy;
end;
Wenn der Dialog angezeigt wurde, und man ihn ohne vorheriges Verstecken zerstören möchte, dann bleibt das Fenster am Leben. Hier schafft ein "Hide" vor dem ersten FreeAndNil Abhilfe.

Allerdings erklärt das mein spezifisches Problem nicht. Mein Problem tritt ja auch nicht da - sondern ein paar hundert Zeilen später - in einem ganz anderen Modul auf. Und das ohne das ich noch einmal diesen ProgressDialog verwende. Das ganze bleibt mir ein Rätsel. :cyclops: Aber immerhin konnte ich so jetzt einen kleinen JVCL-Fehler verbessern :-) Hat ja auch was!

Weiteres Debuggen werde ich nicht machen - zumal sich dieser Fehler ja auch nicht debuggen lies.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:43 Uhr.
Seite 1 von 2  1 2      

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