Конференция "WinAPI" » Проблема с циклом выборки сообщений
 
  • SPeller © (21.12.09 05:10) [0]
    Всем доброго времени суток!
    Задача - при выполнении метода не возвращать управление в основной цикл выборки сообщений, а запустить свой цикл по фильтру пока не придет определенное сообщение. Наподобие того, как работает цикл модальной формы. Только мне нужно выхватывать из цикла не все сообщения, а только одно определенное. Код такой:


    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 без каких-либо задержек. Что делать, как исправить - не пойму... Подскажите, куда копать?
  • brother © (21.12.09 05:15) [1]
    > Отладчиком проскакиваю вызов WaitMessage без каких-либо
    > задержек

    sleep(1) ?
  • SPeller © (21.12.09 05:27) [2]
    Со Sleep(1) не грузит проц, но цикл так же работает постоянно. Думаю что это как-то неправильно
  • brother © (21.12.09 05:30) [3]
    > не грузит проц, но цикл так же работает постоянно. Думаю
    > что это как-то неправильно

    ты хотел разгрузить проц? разгрузил? ;)
    зы можно еще подумать)
  • SPeller © (21.12.09 05:36) [4]
    Цель не замаскировать проблему, а решить ее :)
  • brother © (21.12.09 05:37) [5]
    > а решить ее :)

    имхо, проблема нагрузки проца - решена)
  • SPeller © (21.12.09 05:42) [6]
    Проблема не в загрузке проца, а в том, что WaitMessage не выполняет своих функций, хотя вроде всё по msdn. Почему - надо разобраться.
  • Вариант (21.12.09 06:50) [7]

    > SPeller ©   (21.12.09 05:10)


    > Проблема в том, что WaitMessage не ждет новых сообщений
    > и возвращается сразу же

    А где видно, что не ждет новых сообщений? Вполне возможно, что они и пришли уже.  И еще, можно наверное посмотреть для твоей цели функцию GetMessage?
  • SPeller © (21.12.09 06:56) [8]

    > Вполне возможно, что они и пришли уже

    WaitMessage должен ждать прихода новых сообщений, вне зависимости, сколько сообщений в очереди на момент вызова.


    > можно наверное посмотреть для твоей цели функцию GetMessage?

    Ага, согласен
  • SPeller © (21.12.09 07:09) [9]
    С GetMessage всё хорошо, и код меньше. Но всё-равно интересует, почему с PeekMessage/WaitMessage не получается сделать красиво? Вдруг понадобится более сложная фильтрация сообщений? msdn говорит что GetMessage реагирует только на WM_QUIT, а если нужно обработать WM_QUERYENDSESSION?
  • SPeller © (21.12.09 08:03) [10]
    Может, я что-то неправильно понимаю?

    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 вызывается из обработчика сообщения?
  • Вариант (21.12.09 08:05) [11]
    SPeller ©   (21.12.09 06:56) [8]

    > Вполне возможно, что они и пришли уже


    > WaitMessage должен ждать прихода новых сообщений, вне зависимости,
    >  сколько сообщений в очереди на момент вызова.


    Не совсем так, это вольный перевод/интерпретация, но не затевая спора повторю вопрос иначе - А где видно, что они не пришли, новые-то сообщения? При работе с отладчиком я получаю как минимум несколько новых. Да и без него они есть, то перерисовка, то мышка двинулась то еще что-то.


    > msdn говорит что GetMessage реагирует только на WM_QUIT,
    >  а если нужно обработать WM_QUERYENDSESSION?

    Это тоже вольный перевод(хотя надо признать и я не знаток английского). В MSDN говорится, что функция вернет 0 на WM_QUIT, и WM_QUIT будет принят независимо от фильтра, но я не прочитал, что она не примет сообщение WM_QUERYENDSESSION (если WM_QUERYENDSESSION  не исключена фильтром)
  • Leonid Troyanovsky © (21.12.09 08:27) [12]

    > SPeller ©   (21.12.09 08:03) [10]

    > Может, я что-то неправильно понимаю?
    ..
    > цикл работает постоянно...

    Ну, а где ж "выборка сообщений"?
    Это PeekMessage with PM_REMOVE.
    Или GetMessage, но без WaitMessage.

    --
    Regards, LVT.
  • SPeller © (21.12.09 08:48) [13]

    > А где видно, что они не пришли, новые-то сообщения?

    Приложение запущено, висит в указанном выше цикле, запущен 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...
  • Сергей М. © (21.12.09 12:39) [14]

    > свой цикл по фильтру пока не придет определенное сообщение


    А зачем тут цикл-то ?
    Ты же  (см. цитату) ждешь всего одно интересующее тебя сообщение ?

    if GetMessage(.. твой фильтр ..) then //ждешь если в очереди нет сообщения, соответствующего фильтру, или немедленно получаешь его, если иначе
    .. обрабатывай его ..
    else
    .. потоку послано WM_QUIT ..
  • Игорь Шевченко © (21.12.09 12:41) [15]

    > Хотя в мсдн написано что она должна возвращаться только
    > тогда, когда приходят новые сообщения, вне зависимости от
    > количества оных в очереди на момент ее вызова


    Это ты чего-то не так прочитал.

    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) ты узнаешь, что в очереди имеется сообщение, поставленное не системой, а, скажем, другим потоком (ну или этим же).
  • Leonid Troyanovsky © (21.12.09 12:50) [16]

    > SPeller ©   (21.12.09 08:48) [13]


    > WaitMessage check the queue and then change the state

    Ну, ты ж убедился, что в жизни это не так.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (21.12.09 12:56) [17]

    > Игорь Шевченко ©   (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.
  • Игорь Шевченко © (21.12.09 13:13) [18]
    Leonid Troyanovsky ©   (21.12.09 12:56) [17]

    Да, WaitMessage не помечает имеющиеся необработанные (и непомеченные) сообщения, как старые - она просто возвращает управление в этом случае.


    > Это неточность, не знаю, стоит ли делать Add new content


    А кто его знает, в обработке сообщений и так черт ногу сломит, они ж учитывают совместимость с Windows 3.x, я честно всегда стараюсь сообщения обрабатывать без особенных извратов :)
  • SPeller © (22.12.09 00:42) [19]

    > А зачем тут цикл-то ?
    > Ты же  (см. цитату) ждешь всего одно интересующее тебя сообщение
    > ?
    Сейчас да, одно. Но оно может предназначаться не этому циклу, поэтому его понадобится пропустить и ждать следующее. В будущем может быть и не одно сообщение. И даже не по порядку. Сейчас сделал через 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 раз. Думаю, на этом варианте можно остановиться.
 
Конференция "WinAPI" » Проблема с циклом выборки сообщений
Есть новые Нет новых   [134431   +15][b:0][p:0.002]