Наше приложение действует как COM-сервер, где вся автоматизация происходит в пределах одной квартиры STA (в главном потоке приложения), а некоторые VBS-скрипты, которые выполняют длинные (> 10 минут) вызовы, не работают с ошибкой «Системный вызов не выполнен ( 80010100)». Некоторые исследования ( one , two , three ) показывают, что это, вероятно, вызвано заполнением очереди сообщений, поэтому, когда COM пытается вызвать следующий метод, он не может.
Если это важно, приложение разработано с Embarcadero RAD Studio 2010 (в основном C ++ , smatterings 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 (см. Раздел «Примечания»).
Вызов Application->ProcessMessages()
(вызывает цикл сообщений приложения до тех пор, пока он не станет пустым, для тех из вас, кто не является пользователем Delphi / C ++ Builder, - это вполне обычное «получить / перевести / отправить до тех пор, пока больше не будет сообщений») method) решает проблему, и сценарий 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 << 16 должна обрабатывать только сообщения таймера, но она генерирует значительно больше сообщений, многие из которых вообще не являются таймерами. </p>
Наконец, если я назову третий вариант вместо этого:
while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {...
, который фильтрует непосредственно для пользовательского сообщения, которое первая строка кода возвращает тысячи, я получаю семнадцать сообщений удалены.
Я воспроизвел все это несколько раз - ни один из них не является разовым поведением.
Итак:
- Почему первый вызов PeekMessage удаляет больше, чем просто таймеры (по сравнению со вторым вызовом)? Только любопытство.
- Почему первый вызов PeekMessage удаляет несколько тысяч сообщений WM_APP + 202 (одно мы определяем и используем и не отправляем столько), но если я вместо вызываю третий вариант, который Фильтры непосредственно для этого конкретного сообщения, я получаю 17?
- Если я получу такие разные результаты для одного и того же сообщения, как мне выяснить, что заполнило очередь и как лучше ее избежать?
- Или чтобы избежать всего вышеперечисленного: могу ли я спокойно проигнорировать все это, и тогда как мне обращаться с полной очередью сообщений, когда COM собирается попытаться вызвать метод?
Я озадачен и, возможно, допускаю элементарную ошибку - дошло до того, что вы смотрите на что-то загадочное, когда вы это делаете. Будем весьма благодарны за любую помощь в связи с проблемой COM или объяснения поведения сообщения, в том числе «Вы совершили элементарную ошибку X, черт возьми, которая была глупой с вашей стороны»:)