Одной опасностью функции MsgWaitForMultipleObjects является её вызов, когда в очереди есть сообщения для обработки - потому что MsgWaitForMultipleObjects возвращает управление, только когда у нас есть новые сообщения.
Другими словами, рассмотрим такой сценарий:
- PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) возвращает True, говоря, что у нас есть сообщение.
- Вместо обработки сообщения, вы игнорируете его и вызываете MsgWaitForMultipleObjects.
Частый вариант этих действий выглядит так:
- MsgWaitForMultipleObjects возвращает управление, сообщая, что у нас есть сообщение.
- Вы вызываете PeekMessage(Msg, 0, 0, 0, PM_REMOVE) и обрабатываете это сообщение.
- Вы вызываете MsgWaitForMultipleObject, чтобы ждать следующее сообщение.
Когда MsgWaitForMultipleObjects говорит вам, что у вас есть сообщение в очереди, вы должны обрабатывать все сообщения, пока PeekMessage не вернёт False, говоря, что сообщений больше нет.
Заметьте, однако, что такая последовательность не является проблемой:
- PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) возвращает False, указывая, что сообщений больше нет.
- Сообщение post-тся в вашу очередь.
- Вы вызываете MsgWaitForMultipleObjects и включаете флаг QS_ALLPOSTMESSAGE.
Функция MsgWaitForMultipleObjectsEx позволяет вам указать флаг MWMO_INPUTAVAILABLE, чтобы указать, что она должна проверять ранее игнорированный ввод.
Вооружённые этим знанием, объясните, почему наблюдаемое поведение нижеследующего кода таково: "иногда моя программа застревает и сообщает на одну запись меньше, чем должна. Мне приходится подёргать мышью, чтобы обновились значения. Через некоторое время она отстаёт на два значения, а потом - на три..."
// Предположим, что у нас есть рабочий поток, который обрабатывает записи и
// отправляет сообщение WM_NEWRECORD для каждой новой записи
function WaitForNRecords(h: THandle; cRecordsExpected: Integer): Boolean;
var
Msg: TMessage;
cRecords: Integer;
begin
cRecords := 0;
while True do
begin
case MsgWaitForMultipleObjects(1, h, False, INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0:
DoSomethingWith(h); // сработало событие
WAIT_OBJECT_1:
begin
// У нас есть сообщение - обрабатываем его
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
if SendMessage(hwndNotify, WM_GETRECORDCOUNT, 0, 0) >= cRecordsExpected then
Exit(True); // у нас достаточно записей
end;
else
Exit(False); // неожиданная ошибка
end;
end;
end;
Хорошая задачка :)
ОтветитьУдалитьнадо заменить
// У нас есть сообщение - обрабатываем его
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
на
// У нас есть сообщение - обрабатываем его
while not PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
Только не "while not PeekMessage", а "while PeekMessage".
Удалить