【问题标题】:How to prevent message handling after the control's destruction控件销毁后如何防止消息处理
【发布时间】:2018-01-22 03:25:52
【问题描述】:

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

  1. TfrmTimeSliceStructure,是TFrame 的直系后代。
  2. THKSDBVirtualStringTree,是 TDBVirtualStringTree(来自 FIBPlus)的直系后代,它本身是 Mike Lischke 的 TVirtualStringTree 类的直系后代。

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

双击 - 在某些情况下 - 框架将被销毁。

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

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

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

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

在下面,您可以看到调试器的输出。在这两个类中,我在方法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 $0404 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $0405 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $8001 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0200 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0202 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0215 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $02A3 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 $8001 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $8001 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 $0002 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $000E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0272 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0002 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $000E Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class THKSDBVirtualStringTree recieved message $0082 Prozess Memory.exe (20916)
Debug-Ausgabe: Instance of class TfrmTimeSliceStructure recieved message $0082 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 $01819981. Exception-Klasse $C0000005 mit Meldung 'access violation at 0x01819981: read of address 0x00000050'. Prozess Memory.exe (20916)

【问题讨论】:

  • 在这里很难给出一般性的建议。如果我遇到这个问题,我想调试一个复制品。
  • @DavidHeffernan 我会尝试提供一个 MCVE。但是我关于在控件销毁后没有发生消息处理的异常至少是正确的吗?
  • 您是否尝试过调用框架的 Release 方法而不是 Free?它就是为了这个目的。 (我不知道这是否能解决问题,只是一个想法。
  • @dummzeuch,框架没有Release 方法。 OP实现了自己的。与 Form 的 Release 相同(我会使用 CM_RELEASE 而不是 WM_USER

标签: delphi access-violation windows-messages object-destruction


【解决方案1】:

问题是由在我们的 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 的第一次调用必须是那个,这会破坏代码。李>

【讨论】:

  • 很难理解你为什么写这个答案。我们没有复制品。问题中的信息不会导致这一点。您可以通过在问题中提供 minimal reproducible example 来使其对其他人有价值,但尚未这样做。
  • 我写了这个答案,因为很难到达那里。这只是导致问题的一条坏线。我发现其他有类似问题的人知道调试此类问题的方法很有帮助。我无法提供实际的 MCVE,因为它远非最小。因此,这个问题可能有问题。虽然,我认为答案包含对其他人有用的信息。
猜你喜欢
  • 2020-12-16
  • 1970-01-01
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 2015-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多