【问题标题】:Delphi: Freeing a dynamic control at runtimeDelphi:在运行时释放动态控件
【发布时间】:2011-05-18 02:01:06
【问题描述】:

是否有释放 Delphi 控件的故障安全方法?

我有一个 TStringGrid 后代,我在其中“嵌入”了一个自定义控件,用于就地编辑器。当用户通过 tab 键或箭头键在网格的单元格中导航时,如果单元格是可编辑的,我需要创建一个动态控件。我已经挂钩了所需的事件,并正在利用我的自定义控件的 OnKeyDown 事件将导航键传递回父 TStringGrid。

以前,TStringGrid 后代将简单地调用嵌入式控件上的 FreeAndNil,但在某些情况下,这会导致 UpdateUIState/GetParentForm 内部的访问冲突。查看调用堆栈,有时似乎在释放控件后,仍然会出现 WM_KEYDOWN (TWinControl.WMKeyDown) 消息。

我已经准备好查看并实施How to free control inside its event handler? 中讨论的更改。这似乎已经解决了这个问题,但我想知道这种方法是否还有其他需要注意的地方。

实际上,此解决方法只是将控件的销毁延迟到发布 CM_RELEASE 消息时队列中的所有现有消息之后。

难道不可能在 CM_RELEASE 发布后,另一条 WM_KEY* 或类似的消息都已准备好发布到消息队列中吗?

我当前的 CM_RELEASE 处理程序如下所示:

procedure TMyCustomControl.HandleRelease(var Msg: TMessage);
begin
  Free;
end;

那么,这在所有情况下都是安全的,还是我应该做一些事情来清除队列中的任何其他消息? (SendMessage(Self.Handle, WM_DESTROY, 0, 0) 浮现在脑海中)

【问题讨论】:

  • 在类似的情况下,我不会在每次需要时创建和释放嵌入式组件。相反,我在初始化时创建它,然后根据需要使其可见/不可见。
  • 我应该做些什么来清除队列中的任何其他消息吗? 不,这些消息是从 Parent 发送给孩子的,AFAIK。
  • CodeInChaos 的答案比 Andreas 的答案更接近,因为我的复合控件本身没有问题。虽然我知道创建和释放控件的设计不是最佳的,但由于单元格的编辑方式,它是必需的。此自定义控件与约束引擎相关联,并且不容易重新连接到其他单元格值。我想我真的很想知道是否有任何方法可以删除在 CM_RELEASE 消息之后可能排队的任何消息。如果可能的话,我当然想避免 Application.ProcessMessages,但这会产生清除任何现有消息的效果。

标签: delphi


【解决方案1】:

一般而言,您不应在该控件的事件处理程序中销毁该控件。

但是由于您的函数是一个普通的非虚拟消息处理程序,它永远不会从该控件的内部代码中调用,您应该没问题。从风格的角度来看,我不太喜欢太多,但我认为它适合您的用例。

但自定义消息可能更简洁。

难道不可能在 CM_RELEASE 发布后,另一条 WM_KEY* 或类似的消息都已准备好发布到消息队列中吗?

如果队列中的消息会导致大问题,您永远无法安全地销毁控件,因为消息可以从其他线程和应用程序发布。只需确保您的应用程序的正确运行不依赖于在每种情况下都处理的那些消息。

【讨论】:

  • 如果可能,我会保留该组件,但是此用户控件实际上是一个复合控件,其中包含一个或多个基于定义当前单元格的元数据的子控件。我担心的唯一原因是现有问题是在控件释放后触发了 WM_KEY 消息,然后导致 UpdateUIState 调用将 Self(为空)传递给 GetParentForm,它与控件盲目交互进来了。
【解决方案2】:

SendMessage 发送消息并等待它返回,这就是为什么你不能在你正在释放的控件的事件处理程序中安全地使用它。

另一方面,PostMessage 将发送消息并在事件退出后处理(如果事件中没有更多代码)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    相关资源
    最近更新 更多