| Autor | Beitrag | 
| Christian213 
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 16.10.13 12:06 
 
Hallo,
 meine Anwendung schmeißt beim Aufruf von Application.Terminate einen Access Violation Error.
 Ich bin schon soweit gekommen, dass der Fehler irgendwie mit zur Laufzeit erzeugten Formularen zusammenhängen muss (bzw. das automatische Abräumen deren beim Beenden).
 Gibt es eine Best-Practise wie man solche Fehler am sinnvollsten einkreist?
 Bin für jeden Tipp dankbar.
 
 Danke und viele Grüße
 Christian
 | 
|  | 
| baumina 
          Beiträge: 305
 Erhaltene Danke: 61
 
 Win 7
 Delphi 10.2 Tokyo Enterprise
 
 | 
Verfasst: Mi 16.10.13 12:21 
 
Application.Terminate wird im Normalfall nicht benutzt. Gibt es einen bestimmten Grund weswegen Du ein Application.Terminate aufrufst? Kannst du stattdessen kein Hauptformular.Close aufrufen? Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| hathor Ehemaliges Mitglied
 Erhaltene Danke: 1
 
 
 
 
 | 
Verfasst: Mi 16.10.13 12:36 
 
Close schliesst diesen Befehl (Application.Terminate ) ein:
 Zitat:
 Rufen Sie Terminate auf, um die Anwendung programmgesteuert zu beenden. Durch einen Aufruf der Methode Terminate wird das Anwendungsobjekt nicht einfach gelöscht, die Anwendung kann vielmehr ordnungsgemäß heruntergefahren werden.
 
 Die Methode Terminate ruft die Windows-API-Funktion PostQuitMessage auf, sodass die Anwendung ordnungsgemäß heruntergefahren wird. Sie brauchen Terminate nicht direkt aufzurufen.
 
 Diese Methode wird automatisch für eine WM_QUIT-Botschaft und beim Schließen des Hauptformulars aufgerufen.
 
 Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| Nersgatt 
          Beiträge: 1581
 Erhaltene Danke: 279
 
 
 Delphi 10 Seattle Prof.
 
 | 
Verfasst: Mi 16.10.13 12:41 
 
	  |  Christian213 hat folgendes geschrieben  : |  	  | Ich bin schon soweit gekommen, dass der Fehler irgendwie mit zur Laufzeit erzeugten Formularen zusammenhängen muss (bzw. das automatische Abräumen deren beim Beenden). Gibt es eine Best-Practise wie man solche Fehler am sinnvollsten einkreist?
 
 | 
 Vielleicht greifst Du in irgend einem Destructor auf eine Objektinstanz zu, die schon vorher irgendwo abgeräumt wurde.
 Best Practise um das zu vermeiden ist meiner Meinung nach, im Vorfeld sowas zu vermeiden. Sprich, ich erstelle meine Instanzen von Formularen selbst, wo ich ich sie brauche, und sobald ich sie nicht mehr brauche gebe ich sie frei. Außerdem verwende ich Instanzvariablen in der Regel nicht doppelt. Sprich, für eine Instanzvariable gibt es genau eine Stelle im Code, wo ich die Instanz erzeuge und genau eine Stelle, wo ich die Instanz wieder frei gebe. So behält man den Überblick und kann solche Probleme im Vorfeld vermeiden.
 Eingrenzen ist natürlich schwierig. Ich würde anfangen und die Verdächtigen nicht mit .free freigeben, sondern mit FreeAndNil(). So kannst Du vermeiden, dass der Fehler nur sporadisch auftritt, sondern er tritt sicher auf. Und dann kann man das auch besser einkreisen._________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi) Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 16.10.13 12:54 
 
Hallo zusammen,
 Danke erstmal für die Tipps! Ich werde die Punkte mal durchgehen und melde mich dann wieder.
 
 VG
 Christian
 | 
|  | 
| jaenicke 
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Mi 16.10.13 12:58 
 
Ich würde einfach einmal FastMM einbinden, das findet schon viele solcher Probleme, sprich sagt wo das Problem herkommt, wenn es auftritt.
 Dann wäre da noch madExcept, das für den privaten Gebrauch ebenfalls kostenlos ist.
 Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Di 22.10.13 14:32 
 
So, ich konnte den "Übeltäter" einkreisen und den Fehler fixen.
 Der originale Codeblock sah so aus:
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 
 | StringList := TStringList.Create;StringList.LoadFromFile('meinetollestringlist.ini');
 StringList.Free;
 |  Ändere ich das "StringList.Free" in ein "FreeAndNil(StringList)", so fliegt keine Access Violation mehr?!
 Das finde ich sehr erstaunlich, da "FreeAndNil" in der zuständigen Lib so definiert ist:
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 
 |     procedure FreeAndNil(var obj);var
 temp: tobject;
 begin
 temp:=tobject(obj);
 pointer(obj):=nil;
 temp.free;
 end;
 |  Also im Grunde genommen wird dort ja nur zusätzlich der Pointer auf nil gesetzt.
 Ist es grundsätzlich sinnvoll, immer FreeAndNil anstatt Free zu benutzen? | 
|  | 
| baumina 
          Beiträge: 305
 Erhaltene Danke: 61
 
 Win 7
 Delphi 10.2 Tokyo Enterprise
 
 | 
Verfasst: Di 22.10.13 14:38 
 
Ein FreeAndNil wird dann empfohlen, wenn danach eine Abfrage auf NIL bzw. Assigned auf dieses Object gemacht wird. Ansonsten reicht ein .Free völlig aus. Dass du durch Ersetzen von .Free auf FreeAndNil Erfolg hattest, erscheint mir äußerst suspekt. Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| Martok 
          Beiträge: 3661
 Erhaltene Danke: 604
 
 Win 8.1, Win 10 x64
 Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
 
 | 
Verfasst: Di 22.10.13 14:58 
 
	  |  baumina hat folgendes geschrieben  : |  	  | Dass du durch Ersetzen von .Free auf FreeAndNil Erfolg hattest, erscheint mir äußerst suspekt. | 
 Wobei das immerhin ein Hinweis ist: irgendwo wird nach dem Freigeben noch auf die Stringlist zugegriffen.
 Übrigens, falls das jetzt nicht nur beim Kopieren rausgekürzt war, fehlt da noch der Resourcenschutzblock (try-finally)._________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
 Für diesen Beitrag haben gedankt: Christian213
 | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Di 22.10.13 15:14 
 
Hallo,
 ja, das "try...finally" habe ich in der Tat raus gekürzt.
 
 Kurioserweise greift mein Code nirgends mehr auf "StringList" zu, ich könnte mir nur vorstellen dass dies irgendwo in der "Abräumroutine" der RTL deklariert ist, dass alle "nicht-NIL" Objekte beim Beenden der Anwendung freigegeben werden und es in diesem Fall knallt, da das Objekt bereits freigegeben wurde.
 Was dagegen spricht ist, dass es nur bei diesem Objekt überhaupt derartige Probleme gibt.
 Vielleicht ein Bug im Lazarus? Wer weiß... Ich für meinen Teil bin damit zufrieden, dass es nun klappt und ich auch kein Memoryleak habe.
 | 
|  | 
| baumina 
          Beiträge: 305
 Erhaltene Danke: 61
 
 Win 7
 Delphi 10.2 Tokyo Enterprise
 
 | 
Verfasst: Di 22.10.13 15:26 
 
Dass etwas automatisch freigegeben wird, ohne dass es einen Owner hat, schließe ich aus. 
 Ich freue mich für dich, dass es jetzt ohne Fehler klappt, trotzdem möchte ich dir den Schwung etwas rausnehmen, indem ich dir sagen muss, dass es sehr gut sein kann, dass wenn du nun irgendwo ein paar Zeilen dazu programmierst, es danach wieder an irgendeiner Stelle zum Absturz kommt. Deswegen solltest du dir doch nochmal die Zeit nehmen und den Fehler genauer herausfinden.
 
 Ich kenne solche seltsamen, nicht wirklich nachvollziehbaren sporadischen Abstürze (bzw. Accessviolations) bei der Threadprogrammierung sehr gut, die haben mich des Öfteren fast zum Wahnsinn getrieben.
 | 
|  | 
| hathor Ehemaliges Mitglied
 Erhaltene Danke: 1
 
 
 
 
 | 
Verfasst: Di 22.10.13 15:27 
 
Nach dem "StringList.LoadFromFile" wird nichts geändert und gespeichert? | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 23.10.13 06:52 
 | 
|  | 
| WasWeißDennIch 
          Beiträge: 653
 Erhaltene Danke: 160
 
 
 
 
 | 
Verfasst: Mi 23.10.13 08:22 
 
Und diese wird nicht zufällig durch z.B. irgendwelche Seiteneffekte noch einmal durchlaufen? | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 23.10.13 08:44 
 
	  |  WasWeißDennIch hat folgendes geschrieben  : |  	  | Und diese wird nicht zufällig durch z.B. irgendwelche Seiteneffekte noch einmal durchlaufen? | 
 Die Methode wird mehrfach in der Anwendung benutzt, allerdings jedesmal mit anderen Stringlisten als Parameter. Diese eine Stringlist, die den AV verursacht hat, wird nur 1x im gesamten Code erzeugt, gefüllt, verarbeitet und wieder freigegeben. Daher wundere ich mich ja auch, dass es beim Beenden zu dem Fehler kam. | 
|  | 
| Nersgatt 
          Beiträge: 1581
 Erhaltene Danke: 279
 
 
 Delphi 10 Seattle Prof.
 
 | 
Verfasst: Mi 23.10.13 09:33 
 
Ich würde alle Stringlisten mit FreeAndNil freigeben. Und außerdem am Anfang der Procedure mal irgendwie sowas einfügen:
 		                       Delphi-Quelltext 
 									| 1:
 | Assert(Assigned(DeineListe), 'ups')					 | _________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi) | 
|  | 
| WasWeißDennIch 
          Beiträge: 653
 Erhaltene Danke: 160
 
 
 
 
 | 
Verfasst: Mi 23.10.13 10:06 
 
Das wäre aber eher Herumdoktern an den Symptomen statt Bekämpfung der Ursache. Lediglich lokal benutzte Objekte werden auch nur lokal deklariert, erzeugt und freigegeben. Global benutzte erzeugt man nur einmal und gibt sie auch nur einmal frei, üblicherweise bei Programmende. Wer mag, kann ja Lazy Initialization nutzen, dann wird auch nur der tatsächlich benötigte Speicher belegt. | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 23.10.13 10:51 
 
@Jens: Ich bin mir ziemlich sicher, dass die Methode nie mit einem nicht-definierten Objekt aufgerufen wird, da es sonst eine Exception geben würde.
Aber testen kann ich es ja trotzdem mal.
 | 
|  | 
| Nersgatt 
          Beiträge: 1581
 Erhaltene Danke: 279
 
 
 Delphi 10 Seattle Prof.
 
 | 
Verfasst: Mi 23.10.13 11:00 
 
	  |  Christian213 hat folgendes geschrieben  : |  	  | @Jens: Ich bin mir ziemlich sicher, dass die Methode nie mit einem nicht-definierten Objekt aufgerufen wird, da es sonst eine Exception geben würde. Aber testen kann ich es ja trotzdem mal.
 | 
 Nur wenn das Objekt nil ist. Sonst gibt es nicht zwingend ne Exception.
 Mal ein Codebespiel:
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 
 | procedure TForm4.FormCreate(Sender: TObject);var
 ts : TStringList;
 begin
 
 ts := TStringList.Create;
 ts.Free;
 
 if ts.Count = 0 then
 begin
 MessageDlg('test', mtWarning, mbYesNo, 0);
 end;
 
 end;
 |  Ob eine Exception auftritt oder der MsgDlg erscheint ist mehr oder weniger Zufall._________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi) | 
|  | 
| Christian213  
          Beiträge: 66
 Erhaltene Danke: 3
 
 Win XP, Win 7 64Bit
 Lazarus 1.0.10
 
 | 
Verfasst: Mi 23.10.13 12:59 
 
Okay, das "Problem" hinter ".Free" habe ich verstanden: Man gibt zwar den allozierten Speicher wieder frei, der "Zeiger" dorthin bleibt aber gesetzt.
Umso mehr frage ich mich, warum dann nicht auf Nummer sicher gehen und immer FreeAndNil verwenden? Was spricht denn gegen FreeAndNil?
 | 
|  |