Autor Beitrag
Christian213
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 16.10.13 13: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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 16.10.13 13: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



BeitragVerfasst: Mi 16.10.13 13: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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: Mi 16.10.13 13:41 
user profile iconChristian213 hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 16.10.13 13:54 
Hallo zusammen,

Danke erstmal für die Tipps! Ich werde die Punkte mal durchgehen und melde mich dann wieder.

VG
Christian
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 16.10.13 13: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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Di 22.10.13 15:32 
So, ich konnte den "Übeltäter" einkreisen und den Fehler fixen.
Der originale Codeblock sah so aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
StringList := TStringList.Create;
StringList.LoadFromFile('meinetollestringlist.ini');
// ...Dinge tun mit der Stringlist...
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:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Di 22.10.13 15: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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 22.10.13 15:58 
user profile iconbaumina hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Di 22.10.13 16: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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Di 22.10.13 16: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



BeitragVerfasst: Di 22.10.13 16:27 
Nach dem "StringList.LoadFromFile" wird nichts geändert und gespeichert?
Christian213 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 23.10.13 07:52 
user profile iconhathor hat folgendes geschrieben Zum zitierten Posting springen:
Nach dem "StringList.LoadFromFile" wird nichts geändert und gespeichert?


Es wird eine Klassenmethode aufgerufen, die intern mit der Stringlist eigentlich nur folgendes macht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  IniCount := StringList.Count;
  if IniCount > 0 then
  begin
    for i := 0 to IniCount - 1 do
    begin
      IniEntry := StringList[i];

      (...)
    end;
  end;

Es wird dort also an der Stringlist selbst nichts geändert.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 23.10.13 09:22 
Und diese wird nicht zufällig durch z.B. irgendwelche Seiteneffekte noch einmal durchlaufen?
Christian213 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 23.10.13 09:44 
user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: Mi 23.10.13 10:33 
Ich würde alle Stringlisten mit FreeAndNil freigeben. Und außerdem am Anfang der Procedure mal irgendwie sowas einfügen:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 23.10.13 11: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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 23.10.13 11: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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: Mi 23.10.13 12:00 
user profile iconChristian213 hat folgendes geschrieben Zum zitierten Posting springen:
@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:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 66
Erhaltene Danke: 3

Win XP, Win 7 64Bit
Lazarus 1.0.10
BeitragVerfasst: Mi 23.10.13 13: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?