logo
Ответы_ОСиСП

Понятие оконного сообщения. Источники сообщений. Очередь сообщений. Цикл приема и обработки сообщений. Процедура обработки сообщений.

В 16-разрядной Windows передача сообщения в окно всегда осуществляется синхронно: отправитель не может продолжить работу, пока окно не обработает полученное сообщение. Обычно так и нужно. Но, если на обработку сообщения потребуется длительное время или если окно «зависнет», выполнение отправителя просто прекратится. А значит, такая операционная система не вправе претендовать на устойчивость к сбоям.

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

Флаг QS_SENDMESSAGE указывает, что сообщение появилось в очереди синхронных сообщений.

Недокументированный флаг состояния очереди — QS_QUIT. Он устанавливается при вызове потоком PostQuitMessage. Сообщение WM_QUIT при этом не добавляется к очереди сообщений.

Флаг QS_PAINT устанавливается, если в окне, созданном данным потоком, имеется недействительная, требующая перерисовки область. Когда область, занятая всеми окнами, созданными одним потоком, становится действительной (обычно в результате вызова ValidateRect, ValidateRegion или BeginPaint), флаг QS_PAINT сбрасывается.

Флаг QS_TIMER устанавливается после срабатывания таймера (созданного потоком). После того как функция GetMessage или PeekMessage вернет WM_TIMER, флаг

сбрасывается и остается в таком состоянии, пока таймер вновь не сработает.

Алгоритм выборки сообщений из очереди потока

Когда поток вызывает GetMessage или PeekMessage, система проверяет флаги состояния очередей потока и определяет, какое сообщение надо обработать

1. Если флаг QS_SENDMESSAGE установлен, система отправляет сообщение соответствующей оконной процедуре GetMessage и PeekMessage контролируют процесс обработки и передают управление потоку сразу после того, как оконная процедура обработает сообщение, вместо этого обе функции ждут следующего сообщения.

2. Если очередь асинхронных сообщений потока не пуста, GetMessage и Peek Message заполняют переданную им структуру MSG и возвращают управление Цикл выборки сообщений (расположенный в потоке) в этот момент обычно обращается к DispatchMessage, чтобы соответствующая оконная процедура обработала сообщение.

3. Если флаг QS_QUIT установлен, GetMessage и PeekMessage возвращают сообщение WM__QUIT (параметр wParam которого содержит указанный код завершения) и сбрасывают этот флаг.

4 Если в очереди виртуального ввода потока есть какие-то сообщения, GetMessage и PeekMessage возвращают сообщение, связанное с аппаратным вводом.

5. Если флаг QS_PAINT установлен, GetMessage и PeekMessage возвращают сообщение WM_PAINT для соответствующего окна

6 Если флаг QS_TIMER установлен, GetMessage и PeekMessage возвращают сообщение WM_TIMER.

Работая с программой, пользователь может нажать кнопку мыши, что приводит к генерации последовательности определенных событий. А программа порождает отдельные события, асинхронно отправляя сообщения в очередь потока.

7. Синхронные и асинхронные сообщения, их передача и обработка. Ввод данных с манипулятора «мышь». Обработка сообщений мыши. Ввод данных с клавиатуры. Понятие фокуса ввода. Обработка сообщений от клавиатуры.

Синхронными сообщениями называются сообщения, которые Windows помещает в очередь сообщений приложения. Такие сообщения извлекаются и диспетчеризируются в цикле обработки сообщений (нажатие клавиш, перемещение мыши или щелчок левой кнопкой мыши, сообщения от таймера, сообщение о необходимости перерисовки клиентской области и сообщение о выходе из программы). Приложение может само направить в очередь синхронное сообщение, вызвав функцию PostMessage.

Асинхронные сообщения передаются непосредственно окну, когда Windows вызывает оконную процедуру. Во многих случаях асинхронные сообщения являются результатом обработки синхронных сообщений. Вообще, когда асинхронное сообщение обрабатывается функцией DefWindowProc, Windows часто генерирует другие сообщения, направляемые оконной процедуре. Приложение также может послать асинхронное сообщение, вызвав функцию SendMessage.

Таким образом, оконная процедура должна быть повторно входимой (reentrant program). Это означает, что Windows часто вызывает функцию WndProc с новым сообщением, появившимся в результате вызова DefWindowProc из WndProc при обработке предыдущего сообщения.

Рассмотрим, например, какие события произойдут после щелчка кнопкой мыши на кнопке закрытия окна приложения Hellol. Все начнется с того, что Windows отправит асинхронное сообщение WM_SYSCOMMAND оконной процедуре WndProc. Оконная процедура передаст это сообщение на обработку функции DefWindowProc. Функция DefWindowProc реагирует на него, отправляя сообщение WM_CLOSE оконной процедуре. В рассматриваемом примере предусмотрена обработка этого сообщения — вызывается функция DestroyWindow. Однако если не предусмотреть эту обработку, то функция DefWindowProc сделала бы то же самое, то есть вызвала бы функцию DestroyWindow. Функция DestroyWindow заставляет Windows отправить оконной процедуре сообщение WM_DESTROY. И наконец, WndProc, обрабатывая это сообщение, вызывает функцию PostQuitMessage, которая посылает синхронное сообщение WM_QUIT в очередь сообщений приложения. Сообщение WM_QUIT прерывает цикл обработки сообщений в WinMain, и приложение завершает свою работу.

Сообщения не похожи на аппаратные прерывания. Пока оконная процедура обрабатывает одно сообщение, программа не может быть прервана другим сообщением. Только в том случае, когда функция, выполняемая в теле оконной процедуры, генерирует новое асинхронное сообщение, оно вызывает повторно оконную процедуру, и только после его обработки выполнение прерванной функции продолжается.

Ввод с клавиатуры направляется потоком необработанного ввода (RTT) в очередь виртуального ввода какого-либо потока, но только не в окно. RTT помещает события от клавиатуры в очередь потока безотносительно конкретному окну. Когда поток вызывает GetMessage, событие от клавиатуры извлекается из очереди и перенаправляется окну (созданному потоком), на котором в данный момент сосредоточен фокус ввода.

Чтобы направить клавиатурный ввод в другое окно, нужно указать, в очередь какого потока RIT должен помещать события от клавиатуры, а также "сообщить" переменным состояния ввода потока, какое окно будет находиться в фокусе. Одним вызовом SetFocus эти задачи не решить. Если в данный момент ввод от RIT получает поток 1, то вызов SetFocus с передачей описателей окон А, В или С приведет к смене фокуса. Окно, теряющее фокус, убирает используемый для обозначения фокуса прямоугольник или гасит курсор ввода, а окно, получающее фокус, рисует такой прямоугольник или показывает курсор ввода.

Когда фокус переводится с одного окна на другое обычным способом (например, щелчком окна), теряющее фокус окно получает сообщение WM_KILLFOCUS. Если окно, получающее фокус, принадлежит другому потоку, переменные локального со стояния ввода потока, который владеет окном, теряющим фокус, обновляются так, чтобы показать: окон в фокусе нет. И вызов GetFocus возвращает при этом NULL, заставляя поток считать, что окон в фокусе нет.

Функция SetActiveWindow активизирует в системе окно верхнего уровня и переводит на него фокус:

HWND SetActiveWindow(HWND hwnd);

Оконная процедура окна, которое имеет фокус клавиатуры, принимает сообщения о нажатии клавиши, когда пользователь печатает на клавиатуре. Сообщения о нажатии клавиши - WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN и WM_SYSKEYUP. Типичная оконная процедура игнорирует все сообщения о нажатии клавиши кроме WM_KEYDOWN. Windows посылает сообщение WM_KEYDOWN, когда пользователь нажимает клавишу.

Когда оконная процедура принимает сообщение WM_KEYDOWN, она должна проверить код виртуальной клавиши, который сопровождает сообщение, чтобы установить, как обработать нажатие клавиши. Код виртуальной клавиши находится в параметре wParam сообщения. Как правило, приложение обрабатывает только нажатия клавиши, сгенерированные не символьными клавишами, включая функциональные клавиши, клавиши управления курсором, и клавиши специального назначения типа INS, DEL, HOME и END.

Сообщения мыши

Если класс окна определен без флага CS_DBLCLKS и пользователь делает двойной щелчок левой кнопкой мыши, то оконная процедура последовательно получает сообщения WM_BUTT0ND0WN, WM_LBUTTONUP, WM_LBUTT0ND0WN и WM_LBUTTONUP. Если класс окна определен с флагом CS_DBLCLKS, то после двойного щелчка оконная процедура получит сообщения WM_LBUTT0ND0WN, WM_LBUTTONUP, WM_ LBUTTON-DBLCLK и WM_LBUTTONUP. Легко заметить, что в этом случае второе сообщение WM_LBUTT0ND0WN заменяется сообщением WM_LBUTTONDBLCLK. Прокрутка же колесика вызывает сообщение WM_MOUSEWHEEL. Мышь может порождать много сообщений, всего их 22.

Для того чтобы захватить мышь, приложение должно вызвать функцию SetCapture:

HWND WINAPI SetCapture(HWND hwnd);

Функция ReleaseCapture возвращает нормальный режим обработки сообщений мыши:

void WINAPI ReleaseCapture(void);

Функция GetCapture позволяет определить идентификатор окна, захватившего мышь:

HWND WINAPI GetCapture(void);

Если ни одно окно не захватывало мышь, эта функция возвратит значение NULL. В любом случае на получение сообщений от мыши никак не влияет факт приобретения или потери окном фокуса ввода.