Delphi:在运行时释放动态控件

3
有没有一种绝对安全的方法来释放 Delphi 控件?
我有一个 TStringGrid 的子类,我正在其中“嵌入”一个自定义控件以进行原地编辑。当用户通过 Tab 键或箭头键在网格单元格之间导航时,如果单元格可编辑,我需要创建一个动态控件。我已经钩住了所需的事件,并利用我的自定义控件的 OnKeyDown 事件将导航键传回父 TStringGrid。
以前,TStringGrid 子类只会对嵌入的控件调用 FreeAndNil,但在某些情况下,这会导致在 UpdateUIState/GetParentForm 中出现访问冲突。查看调用堆栈,似乎有时在控件被释放后,仍会发生 WM_KEYDOWN (TWinControl.WMKeyDown) 消息。
我已经查看并实施了如何在其事件处理程序中释放控件?中讨论的更改。这似乎解决了问题,但我想知道是否还有其他注意事项。
实际上,这种解决方法只是延迟了控件的销毁,直到 CM_RELEASE 消息发布时队列中的所有现有消息都被处理完毕。

我能否在CM_RELEASE被发布后,消息队列中已经有另外的WM_KEY*或类似的消息?

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

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

那么,在所有情况下,这是否安全?或者我应该做一些清除队列中其他消息的操作?( SendMessage(Self.Handle,WM_DESTROY,0,0) 很容易想到)


2
在类似的情况下,我不会每次需要时都创建和释放嵌入式组件。相反,我会在初始化时创建它,然后根据需要使其可见/不可见。 - PA.
我需要清除队列中的其他消息吗?不需要,据我所知,消息是从“父级”分派到子级的。 - Trinidad
CodeInChaos的答案比Andreas更接近,因为我对我的复合控件本身没有任何问题。虽然我理解创建和释放控件的设计是次优的,但由于单元格的编辑方式,这是必需的。这个自定义控件与约束引擎相关联,不容易重新连接到其他单元格值。我想知道是否有任何方法可以在CM_RELEASE消息之后删除任何可能排队的消息。如果可能的话,我肯定要避免使用Application.ProcessMessages,但这会清除任何现有的消息。 - jchoover
2个回答

0
通常情况下,您不应该在控件的事件处理程序中销毁该控件。但是,由于您的函数是一个普通的非虚拟消息处理程序,在该控件的内部代码中从未被调用过,因此您应该没问题。从风格的角度来看我不太喜欢这种方式,但我认为对于您的用例来说是可以的。但是,自定义消息可能更清晰一些。
“在CM_RELEASE被发送后,是否有可能已经向消息队列发布了另一个WM_KEY*或类似的消息?” 如果消息队列中的消息会造成大问题,那么您永远无法安全地销毁控件,因为消息可以从其他线程和应用程序中发布。只需确保您的应用程序的正确运行不依赖于每种情况下处理这些消息。

如果可能的话,我会持久化这个组件,但是这个用户控件实际上是一个复合控件,包含一个或多个基于定义当前单元格的元数据的子控件。我之所以有顾虑,是因为现有问题是在释放控件后触发了 WM_KEY 消息,导致 UpdateUIState 调用将 Self(即 null)传递给 GetParentForm,而 GetParentForm 会盲目地与传入的控件交互。 - jchoover

0

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接