Just before my TCustomWinControl
is destroyed permanently, I need to do somthing with its handle.
If I try to access its handle in the destructor
I get an error:
"Control "xxx" has no parent window".
So what is the last stage before TWinControl Destructor where its handle (HandleAllocated
) is still valid?
type
TPanel = class(ExtCtrls.TPanel)
protected
procedure DestroyWindowHandle; override;
public
procedure BeforeDestruction; override;
end;
procedure TPanel.DestroyWindowHandle;
begin
Beep;
if csDestroying in ComponentState then Beep;
inherited;
end;
procedure TPanel.BeforeDestruction;
begin
if HandleAllocated then Beep;
inherited;
end;
There is no Beep.
Update
It is more complex than I originally thought. Your control lives on a form, and the control's death is provoked by the death of that form. When a form is destroyed, the child windows are also destroyed. So, the Win32 API is responsible for destroying your window. The VCL keeps track of this by responding to the WM_NCDESTROY
message:
procedure TWinControl.WMNCDestroy(var Message: TWMNCDestroy);
begin
inherited;
FHandle := 0;
FShowing := False;
end;
So, I guess you could handle WM_NCDESTROY
yourself. Look for csRecreating
in ControlState
to switch behaviour based on whether or not the window destruction is related to VCL window re-creation.
An interesting point to note here is that there's no reason why the destructor of your control has to be called. If it is not owned by the form then your control won't be destroyed. You could then re-parent it onto another form. So WM_NCDESTROY
really is the right hook.
Original answer
The source code of the destructor looks like this:
destructor TWinControl.Destroy;
var
I: Integer;
Instance: TControl;
begin
Destroying;
if FDockSite then
begin
FDockSite := False;
RegisterDockSite(Self, False);
end;
FDockManager := nil;
FDockClients.Free;
if Parent <> nil then RemoveFocus(True);
if FHandle <> 0 then DestroyWindowHandle;
I := ControlCount;
while I <> 0 do
begin
Instance := Controls[I - 1];
Remove(Instance);
Instance.Destroy;
I := ControlCount;
end;
FBrush.Free;
{$IFDEF LINUX}
if FObjectInstance <> nil then WinUtils.FreeObjectInstance(FObjectInstance);
{$ENDIF}
{$IFDEF MSWINDOWS}
if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);
{$ENDIF}
inherited Destroy;
end;
The call to the Win32 API DestroyWindow
is made in this line:
if FHandle <> 0 then DestroyWindowHandle;
So you need to run your code before then.
You could override DestroyWindowHandle
and do your work there. That would work well so long as the event you need to deal with is the destruction of the window. But bear in mind that DestroyWindowHandle
will be called when the window is re-created.
If you need to do something related to the destruction of the VCL control, then you would be best overriding BeforeDestruction
. Or as an alternative, you could override DestroyWindowHandle
and in there test for csDestroying
in the ComponentState
.