Dav*_*vid 6 delphi com winapi c++builder message-queue
我们的应用程序充当COM服务器,其中所有自动化都发生在单个STA公寓内(在应用程序的主线程中),并且一些进行冗长(> 10分钟)调用的VBS脚本失败,并显示错误"系统调用失败(80010100)" .一些研究(一,二,三)表明这可能是由消息队列填满引起的,因此当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
}
Run Code Online (Sandbox Code Playgroud)
当在一个简短的COM调用方法结束时运行可以发布数千(例如,9996)消息; 在导致脚本失败的冗长方法调用结束时,它可以发布0.我的结论是消息队列已满是真正导致问题的原因.我的系统的消息队列限制是默认值10000(请参阅"备注"部分.)
调用Application->ProcessMessages()
(调用应用程序的消息循环,直到它为空,对于那些不是Delphi/C++ Builder用户的人来说 - 这是一个相当正常的"get/translate/dispatch,直到没有更多的消息"方法)解决了问题, COM脚本可以成功调用下一个方法.虽然在这种特定情况下可能还可以,但ProcessMessages()
有效地随意调用随机点是可以避免的 - 它可以导致重新进入.如果可能的话,我想找出导致队列满的原因.
使用GetQueueStatus
以确定什么样的消息在队列中发现,有计时器(QS_TIMER
),发布的消息(QS_POSTMESSAGE
),"所有发布的消息"(即其他发布者,QS_ALLPOSTMESSAGE
)和绘制消息(QS_PAINT
).
这就是它变得奇怪的地方.我正在尝试使用PeekMessage
with 删除选择消息或消息类型,PM_REMOVE
以部分清空队列并计算每种消息类型的数量.
如果我打电话:
while (::PeekMessage(&oMsg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD | (QS_TIMER << 16)) != 0) {...
Run Code Online (Sandbox Code Playgroud)
我收到的信息超过一万条,通常是10006条左右.并非所有这些都是WM_TIMER:数千是WM_APP + 202,我们在内部使用的消息,似乎没有在如此大量的数量附近发布(由我们).我已经检查了这个:它只发送了几次.我们还使用了几千条另WM_APP+something
一条消息; 这个可能真的是经常发送的.
如果我这样称呼:
while (::PeekMessage(&oMsg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD) != 0) {...
Run Code Online (Sandbox Code Playgroud)
我得到了大约十条消息,所有消息都是真正的WM_TIMER.为什么?PeekMessage文档表明,传递QS_TIMER << 16应仅处理定时器消息,但它会产生更多的消息,其中许多消息根本不是定时器.
最后,如果我改为调用第三个变体:
while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {...
Run Code Online (Sandbox Code Playgroud)
这是直接过滤自定义消息,第一行代码返回数千,我删除了17条消息.
我已经多次复制了这一切 - 没有一次是一次性行为.
所以:
我很困惑,很可能犯了一个基本的错误 - 它已经进入了一个令人费解的事情的阶段.任何有关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)
Run Code Online (Sandbox Code Playgroud)
其他PM_QS_xxx
常量被记录为等于相应的QS_xxx
常量<< 16
,但没有任何地方表明情况始终如此并且可以外推到所有QS_xxxx
常量。
我怀疑这QS_TIMER << 16
会产生一些过滤器,它的作用不仅仅是过滤WM_TIMER
消息(显然是这样,我只是不能肯定地说它会产生什么过滤器)。
据我所知,WM_TIMER
是唯一与计时器相关的消息,因此不需要为更大的计时器消息超集设置更广泛的过滤器 - 没有这样的超集。如果你想过滤计时器消息,只需过滤即可WM_TIMER
。