Цикл сообщений блокируется, когда меню приложения имеет фокус - PullRequest
2 голосов
/ 20 сентября 2009

Я разрабатываю приложение, которое выглядит в основном так:

while (true)
{
    while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
    {
       TranslateMessage(&Msg);
       DispatchMessage(&Msg);
    }
    DoSomething();
    Sleep(1);
}

Что я заметил, так это то, что DoSomething () не вызывается, когда я щелкаю по строке меню (отображая пункты меню). Я заметил, что вызов DispatchMessage блокирует цикл Messae, пока я не выхожу из строки меню!

Как я мог избежать этого поведения ??

Спасибо!

Ответы [ 2 ]

2 голосов
/ 20 сентября 2009

Причина в том, что Windows берет на себя обработку сообщений, когда отображается что-то вроде меню приложения или окна сообщения, и тот цикл обработки сообщений, который использует Windows, не вызовет ваш метод DoSomething(). Это может быть трудно визуализировать, поэтому я постараюсь пройтись по происходящему:

  • Когда кто-то открывает ваше меню, в ваше окно отправляется сообщение с просьбой нарисовать окно. DispatchMessage() отправляет сообщение на ваш WndProc, как и все другие сообщения.
  • Поскольку вы не обрабатываете это сообщение, оно передается в Windows (так как ваш WndProc более вероятно вызывает DefWindowProc)
  • В качестве операции по умолчанию Windows рисует меню и запускает другой цикл сообщений по умолчанию , который не будет вызывать DoSomething()
  • Этот цикл извлекает сообщения, предназначенные для вашего приложения, и отправляет их в ваше приложение, вызывая WndProc, чтобы ваше приложение не зависало и продолжало работать (за исключением вызова DoSomething()).
  • Как только меню закроется, управление вернется в ваш цикл сообщений (только в этот момент вызов DispatchMessage() с самого начала вернется)

Другими словами, когда отображается меню, ваш цикл сообщений заменяется циклом по умолчанию, который выглядит следующим образом (например)

while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
} 

, который, как вы можете видеть, не будет вызывать ваш DoSomething() метод.

Чтобы проверить это, попробуйте приостановить ваш код в отладчике, когда меню не отображается, а когда оно есть. Если вы видите стек вызовов, вы увидите, что при отображении меню сообщения обрабатываются циклом сообщений Windows, а не вашим.

Единственный обходной путь, о котором я могу подумать (без многопоточности), - это если вы запускаете таймер и обрабатываете сообщение WM_TIMER, вызывая DoSomething(), но это не будет идеальным решением (поскольку я предполагаю, что ваше намерение состоит в том, чтобы вызвать DoSomething() только когда не осталось сообщений для обработки).

0 голосов
/ 20 сентября 2009

Spin off Перевод и отправка сообщения в отдельную тему.

Пока DoSomething не зависит от отправки сообщения.

Хотя я мог бы хотеть понять, почему Dispatch блокируется; это ожидаемое нормальное поведение?

...