我们的应用程序充当COM服务器,其中所有自动化都发生在单个STA公寓内(在应用程序的主线程中),并且一些进行冗长(> 10分钟)调用的VBS脚本失败并显示错误“系统调用失败(80010100)”。一些研究(one,two,three)表明这可能是由消息队列填满引起的,因此当COM尝试调用下一个方法时,它无法进行。
如果它很重要,应用程序的开发时间为Embarcadero RAD Studio 2010(大部分为C++,某些COM类的Delphi字节为。{/ p>
我以为我会在漫长的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用户的人来说 - 这是一个相当正常的“获取/翻译/调度直到不再消息“方法”解决了问题,COM脚本可以成功调用下一个方法。虽然在这种特定情况下可能还行,但在有效的随机位置调用ProcessMessages()
是可以避免的 - 它可以导致重新入侵。我想找出导致队列充满的原因。
使用GetQueueStatus
确定队列中的消息类型显示有计时器(QS_TIMER
),已发布消息(QS_POSTMESSAGE
),“所有已发布消息”(即其他已发布的,QS_ALLPOSTMESSAGE
)和绘制消息(QS_PAINT
)。
这是奇怪的地方。我正在尝试使用带有PM_REMOVE
的{{3}}删除选择的消息或消息类型,以便部分清空队列并计算每种消息类型的数量。
如果我打电话:
while (::PeekMessage(&oMsg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD | (QS_TIMER << 16)) != 0) {...
我收到的信息超过一万条,通常是10006条左右。并非所有这些都是WM_TIMER:数千是WM_APP + 202,我们在内部使用的消息, not 似乎是在我们这样大量的任何地方发布(由我们)。我已经检查了这个:它只发送了几次。我们还使用了几千条WM_APP+something
消息;这个可能真的经常发送。
如果我这样称呼:
while (::PeekMessage(&oMsg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD) != 0) {...
我收到大约十条消息,所有消息都是真正的WM_TIMER。为什么? PeekMessage文档表明传递QS_TIMER&lt;&lt; 16应该只处理定时器消息,但它会产生更多的消息,其中许多消息根本不是定时器。
最后,如果我改为调用第三个变体:
while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {...
直接过滤自定义消息,第一行代码返回数千个,我删除了 17个消息。
我已经多次复制了这一切 - 没有一次是一次性行为。
所以:
我很困惑,很可能犯了一个基本的错误 - 它已经到了一个令人费解的事情的阶段。任何有关COM问题的帮助或消息行为的解释,包括'你犯了基本错误X,对你来说是愚蠢的',将不胜感激:)
答案 0 :(得分:1)
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
1}}常数。
我怀疑QS_TIMER << 16
正在产生一些过滤器,这不只是过滤WM_TIMER
消息(显然它是,我不能肯定地说它产生了什么过滤器)。
据我所知,WM_TIMER
是唯一与定时器相关的消息,因此不需要为更大的超级定时器消息集提供更宽的过滤器 - 没有这样的超级集。如果您想过滤计时器消息,只需过滤WM_TIMER
。