TWinControl - 当WinControl具有Valid句柄时,Destructor之前的最后一个阶段是什么?

时间:2014-11-20 11:52:05

标签: delphi delphi-7

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;

没有哔声。

1 个答案:

答案 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进行测试。