gpt4 book ai didi

delphi - 什么解释了这种奇怪的 PeekMessage 行为(尝试处理完整的消​​息队列,过滤特定消息)?

转载 作者:行者123 更新时间:2023-12-03 15:02:31 25 4
gpt4 key购买 nike

我们的应用程序充当 COM 服务器,其中所有自动化都发生在单个 STA 单元内(在应用程序的主线程中),并且一些进行长时间(> 10 分钟)调用的 VBS 脚本失败并显示错误“系统调用失败(80010100)” .一些研究( one , two , three )表明这可能是由于消息队列填满造成的,因此当 COM 尝试调用下一个方法时,它无法调用。

万一它很重要,应用程序是用 Embarcadero RAD Studio 2010 开发的(主要是 C++ ,对于某些 COM 类,只有 Delphi 。)

我想我会在冗长的 COM 方法调用结束时(即,在它返回之前)检查线程的消息队列,以查看它包含的内容,使用 GetQueueStatus PeekMessage 。虽然队列似乎已满,但我看到了一些奇怪的行为,而且我无法弄清楚为什么 PeekMessage 的行为方式如此,以及队列为何已满 - 即它充满了什么。

前面稍微冗长的解释:

测试线程的消息队列已满

像这样的代码:

int iMessages = 0;
DWORD dwThreadId = GetCurrentThreadId();
while (::PostThreadMessage(dwThreadId, WM_USER, 0, 0)) {
iMessages++;
}
if (GetLastError() == ERROR_NOT_ENOUGH_QUOTA) {
String strError = L"Not enough quota, posted " + IntToStr(iMessages) + L" messages";
// Do something with strError
}

当在一个简短的 COM 调用方法的末尾运行时,可以发布数千(例如 9996)条消息;在导致脚本失败的冗长方法调用结束时,它可以发布 0。我的结论是消息队列已满确实是问题的原因。我的系统的 message queue limit is the default 10000(请参阅备注部分。)

调用 Application->ProcessMessages()(调用应用程序的消息循环直到它为空,对于那些不是 Delphi/C++Builder 用户的人 - 这是一个相当正常的“get/translate/dispatch until no more messages”方法)解决了这个问题并且 COM 脚本可以成功调用下一个方法。虽然在这种特定情况下可能没问题,但在有效的随机位置调用 ProcessMessages() 是需要避免的 - 它可能导致重入。如果可能,我想找出导致队列已满的原因。

检查消息队列的内容

使用 GetQueueStatus 来确定队列中的消息类型显示有计时器( QS_TIMER )、发布的消息( QS_POSTMESSAGE )、“所有发布的消息”(即其他发布的消息, QS_ALLPOSTMESSAGE )和 10445 条消息( QS_PAINT)。

这就是它变得奇怪的地方。我正在尝试使用 PeekMessage PM_REMOVE 删除选择的消息或消息类型,以部分清空队列并计算每种类型消息的数量。

如果我打电话:
while (::PeekMessage(&oMsg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD | (QS_TIMER << 16)) != 0) {...

我收到一万多条消息,通常是 10006 条左右。并非所有这些都是 WM_TIMER:数千是 WM_APP+202,这是我们内部使用的一条消息,似乎没有(由我们)以如此大的数量在任何地方发布。我已经检查过:它只发送了几次。我们还使用了另外几千条 WM_APP+something 消息;这个可能真的是经常发送。

如果我改为调用它:
while (::PeekMessage(&oMsg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD) != 0) {...

我收到大约 10 条消息,所有这些消息都是真正的 WM_TIMER。为什么? PeekMessage 文档表明传递 QS_TIMER << 16 应该只处理计时器消息,但它会产生更多的消息,其中许多根本不是计时器。

最后,如果我改为调用第三个变体:
while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {...

这是直接过滤第一行代码返回数千条的自定义消息,我删除了 17 条消息。

我已经多次复制了所有这些 - 没有一个是一次性行为。

所以:
  • 为什么第一次调用 PeekMessage 删除的不仅仅是计时器(与第二次调用相比)?只是好奇,真的。
  • 为什么第一次调用 PeekMessage 会删除数千条 WM_APP+202 消息(我们定义和使用的消息并且不发送那么多消息),但如果我改为调用第三个变体,它直接过滤该特定消息,我得到 17?
  • 如果我对同一条消息得到如此不同的结果,我如何确定队列中的内容以及如何最好地避免它?
  • 或者为了避免上述所有情况:我可以安全地忽略所有这些,那么当 COM 即将尝试调用方法时,我应该如何处理完整的消​​息队列?

  • 我很困惑,很可能犯了一个基本错误 - 它已经到了寻找令人费解的地方的阶段。对 COM 问题的任何帮助或对消息行为的解释,包括“你犯了基本错误 X,天哪,你太蠢了”,将不胜感激:)

    最佳答案

    GetQueueStatus () 接受 QS_xxx参数但 PeekMessage () 只接受 PM_QS_xxx常数。

    这解释了WM_TIMER数量之间的差异。由 QueueStatus 指示的消息随后被 PeekMessage 删除()。您的 PeekMessage(PM_REMOVE)调用未删除 WM_TIMER消息,但完全是别的东西。

    我认为您误解了 PeekMessage 的文档()。 PM_QS_POSTMESSAGE被记录为与以下等价:

    ((QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16)

    及其他 PM_QS_xxx常量被记录为等于相应的 QS_xxx常数 << 16 ,但没有任何地方说情况始终如此,可以推断为所有 QS_xxxx常数。

    我怀疑 QS_TIMER << 16正在产生一些过滤器,它不仅仅是过滤 WM_TIMER消息(很明显,我不能肯定地说它产生了什么过滤器)。

    据我所知, WM_TIMER是唯一与计时器相关的消息,因此不需要为更大的 super 计时器消息集设置更宽的过滤器 - 没有这样的 super 集。如果您想过滤计时器消息,只需过滤 WM_TIMER .

    关于delphi - 什么解释了这种奇怪的 PeekMessage 行为(尝试处理完整的消​​息队列,过滤特定消息)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8229710/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com