如何在控件销毁后防止消息处理

How to prevent message handling after the control's destruction

以下组件类型有两个实例:

  1. TfrmTimeSliceStructure,它是 TFrame.
  2. 的直系后代
  3. THKSDBVirtualStringTree,它是 TDBVirtualStringTree 的直接后代(来自 FIBPlus),它本身是 Mike Lischke 的 TVirtualStringTree class.[=50 的直接后代=]

THKSDBVirtualStringTree 组件用作 TfrmTimeSliceStructure 上的子控件。

双击 - 在特定条件下 - 框架将被破坏。

为此,我使用自定义消息代码 WM_USER + 4(这是十六进制的 04)进行了 PostMessage 调用,以便延迟销毁,直到所有当前消息都被完全处理.

不过,在许多情况下会发生访问冲突,因为 THKSDBVirtualStringTree 组件在其自身销毁后仍在处理消息。

我预计控件销毁后不会发生消息处理。

如何防止消息被已经被销毁的控件处理?

在下面,您可以看到调试器的输出。在两个classes中,我都在方法WndProc中添加了一个消息日志记录来输出接收到的消息代码。在第一行,可以看到我的自定义消息代码 WM_USER + 4 已收到。

几行之后,有两行Instance of class THKSDBVirtualStringTree is going to be destroyed.Instance of class THKSDBVirtualStringTree has been destroyed.。在这两行之间,没有收到任何消息。
在这些行之后,仍然处理了一些消息。最终,这会导致最后的访问冲突。从他们的消息代码我可以看出,这些消息是控制消息,因为 CM_BASE = $B000;.

Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message 04 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message 05 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message 01 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message 00 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message 02 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message 15 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message A3 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B014 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $B014 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message 01 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message 01 Prozess Memory.exe (20916)
Thread-Start: Thread-ID: 6260. Prozess Memory.exe (20916)
Thread-Start: Thread-ID: 21148. Prozess Memory.exe (20916)
Thread-Ende: Thread-ID: 6260. Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message [=10=]02 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message [=10=]0E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message 72 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message [=10=]02 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message [=10=]0E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message [=10=]82 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message [=10=]82 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree is going to be destroyed. Prozess Memory.exe (20916)
Thread-Ende: Thread-ID: 22156. Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree has been destroyed. Prozess Memory.exe (20916)
Thread-Start: Thread-ID: 5672. Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B028 Prozess Memory.exe (20916)
Thread-Start: Thread-ID: 9244. Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B009 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B008 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B023 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B03D Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B050 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B058 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B011 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B03B Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B03B Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B00D Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B022 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B009 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B008 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B023 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B035 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B03D Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B050 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B058 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B011 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B009 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B035 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B034 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B009 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B03B Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B008 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B00E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B034 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B008 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B00E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B034 Prozess Memory.exe (20916)
Thread-Ende: Thread-ID: 5672. Prozess Memory.exe (20916)
Thread-Ende: Thread-ID: 21148. Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $B007 Prozess Memory.exe (20916)
Erste Gelegenheit für Exception bei 819981. Exception-Klasse $C0000005 mit Meldung 'access violation at 0x01819981: read of address 0x00000050'. Prozess Memory.exe (20916)

问题是由于在我们的 OnNodeDblClick 事件处理程序代码中某处显式调用 Application.ProcessMessages 引起的。

不幸的是,我们的代码库中仍有一些这样的调用。 :-(


让我告诉你我是如何找出问题原因的:

正如你在问题中看到的,我尝试了这两种方法:

  1. 覆盖控件的 WndProc 并使用 OutputDebugString 记录每个已处理的消息。
  2. 检查控件是否已经销毁,方法是在控件析构函数的开头添加对 OutputDebugString 的调用,在控件析构函数的末尾添加另一个调用。

后来我想到了...

  1. ...检查发起的用户交互(此处:双击)是否已完全处理。这也是用 OutputDebugString 完成的。
    在我的例子中,在发生访问冲突之前,第二次鼠标按下没有完全处理。

然后我可以得出结论,消息队列在某处被过早处理。所以,我必须找出调用 Application.ProcessMessages 的地方。

  1. 因此,我在这个方法中加了一个断点。因为正常的断点会中断消息处理。我使用了另一种断点类型,我不得不承认,我是第一次使用它。
    在断点的 高级设置 中,我取消选择 Break 并改为选择 Log Call Stack。我决定只记录两个堆栈帧,因为我只想知道 Application.ProcessMessages 的直接调用者是谁。

在又一轮的重现之后,我得到了一个广泛的调试器日志,其中包含所有那些已处理的消息代码和每次调用鼠标按下处理程序、对象的析构函数以及每次调用 Application.ProcessMessages.

  1. 现在,我可以推断在未完成的鼠标按下处理程序开始之后和接收自定义事件 WM_USER + 4 之前对 Application.ProcessMessages 的第一次调用必须是那个,破解密码。