Наше приложение работает как COM-сервер, где вся автоматизация происходит в пределах одной квартиры STA (в главном потоке приложения), и некоторые VBS-скрипты, которые делают длительные (>10 минут) вызовы, терпят неудачу с ошибкой "System call failed (80010100)". Некоторые исследования (один, два, три) показывают, что это, вероятно, вызвано заполнением очереди сообщений, так что когда COM пытается вызвать следующий метод, он не может этого сделать.
Если это важно, приложение разработано в Embarcadero RAD Studio 2010 (в основном C++, немного Delphi для некоторых COM-классов)
Я решил исследовать очередь сообщений потока в конце длинного вызова метода 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. Мой вывод заключается в том, что заполненность очереди сообщений действительно является причиной проблемы. Лимит очереди сообщений в моей системе по умолчанию 10000 (см. раздел Remarks.)
Вызов Application->ProcessMessages()
(вызывает цикл сообщений приложения, пока он не опустеет, для тех из вас, кто не является пользователем Delphi / C++Builder - это вполне обычный метод "получить/перевести/отправить, пока больше нет сообщений") решает проблему, и COM-скрипт может успешно вызвать следующий метод. Хотя, возможно, в данной конкретной ситуации все в порядке, вызова ProcessMessages()
в фактически случайных местах следует избегать - это может привести к реентерабельности. Я бы хотел выяснить причину переполнения очереди, если это возможно.
Использование GetQueueStatus
для определения типа сообщений в очереди показывает, что есть таймер (QS_TIMER
), опубликованные сообщения (QS_POSTMESSAGE
), 'все опубликованные сообщения' (т.е. другие опубликованные сообщения, ). т.е. другие опубликованные, QS_ALLPOSTMESSAGE
), и сообщения рисования (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) {...
я получу около десяти сообщений, все из которых действительно WM_TIMER. Почему? Документация PeekMessage указывает, что передача QS_TIMER
Наконец, если вместо этого я вызываю третий вариант,
while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {...
который фильтрует непосредственно пользовательское сообщение, тысячи которых возвращает первая строка кода, я получаю семнадцать удаленных сообщений.
Я воспроизводил все это несколько раз - ничего из этого ни разу не...отклонение поведения.
Итак:
Я в недоумении, и вполне возможно, что делаю элементарную ошибку - дошел до стадии разглядывания чего-то загадочного, когда так поступаешь. Любая помощь в решении проблемы с COM или объяснения поведения сообщений, включая "Вы совершили элементарную ошибку X, черт возьми, это было глупо с вашей стороны", будут очень признательны :)