在TCustomWinControl
被永久销毁之前,我需要用它的手柄做一些事情。
如果我尝试在destructor
中访问其句柄,我会收到错误:
" Control" xxx"没有父窗口"。
那么在TWinControl析构函数的句柄(HandleAllocated
)仍然有效之前的最后一个阶段是什么?
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;
没有哔声。
答案 0 :(得分:1)
<强>更新强>
它比我原先想象的要复杂得多。你的控制权存在于一个形式上,而控制的死亡是由于那种形式的死亡而引起的。当一个表单被销毁时,子窗口也会被销毁。因此,Win32 API负责销毁您的窗口。 VCL通过回复WM_NCDESTROY
消息来跟踪这一情况:
procedure TWinControl.WMNCDestroy(var Message: TWMNCDestroy);
begin
inherited;
FHandle := 0;
FShowing := False;
end;
所以,我想你可以自己处理WM_NCDESTROY
。在csRecreating
中查找ControlState
,根据窗口销毁是否与VCL窗口重新创建相关来切换行为。
这里需要注意的一点是,没有必要调用控件的析构函数。如果它不是表单所有,则您的控件不会被销毁。然后,您可以将其重新托管到另一个表单上。所以WM_NCDESTROY
确实是正确的钩子。
原始回答
析构函数的源代码如下所示:
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;
在此行中调用Win32 API DestroyWindow
:
if FHandle <> 0 then DestroyWindowHandle;
所以你需要在那之前运行你的代码。
您可以覆盖DestroyWindowHandle
并在那里完成工作。只要您需要处理的事件是窗口的破坏,那就行得很好。但请记住,重新创建窗口时将调用DestroyWindowHandle
。
如果您需要执行与销毁VCL控件相关的操作,那么最好覆盖BeforeDestruction
。或者作为替代方案,您可以覆盖DestroyWindowHandle
并在csDestroying
中对ComponentState
进行测试。