Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - FormCreate und PanelResize


Gausi - So 15.09.19 15:26
Titel: FormCreate und PanelResize
Ich habe hier grade ein komisches Problem über "Ferndebugging" lösen dürfen (also mehrfaches Abändern des Codes mit anschließender Zusendung des MadExcept-Bugreports.

Ich hoffe, ich breche den Code nicht zu stark herunter. Folgendes.

Im OnCreate der Form erstelle ich ein paar Objekte.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.FormCreate(Sender: TObject);
begin
   Foo := TFoo.Create;
   // Foo.stuff := Gedöns; // Variablen-Initialisierung 

   Bar := TBar.Create;
   Bar.foo := Foo;
end;


Diese Objekte brauche ich dann z.B., um passend auf das Resize eines Panels auf der Form1 zu reagieren.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Panel1Resize(Sender: TObject);
begin
   if Foo.Bool1 then  
       Foo.DoSomething;
   // das "if" knallt beim Start.
   // Abhilfe:
   // if assigned(Foo) and Foo.Bool1 then   
end;


Das finde ich schonmal merkwürdig, dass das Panel.OnResize aufgerufen wird, bevor das FormCreate fertig ist. Aber gut. Noch seltsamer wird es dann beim nächsten Panel-Resize:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.Panel2Resize(Sender: TObject);
begin
   if Bar.Bool2 then   // läuft erstmal
      Bar.DoStuff;
end;

// in der Klasse TBar:
procedure TBar.DoStuff;
begin
    if foo.Bool3 then // knallt
       DoSomeWork;
end;

Abhilfe ist wieder die gleiche.

Ich frage mich nun, wie es passieren kann, dass Zugriff auf Foo fehlschlägt, aber der Zugriff auf Bar, das nach Foo erzeugt wird, erst einmal keine Fehlermeldung verursacht. Und das ganze natürlich nur auf einem System (oder einigen?). Bei mir unter Win10, 64 Bit läuft das durch. Der gemeldete Fall war wohl ein Windows 10 (32 Bit) auf einem Tablet-PC.

Klingt nach einem klassischen Race-Condition-Bug, aber wie kommt der hier zustande? Macht die VCL hier auf einmal Multithreading und arbeitet schonmal weiter, während das Foo.Create und die Variablenzuweisung noch läuft?

Und wie kann ich das sauber verhindern? Beim Start des FormCreate alle Resize-Handler auf Nil setzen und am Ende wieder passend? Oder muss ich im Form-Designer schon die Resize-Handler austragen, und darf sie nur manuell nach dem Create setzen?

Hat der wer einen Denkanstoß für mich? :gruebel:


Sinspin - So 15.09.19 17:24

Du kannst erstmal testen ob es deine Objekte schon gibt.
Also sauber initialisierte Variablen und dann auf foo <> nil oder via Assigned(foo).
Auch Bar natürlich. Es kann sein das hier einfach beim Zugriff die Adresse nicht in die Ferne führt.
Ist schon ein interessanter Fehler. Ereignisse werden via Windowsbotschaften erzeugt / geparkt.
Das kann auf unterschiedlichen System in unterschiedlicher Reihenfolge passieren.
Generell würde ich mit dem arbeiten in OnResize warten bis OnActivated/OnShow gekommen ist.
Und zwar nicht via abhängen von Ereignishandlern, sondern einer Variable die Du in OnActivated setzt.


Gausi - So 15.09.19 20:46

Den Test auf Assigned habe ich ja schon eingebaut, und es funktioniert.

Das mit der "ReadyForResize"-Variable, die im OnActivate/OnShow passend gesetzt wird, finde ich eine schöne Lösung. Denn das ist dann später eher nachvollziehbar als der Test auf Assigned dieser Objekte. Das werde ich wohl so einbauen. :zustimm:

Ich finde die "Unordnung", die da manchmal entsteht, aber trotzdem nicht schön - hauptsächlich, weil ich das noch nicht verstehe ... :lol:


Nersgatt - Mo 16.09.19 16:49

Überschreib doch den echten Konstruktor der Basisklasse des Formulars und nutze nicht das Event FormCreate. Wenn Du Deine Instanzen dann sogar noch vorm Inherited erzeugst, dann kannst Du absolut sicher sein, dass das als allererstes passiert.


Sinspin - Di 17.09.19 09:10

Das setzt aber vorraus das man nicht mit TForm arbeitet sondern mit einer eigenen Ableitung TFormFoo.
Das macht durchaus Sinn. Erst recht dann wenn man all seinen Fenstern weiteres Verhalten beibringen will. Wie zum Beispiel automatisches laden und speichern von Position und Größe.


Gausi - Di 17.09.19 17:58

Das mit der eigenen Formklasse ginge natürlich auch. Aber ich werde wohl den Weg gehen, die Resize-Handler erst dann zu "aktivieren", wenn es auch was bringt. :D