【问题标题】:Find out why application thread messages go missing with TSaveDialog使用 TSaveDialog 找出应用程序线程消息丢失的原因
【发布时间】:2017-08-31 14:08:29
【问题描述】:

使用 RAD Studio (Delphi) v10.2.1 (Tokyo release 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)。 ...但这可能是(可能是)不同的东西。

所以我的问题是:我该怎么办?

有关如何查找/验证是否有其他代码窃取应用程序消息的任何建议?除了 PeekMessage,我还应该搜索什么?

是否有其他方法可以拦截应用程序消息队列消息,这可能会让我避免该问题?

如果没有更好的选择,我可能不得不放弃使用应用程序线程消息并实现我自己的消息传递/同步系统,我宁愿在其他时间都很好地工作后不这样做。

【问题讨论】:

  • 你可以使用SetWindowsHookEx()在UI线程中安装一个线程特定的WH_GETMESSAGE钩子
  • @Remy 对此表示感谢。这样的钩子将提供一种方法,让我将消息集中在线程 ID 周围,这可能比必须引入窗口句柄更整洁。

标签: multithreading delphi winapi delphi-10.2-tokyo


【解决方案1】:

你提到了PostThreadMessage。不要再看了。除非您控制所有可能拉出线程消息的消息循环,否则您不能使用它,而您没有。模态文件对话框消息循环不在您的控制范围内。它将拉出用于不同消息循环的线程消息,并且不知道如何处理它们。

解决方案很简单。将消息发布到窗口而不是线程。这样,所有正常的消息循环(模式文件对话框的消息循环是正常的)都会将消息发送到预期的收件人窗口。

在 Delphi 术语中,这将涉及使用AllocateHWnd 或类似方法来创建一个隐藏窗口来接收消息。

Raymond Chen 在这里讨论了这个话题:Why do messages posted by PostThreadMessage disappear?

【讨论】:

  • 问题中的粗体部分似乎表明即使在取消对话后消息也会出错。或者我理解错了..
  • @SertacAkyuz 我认为这意味着对话框已打开然后取消,但仍然缺少一些事件。无论如何,PTM 无疑是个问题。
  • Sertac,问题的性质使得很难准确判断消息何时再次通过,但在对话框消失后它们肯定会再次开始。大卫的解释很合适。
  • 大卫,你的解释很有道理。我想我一直认为 VCL 会确保 OnMessage 被调用。现在我知道得更清楚了。有点难过,之前我只需要一个thread-id,不管是谁在发送或接收,现在我必须修改代码来区分主线程和其他线程。这就是编程。非常感谢。
  • @Geoff 当您调用 api 对话框时,您已退出 VCL。 ;)
猜你喜欢
  • 2012-03-29
  • 2017-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多