Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Merkwürdige Reihenfolge bei der Freigabe eines Formulars

9 views
Skip to first unread message

Michael Landenberger

unread,
Feb 6, 2022, 5:50:21 AM2/6/22
to
Hallo,

meine Anwendung hat ein Formular mit Controls drauf. Die Controls sind z. T.
eigene, von TWinControl abgeleitete Klassen. Die Controls müssen von Zeit zu
Zeit dem Formular, auf dem sie sitzen, etwas mitteilen. Ich habe das so
gelöst, dass jedes dieser Controls in diesen Fällen ein Ereignis auslöst, das
von einem Handler des Formulars behandelt wird. Also ungefähr so:

//Control

type

TMyControl = class (TWinControl)
private
FOnUpdate : TNotifyEvent;
public
property OnUpdate : TNotifyEvent read FOnUpdate write FOnUpdate;
end;

//Formular

procedure TMyForm.FormCreate (Sender : TObject);

begin
MyControl.OnUpdate := ControlUpdate;
...
end;

procedure TMyForm.ControlUpdate (Sender : TObject);

begin
...
end;

Jedes Mal, wenn sich in MyControl etwas ändert, kann es das Ereignis OnUpdate
auslösen und das Formular reagiert darauf. So weit, so schön.

Jetzt ist es allerdings so, dass die Controls das Ereignis OnUpdate auch in
ihrem Destruktor nochmal auslösen. D. h. kurz bevor sie selbst freigegeben
werden, teilen sie dem Formular noch schnell ihren letzten Status mit (das
Formular speichert den dann in der Registry). Dabei kam es zu unerklärlichen
Schutzverletzungen, außerdem wurde der letzte Status nicht in die Registry
eingetragen. Jetzt habe ich herausgefunden, warum:

Im Destroy-Handler des Formulars (!) wird ein Objekt freigegeben, das für das
Schreiben in die Registry zuständig ist. Dummerweise löst aber das Formular
sein OnDestroy-Ereignis aus, *bevor* es die Controls darauf freigibt. Wenn
diese dann ihrerseits freigegeben werden, lösen sie ihr OnUpdate-Ereignis aus.
Die zugehörige Ereignisbehandlungsroutine auf dem Formular versucht jetzt, die
Status-Daten des Controls in die Registry zu schreiben, wofür es auf das
besagte Registry-Objekt zugreifen muss. Das aber wurde bereits beim vorher (!)
eingetretenen OnDestroy-Events des Formulars freigegeben. Ergebnis: Peng.

Lange Rede, kurzer Sinn: die Reihenfolge der Freigabe des Formulars und der
Controls darauf ist genau andersrum wie ich das erwartet habe. Ich hätte
gedacht, dass das Formular bei der Freigabe erstmal alle Controls wegräumt und
erst dann sich selbst, wobei es auch erst dann das OnDestroy-Ereignis auslöst.
Ist aber nicht so. Das Setzen der Eigenschaft OldCreateOrder des Formulars
ändert daran auch nichts.

Mein Workaround: ich habe den Destruktor des Formulars überschrieben. Im
überschriebenen Destruktor wird zuerst der vererbte Destruktor aufgerufen,
der wiederum die Controls freigibt. Dabei lösen die ihr OnUpdate-Event aus.
Die aber kann das Formular zu diesem Zeitpunkt noch verarbeiten, denn das
Registry-Objekt existiert dann noch. Erst nach dem Aufruf des vererbten
Destruktors (d. h. erst nach Freigabe der Controls und damit nach dem
letzten OnUpdate-Event) gibt der überschriebene Destruktor auch das
Registry-Objekt frei. Das scheint erstmal zu funktionieren. Jedenfalls
knallt's jetzt nicht mehr und der letzte Status landet auch in der
Registry.

Irgendwie ist mir aber unwohl bei dieser Lösung. Schließlich räumt der
Destruktor des Formulars das ganze Formular weg und damit auch seine
Event-Handler. Der Event-Handler für die OnUpdate-Ereignisse könnte also
nicht mehr mehr gültig sein, wenn die Controls ihre OnUpdate-Events ein
letztes Mal auslösen. Dass o. g. Workaround funktioniert, kann also reiner
Zufall sein.

Die Lösung wäre ein Ereignis, mit dem einem Control mitgeteilt wird, dass
sein Parent (das Formular) gleich freigegeben wird, so dass das Control noch
*vor* der Freigabe des Formulars sein OnUpdate-Ereignis absetzen kann. Das
wäre dann ein Zeitpunkt, zu dem sowohl das Registry-Objekt als auch der
OnUpdate-Handler noch existieren, so dass das OnUpdate-Ereignis sauber
verarbeitet werden kann. Aber welches Ereignis nimmt man dafür? Bin für
sachdienliche Hinweise dankbar.

Gruß

Michael

Jens Köhler

unread,
Feb 7, 2022, 3:54:58 AM2/7/22
to
Am 06.02.2022 um 11:50 schrieb Michael Landenberger:
> Hallo,
>
> meine Anwendung hat ein Formular mit Controls drauf.
> Bin für sachdienliche Hinweise dankbar.
> Gruß Michael

Hallo,

eine Idee: das Registry-Objekt selbst erzeugen so das es nicht
automatisch freigegeben wird sondern erst nach der Freigabe der Controls
händisch. Da Events aber asyncron sind, kann das trotzdem schief gehen.
Das gleiche Problem bringt vermutlich auch WM_DESTROY des Formular
abzufangen. Evtl. braucht es dann noch eine Pause zum abarbeiten der
Ereignisse.

Jens



Jens Köhler

unread,
Feb 7, 2022, 2:18:04 PM2/7/22
to
Am 07.02.2022 um 09:54 schrieb Jens Köhler:
> Am 06.02.2022 um 11:50 schrieb Michael Landenberger:
>> Hallo,
>>
>> meine Anwendung hat ein Formular mit Controls drauf.
>> Bin für sachdienliche Hinweise dankbar.
>> Gruß Michael
>
> Hallo,
> Da Events aber asyncron sind, kann das trotzdem schief gehen.
> Jens

Tschuldigung, das war natürlich teilweise Quatsch, was ich schrieb.
Ereignisse sind natürlich syncron. ASyncron sind Windows-Nachrichten wie
WM_DESTROY, da diese in der Warteschlange liegen.
Ich kann aber gerade nicht sagen ob UnterControls direkt zerstört werden
oder eine Nachricht erhalten.

Jens

Michael Landenberger

unread,
Feb 8, 2022, 10:56:41 AM2/8/22
to
"Jens Köhler" schrieb am 07.02.2022 um 09:54:58:

> eine Idee: das Registry-Objekt selbst erzeugen so das es nicht automatisch
> freigegeben wird sondern erst nach der Freigabe der Controls händisch.

Das Registry-Objekt wird ja händisch freigegeben, und zwar zu einem Zeitpunkt,
von dem ich annahm, dass es garantiert nicht mehr benötigt wird (nämlich als
letzten Befehl im OnDestroy-Handler des Hauptfensters). Womit ich nicht
gerechnet habe, ist der Umstand, dass danach immer noch Controls existieren,
die noch später freigegeben werden und die bei ihrer Freigabe nochmal auf das
Registry-Objekt zugreifen wollen.

Ich habe die Sache jetzt anders gelöst: ich lasse jetzt das Formular den
finalen Status meiner Controls abrufen, und zwar im OnClose-Handler. Zu diesem
Zeitpunkt existiert das Registry-Objekt noch. Meine Controls lösen zwar immer
noch OnUpdate-Events aus, aber nur noch während der regulären Benutzung des
Programms und nicht mehr aus ihrem Destruktor heraus.

Gruß

Michael

Christian Schmitt

unread,
May 30, 2022, 11:24:04 AM5/30/22
to
Jens Köhler schrieb am Montag, 7. Februar 2022 um 20:18:04 UTC+1:

> Tschuldigung, das war natürlich teilweise Quatsch, was ich schrieb.
> Ereignisse sind natürlich syncron. ASyncron sind Windows-Nachrichten wie
> WM_DESTROY, da diese in der Warteschlange liegen.
> Ich kann aber gerade nicht sagen ob UnterControls direkt zerstört werden
> oder eine Nachricht erhalten.

sorry wenn ich so spät noch hier reingrätsche, aber ich glaube das stimmt so auch nicht ganz. Ob die Message synchron oder asynchron abgearbeitet wird hängt doch an der sendemethode: Sendmessage ist synchron, dh. hier wird auf die Abarbeitung gewartet, postmessage hingegen wartet nicht, der code wird fortgesetzt und die Message erst abgearbeitet, wenn Zeit ist.

Gruß

Jens Köhler

unread,
May 30, 2022, 12:07:27 PM5/30/22
to
stümmt.
Und wie wird WM_DESTROY gesendet?

Jens

Alfred Gemsa

unread,
May 30, 2022, 12:42:57 PM5/30/22
to
Am 30.05.2022 um 18:07 schrieb Jens Köhler:

> Und wie wird WM_DESTROY gesendet?

if SendMessage(HandleOfWindowToReceiveTheMessage, WM_DESTROY, nil,
nil)=0 then
ShowMessage('Fehler: '+SysErrorMessage(GetLAstError)
else
ShowMessage('OK');

Alfred.
0 new messages