События пользователя это любое взаимодействие пользователя с программой, например перемещения курсора мыши, нажатие клавиш на клавиатуре и т.д.
События ОС включают в себя любое действие за пределами программы, которые могут повлиять на поведение программы. Например подключение нового устройства или переход в режим пониженного энергопотребления (а так же выключение или гибернация).
Все эти события могут происходить в любое время и в неизвестном порядке. Чтобы структурировать поток выполнения программы, Windows использует модель передачи сообщений. Она взаимодействует с окнами приложения передавая ему сообщения. По сути, сообщение это цифровой код, обозначающий конкретное событие. Например, при нажатии пользователем левой кнопки мыши окно получает сообщение со следующим кодом
#define WM_LBUTTONDOWN 0x0201Некоторые сообщения имеют связанные данные. Например, WM_LBUTTONDOWN содержит в качестве данных координаты x и y курсора мыши.
Для передачи этих сообщений ОС использует вызов оконной процедуры (WindowProc) зарегистрированной для окна.
Цикл Message Loop
Так как приложение будет получать тысячи сообщений во время своей работы, а так же иметь несколько окон со своей собственной оконной процедурой, программе необходимо будет каким-то образом получать все эти сообщения и передать их в нужную процедуру окна. Для этого в приложении запускается цикл Message Loop, который будет получать сообщение и отправлять их в нужную оконную процедуру.Для каждого потока, который создает окно ОС создает очередь оконных сообщений, которая содержит сообщения для всех окон этого потока. Очередь сообщений скрыта от выполняемой программы и нельзя управлять очередью напрямую. Но можно вытащить сообщение из очереди с помощью функции GetMessage.
MSG msg; GetMessage(&msg, NULL, 0, 0);Эта функция удаляет первое сообщение из головы очереди. Если она пуста, то функция блокируется до следующего сообщения в очереди. Блокирование GetMessage не будет означать остановку программы. Если сообщений нет, то программа не будет ничего выполнять. Т.е. иными словами GetMessage будет просто ожидать приход нового сообщения.
Функция GetMessage принимает 4 параметра. Первый параметр это адрес структуры MSG. Если функция завершается успешно, то она заполнит структуру информацией о сообщении. Остальные три параметра дают возможность фильтровать сообщения, получаемые из очереди. В большинстве случаев их устанавливают в ноль.
Несмотря на то, что MSG структура содержит информацию о сообщении, изучать ее не имеет никакого смысла. Вместо этого, эту структуру необходимо передать двум другим функциям
TranslateMessage(&msg); DispatchMessage(&msg);Функция TranslateMessage связанна с клавиатурой. Она переводит нажатия клавиш в код символа. Разработчику в большинстве случаем не нужно знать как она работает, главное не забывать вызывать ее перед DispatchMessage.
Функция DispatchMessage вызывает оконную процедуру, того окна с которым связанно данное событие.
Рассмотрим пример, пользователь нажимает левую кнопку мыши. Вот что происходит:
- ОС помещает сообщение WM_LBUTTONDOWN в очередь сообщений
- Программа вызывает функцию GetMessage
- GetMessage выталкивает сообщение WM_LBUTTONDOWN из очереди и заполняет структуру MSG
- Программа вызывает TranslateMessage и DispatchMessage
- Внутри DispatchMessage ОС вызывает оконную процедуру
- Оконная процедура может отвечать на сообщение или игнорировать его
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Вызов функции GetMessage должен быть расположен в условии цикла while. Так обеспечивается завершение цикла, когда GetMessage вернет нулевое значение. Всякий раз когда необходимо выйти из цикла Message Loop, необходимо вызвать функцию PostQuitMessage.
PostQuitMessage(0);
Функция PostQuitMessage ставит сообщение WM_QUIT в очередь сообщений. Сообщение WM_QUIT является специальным сообщением, оно заставляет GetMessage вернуть нулевое значение и таким образом завершить цикл Message Loop.
Комментариев нет:
Отправить комментарий