使用 TSaveDialog 找出应用程序线程消息丢失的原因
Find out why application thread messages go missing with TSaveDialog
使用 RAD Studio 开发 (Delphi) v10.2.1(东京版 1)
关于 Windows 10 "Creators Update" 64 位,但 32 位开发。
该应用程序是具有多个后台线程的 VCL,每个线程都使用 Indy TidHTTP 来获取网络资源。主线程和后台线程之间的同步是使用消息队列(PostThreadMessage 调用)实现的。这已经足够复杂了,在这里提供直接代码会很困难而且很混乱,所以我从口头描述开始。
应该发生什么:打开一个带有外部资源链接的文件,这会生成 HTTP 请求并将它们交给后台处理,然后等待应用程序消息队列中的传入消息表明资源已下载.应用程序消息在分配给 TApplication.OnMessage 的事件代码中匹配(我怀疑这是我的问题所在)。
有效。大多数时候一切都很顺利。但是如果我打开一个 TSaveDialog - 即使我取消了对话框而不是实际做任何事情 - 那么消息就会从应用程序消息队列中丢失。
通过写日志消息的过程(直接调试是不可能的,因为这打乱了导致问题所需的时间)我已经确定后台线程确实在发布消息(并得到积极的回应PostThreadMessage),但它们从未出现在我的 TApplication.OnMessage 事件代码中。
我看到各种库中的一些偷偷摸摸的代码会设置自己的 PeekMessage/TranslateMessage/DispatchMessage 循环,但并不是所有的库都记得检查是否有 TApplication.OnMessage 事件。但是我刚刚搜索了 VCL 代码和我正在使用的十几个第三方库,但没有发现在这种情况下会受到影响的任何实例(据我所知)。
注意:我正在使用 madExcept、Indy、FastReport、AddictSpell、SynEdit、VclStyleUtils(以及其他一些鲜为人知的库)
注意 2:我想知道它是否与 Delphi 10.2.1 或 Windows 10 Creator 的更新有关,因为我还看到了一些其他奇怪的行为(与第一个异常或第一个 TOpenDialog - 但仅在某些应用程序上)10.1 肯定不会发生(我没有使用 10.2.0)。 ...但这可能(可能是)不同的东西。
所以我的问题是:我该怎么办?
关于如何 find/verify 有一些其他代码窃取应用程序消息的任何建议?除了 PeekMessage,还有什么我应该搜索的吗?
是否有另一种拦截应用程序消息队列消息的方法可以让我避免这个问题?
如果没有更好的选择,我可能不得不放弃使用应用程序线程消息并实现我自己的 messaging/synchronisation 系统,我宁愿在其他时间都很好地工作之后不这样做.
你提到 PostThreadMessage
。别再看了。除非您控制所有可能拉出线程消息的消息循环,否则您不能使用它,而您没有。模态文件对话框消息循环不受您的控制。它将提取用于不同消息循环的线程消息,并且不知道如何处理它们。
解决方法很简单。 Post 消息发送到 window 而不是线程。这样所有正常的消息循环(模态文件对话框的消息循环是正常的)都会将消息发送给预期的接收者 window.
在 Delphi 术语中,这将涉及使用 AllocateHWnd
或类似的方法创建一个隐藏的 window 来接收消息。
Raymond Chen 在这里讨论了这个话题:Why do messages posted by PostThreadMessage disappear?
使用 RAD Studio 开发 (Delphi) v10.2.1(东京版 1) 关于 Windows 10 "Creators Update" 64 位,但 32 位开发。
该应用程序是具有多个后台线程的 VCL,每个线程都使用 Indy TidHTTP 来获取网络资源。主线程和后台线程之间的同步是使用消息队列(PostThreadMessage 调用)实现的。这已经足够复杂了,在这里提供直接代码会很困难而且很混乱,所以我从口头描述开始。
应该发生什么:打开一个带有外部资源链接的文件,这会生成 HTTP 请求并将它们交给后台处理,然后等待应用程序消息队列中的传入消息表明资源已下载.应用程序消息在分配给 TApplication.OnMessage 的事件代码中匹配(我怀疑这是我的问题所在)。
有效。大多数时候一切都很顺利。但是如果我打开一个 TSaveDialog - 即使我取消了对话框而不是实际做任何事情 - 那么消息就会从应用程序消息队列中丢失。
通过写日志消息的过程(直接调试是不可能的,因为这打乱了导致问题所需的时间)我已经确定后台线程确实在发布消息(并得到积极的回应PostThreadMessage),但它们从未出现在我的 TApplication.OnMessage 事件代码中。
我看到各种库中的一些偷偷摸摸的代码会设置自己的 PeekMessage/TranslateMessage/DispatchMessage 循环,但并不是所有的库都记得检查是否有 TApplication.OnMessage 事件。但是我刚刚搜索了 VCL 代码和我正在使用的十几个第三方库,但没有发现在这种情况下会受到影响的任何实例(据我所知)。
注意:我正在使用 madExcept、Indy、FastReport、AddictSpell、SynEdit、VclStyleUtils(以及其他一些鲜为人知的库)
注意 2:我想知道它是否与 Delphi 10.2.1 或 Windows 10 Creator 的更新有关,因为我还看到了一些其他奇怪的行为(与第一个异常或第一个 TOpenDialog - 但仅在某些应用程序上)10.1 肯定不会发生(我没有使用 10.2.0)。 ...但这可能(可能是)不同的东西。
所以我的问题是:我该怎么办?
关于如何 find/verify 有一些其他代码窃取应用程序消息的任何建议?除了 PeekMessage,还有什么我应该搜索的吗?
是否有另一种拦截应用程序消息队列消息的方法可以让我避免这个问题?
如果没有更好的选择,我可能不得不放弃使用应用程序线程消息并实现我自己的 messaging/synchronisation 系统,我宁愿在其他时间都很好地工作之后不这样做.
你提到 PostThreadMessage
。别再看了。除非您控制所有可能拉出线程消息的消息循环,否则您不能使用它,而您没有。模态文件对话框消息循环不受您的控制。它将提取用于不同消息循环的线程消息,并且不知道如何处理它们。
解决方法很简单。 Post 消息发送到 window 而不是线程。这样所有正常的消息循环(模态文件对话框的消息循环是正常的)都会将消息发送给预期的接收者 window.
在 Delphi 术语中,这将涉及使用 AllocateHWnd
或类似的方法创建一个隐藏的 window 来接收消息。
Raymond Chen 在这里讨论了这个话题:Why do messages posted by PostThreadMessage disappear?