-
Всем доброго времени суток! Задача - при выполнении метода не возвращать управление в основной цикл выборки сообщений, а запустить свой цикл по фильтру пока не придет определенное сообщение. Наподобие того, как работает цикл модальной формы. Только мне нужно выхватывать из цикла не все сообщения, а только одно определенное. Код такой:
const
AM_MARSHAL = WM_USER + 51;
function ...
var
msg: TMsg;
doLoop, retReceived: Boolean;
requestID: Cardinal;
begin
...
doLoop := True;
retReceived := False;
while doLoop do
begin
while
not retReceived and
PeekMessage(msg, 0, AM_MARSHAL, AM_MARSHAL, PM_REMOVE or PM_NOYIELD) do
begin
retReceived :=
not proxyApt.ProcessMarshalMsg(msg.wParam, msg.дParam) and
(PAptMarshalData(msg.wParam).ID = marshalData.ID);
end;
doLoop :=
not retReceived and doLoop and
not PeekMessage(msg, 0, WM_QUERYENDSESSION, WM_QUIT, PM_NOREMOVE or PM_NOYIELD);
if doLoop then
doLoop := WaitMessage;
end;
...
end; Проблема в том, что WaitMessage не ждет новых сообщений и возвращается сразу же, в результате цикл гоняется непрерывно, полностью загружая процессор. Хотя в мсдн написано что она должна возвращаться только тогда, когда приходят новые сообщения, вне зависимости от количества оных в очереди на момент ее вызова. Отладчиком проскакиваю вызов WaitMessage без каких-либо задержек. Что делать, как исправить - не пойму... Подскажите, куда копать?
-
> Отладчиком проскакиваю вызов WaitMessage без каких-либо > задержек
sleep(1) ?
-
Со Sleep(1) не грузит проц, но цикл так же работает постоянно. Думаю что это как-то неправильно
-
> не грузит проц, но цикл так же работает постоянно. Думаю > что это как-то неправильно
ты хотел разгрузить проц? разгрузил? ;) зы можно еще подумать)
-
Цель не замаскировать проблему, а решить ее :)
-
> а решить ее :)
имхо, проблема нагрузки проца - решена)
-
Проблема не в загрузке проца, а в том, что WaitMessage не выполняет своих функций, хотя вроде всё по msdn. Почему - надо разобраться.
-
> SPeller © (21.12.09 05:10)
> Проблема в том, что WaitMessage не ждет новых сообщений > и возвращается сразу же
А где видно, что не ждет новых сообщений? Вполне возможно, что они и пришли уже. И еще, можно наверное посмотреть для твоей цели функцию GetMessage?
-
> Вполне возможно, что они и пришли уже
WaitMessage должен ждать прихода новых сообщений, вне зависимости, сколько сообщений в очереди на момент вызова.
> можно наверное посмотреть для твоей цели функцию GetMessage?
Ага, согласен
-
С GetMessage всё хорошо, и код меньше. Но всё-равно интересует, почему с PeekMessage/WaitMessage не получается сделать красиво? Вдруг понадобится более сложная фильтрация сообщений? msdn говорит что GetMessage реагирует только на WM_QUIT, а если нужно обработать WM_QUERYENDSESSION?
-
Может, я что-то неправильно понимаю? The WaitMessage function suspends the thread and does not return until a new message is placed in the thread's message queue
Note that WaitMessage does not return if there is unread input in the message queue after the thread has called a function to check the queue. This is because functions such as ... WaitMessage, ... check the queue and then change the state information for the queue so that the input is no longer considered new. A subsequent call to WaitMessage will not return until new input of the specified type arrives. The existing unread input (received prior to the last time the thread checked the queue) is ignored. Но при этом даже вот в таком случае: procedure TForm1.Button5Click(Sender: TObject);
begin
while True do
WaitMessage;
end; цикл работает постоянно... Может, дело в том, что WaitMessage вызывается из обработчика сообщения?
-
SPeller © (21.12.09 06:56) [8]
> Вполне возможно, что они и пришли уже
> WaitMessage должен ждать прихода новых сообщений, вне зависимости, > сколько сообщений в очереди на момент вызова.
Не совсем так, это вольный перевод/интерпретация, но не затевая спора повторю вопрос иначе - А где видно, что они не пришли, новые-то сообщения? При работе с отладчиком я получаю как минимум несколько новых. Да и без него они есть, то перерисовка, то мышка двинулась то еще что-то.
> msdn говорит что GetMessage реагирует только на WM_QUIT, > а если нужно обработать WM_QUERYENDSESSION?
Это тоже вольный перевод(хотя надо признать и я не знаток английского). В MSDN говорится, что функция вернет 0 на WM_QUIT, и WM_QUIT будет принят независимо от фильтра, но я не прочитал, что она не примет сообщение WM_QUERYENDSESSION (если WM_QUERYENDSESSION не исключена фильтром)
-
> SPeller © (21.12.09 08:03) [10]
> Может, я что-то неправильно понимаю? .. > цикл работает постоянно...
Ну, а где ж "выборка сообщений"? Это PeekMessage with PM_REMOVE. Или GetMessage, но без WaitMessage.
-- Regards, LVT.
-
> А где видно, что они не пришли, новые-то сообщения?
Приложение запущено, висит в указанном выше цикле, запущен Task Manager и показывает в трее постоянную 100%-ю загрузку проца. В обычном режиме этого быть не должно однозначно.
> Ну, а где ж "выборка сообщений"? > Это PeekMessage with PM_REMOVE. > Или GetMessage, но без WaitMessage.
А как же functions such WaitMessage check the queue and then change the state information for the queue so that the input is no longer considered new? Тут ведь явно написано что функция WaitMessage должна пометить очередь как не содержащую новых событий, в результате чего WaitMessage function suspends the thread and does not return until a new message is placed in the thread's message queue...
-
> свой цикл по фильтру пока не придет определенное сообщение
А зачем тут цикл-то ? Ты же (см. цитату) ждешь всего одно интересующее тебя сообщение ?
if GetMessage(.. твой фильтр ..) then //ждешь если в очереди нет сообщения, соответствующего фильтру, или немедленно получаешь его, если иначе .. обрабатывай его .. else .. потоку послано WM_QUIT ..
-
> Хотя в мсдн написано что она должна возвращаться только > тогда, когда приходят новые сообщения, вне зависимости от > количества оных в очереди на момент ее вызова
Это ты чего-то не так прочитал.
The WaitMessage function yields control to other threads when a thread has no other messages in its message queue
Для того, чтобы пометить неинтересные тебе невыбранные сообщения в очереди, как "старые", нужно вызвать GetQueueStatus с соответствующим флагом, но при этом сообщения могут потеряться (Ну или Get/PeekMessage,MsgWaitForMultipleObjects). То есть, WaitMessage не предназначена для того, чтобы отправлять поток в спячку, если в очереди нет сообщения определенного типа.
Рихтер по этому поводу довольно подробно пишет, его можно почитать.
Система помещает в очередь потока только сообщения ввода (WM_KEY, ...WM_MOUSE) и таймера, все остальные сообщения она посылает синхронно. Quit и Paint в очередь не посылаются, для них в очереди выставляются флажки. Приложения, разумеется, могут ставить в очередь сообщения, но система ставит только перечисленные. Ну и соответственно, у очереди имеются флаги, какого типа НЕОБРАБОТАННЫЕ сообщения в ней находятся.
Вызвав GetQueueStatus (QS_POSTMESSAGE) ты узнаешь, что в очереди имеется сообщение, поставленное не системой, а, скажем, другим потоком (ну или этим же).
-
> SPeller © (21.12.09 08:48) [13]
> WaitMessage check the queue and then change the state
Ну, ты ж убедился, что в жизни это не так.
-- Regards, LVT.
-
> Игорь Шевченко © (21.12.09 12:41) [15]
Цитата, видимо, из Remarks
This is because functions such as PeekMessage, GetMessage, GetQueueStatus, _WaitMessage_, MsgWaitForMultipleObjects, and MsgWaitForMultipleObjectsEx check the queue and then change the state information for the queue so that the input is no longer considered new.
Подчеркнуто мной. Это неточность, не знаю, стоит ли делать Add new content и насколько оно действенно.
-- Regards, LVT.
-
Leonid Troyanovsky © (21.12.09 12:56) [17]
Да, WaitMessage не помечает имеющиеся необработанные (и непомеченные) сообщения, как старые - она просто возвращает управление в этом случае.
> Это неточность, не знаю, стоит ли делать Add new content
А кто его знает, в обработке сообщений и так черт ногу сломит, они ж учитывают совместимость с Windows 3.x, я честно всегда стараюсь сообщения обрабатывать без особенных извратов :)
-
> А зачем тут цикл-то ? > Ты же (см. цитату) ждешь всего одно интересующее тебя сообщение > ?
Сейчас да, одно. Но оно может предназначаться не этому циклу, поэтому его понадобится пропустить и ждать следующее. В будущем может быть и не одно сообщение. И даже не по порядку. Сейчас сделал через GetMessage, всё отлично работает, но интересно разобраться что к чему чтобы в будущем на грабли не наступать. > Для того, чтобы пометить неинтересные тебе невыбранные сообщения > в очереди, как "старые", нужно вызвать GetQueueStatus с > соответствующим флагом
Попробовал:
while True do
begin
GetQueueStatus(QS_ALLEVENTS);
WaitMessage;
end;
Не помогло :) И вот так тоже
tmp: Integer;
begin
tmp := CreateEvent(nil, True, False, nil);
while True do
begin
MsgWaitForMultipleObjects(1, tmp, False, INFINITE, QS_ALLEVENTS);
WaitMessage;
end;
Вобщем, фокусы с WaitMessage никак не проходят. А вот так получилось:
tmp := 0;
while True do
begin
GetQueueStatus(QS_ALLEVENTS);
MsgWaitForMultipleObjects(0, tmp, False, INFINITE, QS_ALLEVENTS);
end;
Поток просыпается только тогда, когда приходят сообщения. Например, кликаю по кнопке формы в таскбаре и цикл пробегает 1 раз. Думаю, на этом варианте можно остановиться.
-
QS_ALLEVENTS
А это зачем ? Тебе же надо ждать только своих сообщений. И потом GetQueueStatus что-то возвращает, как ни странно.
Тебе скорее подойдет MsgWaitForMultipleObjects(0, tmp, False, INFINITE, QS_POSTMESSAGE);
-
Это я упрощенно написал пример с QS_POSTMESSAGE, чтобы знать как остановить поток. А с флагами и результатами да, на месте надо будет разбираться.
|