Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шпора 130стр.doc
Скачиваний:
93
Добавлен:
15.06.2014
Размер:
2.49 Mб
Скачать

33) Структура и типология сообщений в Win32. Классиф сообщений. Очередь сооб, ф-ункции работы с ней. (см также 16!)

Очереди потока и обработка сообщений

Большинство операций, выполняемых Win32-приложеншши, инициируется оконными сообщениями. В 16-разрядной Windows всего один поток. Если Ваша программа посылает сообщение окну другой задачи, исполнение Вашей задачи приостанавливается, и начина-ется исполнение кода, обрабатывающего сообщение. После этого система вновь возвра-щается к исполнению кода Вашей задачи. Так было в 16-разрядной Windows. В многоза-дачной среде все иначе.

В Win32 код оконной процедуры выполняется создавшим окно потоком, и он не обязательно является тем же потоком, что отправил сообщение. Чтобы другой поток мог обработать сообщение, вызывающий поток уведомляет принимающий о необходимости выполнения каких-то действий. Затем вызывающий поток приостанавливает себя до тех пор, пока поток-приемник не обработает запрос. Поэтому далее мы рассмотрим методы, применяемые для синхронной (sent) а асинхронной (posted) посылки различных сообщений.

Архитектура очередей сообщений в Win32

одна из главных целей Win32 — предоставить всем приложениям отказоустойчивую среду. Для этого каждый поток должен исполняться в такой среде, где он может считать себя единственным. Точнее, прочие потоки не должны никоим образом влиять на очередь сообщений данного потока. Кроме того, для каждого потока нужно смоделировать среду, позволяющую ему самостоятельно управлять фокусом клавиатуры (keyboard focus), активизировать окна, поддерживать захват мыши(mouse capture)и тд.

Асинхронная посылка сообщений в очередь потока

Ну а когда с потоком связывается такая структура, тот получает свой набор очередей сообщений. Если процесс создает 10 потоков и все они вызывают функцию CreateWindow (User-функция), то и наборов очередей сообщений будет тоже 10. Сообщения ставятся в очередь асинхронных сообщений функцией PostMessage:

BOOL PostMessage(HWND hWnd, UB4T Msg, WPARAM wParam, LPARAM IParam);

Когда поток вызывает эту функцию, система определяет, каким потоком создано окно, нтифицируемое параметром hWnd, и помещает сообщение в его очередь асинхронных сообщений. Возврат из PostMessage происходит сразу после того, как сообщение поставлено в очередь; вызывающий поток «понятия не имеет», обработано ли оно процедурой соответствующего окна.

Посылка сообщения окну - синхронное

Оконное сообщение можно отправить непосредственно оконной процедуре вызовом SendMessage: LRESULT SendMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM IParam);

Оконная процедура обработает сообщение и только по окончании обработки функ-ция SendMessage вернет управление. Благодаря тому, что в эту функцию заложен механизм синхронизации, ее используют гораздо чаще, чем PostMessage или PostThreadMessage. При переходе к исполнению следующей строки кода, поток, вызвавший SendMessage, знает, что сообщение полностью обработано.

Если поток вызывает SendMessage для посылки сообщения окну, созданному им же, то функция работает очень просто: вызывает оконную процедуру) соответствующего окна как подпрограмму. Окончив обработку, оконная процедура возвращает функции SendMessage 32-битное значение, а та возвращает это значение вызвавшему ее потоку.

Сообщения

Когда программа для Windows начинает выполняться, Windows строит для программы очередь сообщений. В этой очереди хранятся сообщения для любых типов окон, которые могли бы быть созданы программой. Цикл обработки сообщений выбирает эти сообщения из очереди и переправляет их соответствующей оконной процедуре.

Сообщения, которые приложение получает от Windows о событиях, относящихся к клавиатуре, различаются на "аппаратные" и "символьные". Такое положение соответствует двум представлениям о клавиатуре. Во-первых, вы можете считать клавиатуру набором клавиш. В клавиатуре имеется только одна клавиша <A>. Нажатие на эту клавишу является аппаратным событием. Отпускание этой клавиши является аппаратным событием. Но клавиатура также является устройством ввода, генерирующем отображаемые символы. Клавиша <А>, в зависимости от состояния клавиш <Ctrl>, <Shift> и <CapsLock>, может стать источником нескольких символов. Некоторые клавиши не генерируют символов. Это такие клавиши, как клавиши переключения, функциональные клавиши, клавиши управления курсором и специальные клавиши, такие как Insert и Delete. Для таких клавиш Windows вырабатывает только аппаратные сообщения.

Когда нажата клавиша, Windows помещает либо сообщение WM_KEYDOWN, либо сообщение WM_SYSKEYDOWN в очередь сообщений окна, имеющего фокус ввода. Когда отпусщена клавиша, Windows помещает либо сообщение WM_KEYUP, либо сообщение WM_SYSKEYUP в очередь сообщений. Аппаратные сообщения:

Клавиша нажата

Клавиша отпущена

Несистемные сообщения

WM_KEYDOWN

WM_KEYUP

Системные сообщения

WM_SYSKEYDOWN

WM_SYSKEYUP

Аппаратные сообщения клавиатуры становятся в очередь.

Префикс "SYS" в WM_SYSKEYDOWN и WM_SYSKEYUP означает "системное" и относится к аппаратным сообщениям клавиатуры, которые больше важны для Windows, чем для приложений Windows. Сообщения WM_SYSKEYDOWN и WM_SYSKEYUP обычно вырабатываются при нажатии клавиш в сочетании с клавишей <Alt>. Эти сообщения вызывают опции меню программы или системного меню, или используются для системных функций. Программы обычно игнорируют сообщения WM_SYSKEYDOWN и WM_SYSKEYUP и передают их DefWindowProc. Сообщения WM_KEYDOWN и WM_KEYUP обычно вырабатываются для клавиш, которые нажимаются и отпускаются без участия клавиши <Alt>. Если сообщением является WM_KEYDOWN или WM_SYSKEYDOWN и, если нажатие клавиши в сочетании с положением клавиши сдвига генерирует символ, тогда TranslateMessage помещает символьное сообщение в очередь сообщений. Это символьное сообщение будет следующим, после сообщения о нажатии клавиши, которое функция GetMessage извлечет из очереди сообщений.

Существует четыре символьных сообщения:

Символы

Немые символы

Несистемные символы:

WM_CHAR

WM_DEADCHAR

Системные символы:

WM_SYSCHAR

WM_SYSDEADCHAR

Сообщения WM_CHAR и WM_DEADCHAR являются следствием сообщений WM_KEYDOWN. Сообщения WM_SYSCHAR и WM_SYSDEADCHAR являются следствием сообщений WM_SYSKEYDOWN. Все сообщения, за исключением WM_CHAR, игнорируются.

Символьные сообщения доставляются в оконную процедуру в промежутке между аппаратными сообщениями клавиатуры. Сама по себе клавиша <Shift> не вырабатывает символьного сообщения. В программах для Windows клавиша <Ctrl> иногда используется с клавишами букв в качестве быстрых клавиш, в таком случае сообщения от буквенных клавиш не преобразуются в символьные сообщения. Если Windows-программе необходимо обрабатывать символы клавиатуры, то она будет обрабатывать сообщения WM_CHAR.

Одни и те же сообщения могут быть и "синхронные" и "асинхронные". Cинхронными сообщениями называются сообщения, которые Windows помещает в очередь сообщений программы, и которые извлекаются и диспетчеризуются в цикле обработки сообщений. Асинхронные сообщения передаются непосредственно окну, когда Windows вызывает оконную процедуру. В результате оконная процедура получает все предназначенные для окна сообщения, как синхронные, так и асинхронные. Синхронными становятся сообщения, в основном, тогда, когда они являются результатом пользовательского ввода путем нажатия клавиш (например, WM_KEYDOWN и WM_KEYUP), это символы, введенные с клавиатуры (WM_CHAR), результат движения мыши (WM_MOUSEMOVE) и щелчков кнопки мыши (WM_LBOTTONDOWN). Кроме этого синхронные сообщения включают в себя сообщение от таймера (WM_TIMER), сообщение о необходимости плановой перерисовки (WM_PAINT) и сообщение о выходе из программы (WM_QUIT). Сообщения становятся асинхронными во всех остальных случаях. Часто асинхронные сообщения являются результатом синхронных.

Обработка сообщений

Каждое получаемое окном сообщение идентифицируется номером, который содержится в параметре iMsg оконной процедуры. В заголовочных файлах Windows определены идентификаторы, начинающиеся с префикса WM ("window message") для каждого типа сообщений.

Обычно программисты для Windows используют конструкции switch и case для определения того, какое сообщение получила оконная процедура и то, как его обрабатывать. Если оконная процедура обрабатывает сообщение, то ее возвращаемым значением должен быть 0. Все сообщения, не обрабатываемые оконной процедурой, должны передаваться функции DefWindowProc. Значение, возвращаемое функцией DefWindowProc, должно быть возвращаемым значением оконной процедуры.

Большинство программ для Windows вызывают функцию UpdateWindow при инициализации в WinMain, сразу перед входом в цикл обработки сообщений. Windows использует эту возможность для асинхронной отправки в оконную процедуру первого сообщения WM_PAINT. Это сообщение информирует оконную процедуру о том, что рабочая область готова к рисованию. После этого оконная процедура должна быть готова в любое время обработать дополнительные сообщения WM_PAINT и даже перерисовать, при необходимости, всю рабочую область окна. Оконная процедура получает сообщение WM_PAINT при возникновении одной из следующих ситуаций:

  • Предварительно скрытая область окна открылась, когда пользователь передвинул окно или выполнил какие-то действия, в результате которых окно вновь стало видимым.

  • Пользователь изменил размера окна.

  • В программе для прокрутки части рабочей области используются ф-ии ScrollWindow.

  • Для генерации сообщения WM_PAINT в программе используются функции InvalidateRect или InvalidateRgn.

  • В некоторых случаях, когда часть рабочей области временно закрывается, Windows пытается сначала ее сохранить, а затем восстановить. Это не всегда возможно. В некоторых случаях Windows может послать синхронное сообщение WM_PAINT.

Еще одним важным сообщением является сообщение WM_DESTROY. Это сообщение показывает, что Windows находится в процессе ликвидации окна в ответ на полученную от пользователя команду. Программа стандартно реагирует на это сообщение, вызывая PostQuitMessage(0).

Эта функция ставит сообщение WM_QUIT в очередь сообщений программы. Когда GetMessage получает сообщение WM_QUIT, функция возвращает 0. Это заставляет WinMain прервать цикл обработки сообщений и выйти в систему, закончив программу.

Цикл обработки сообщений

После вызова функции UpdateWindow, окно окончательно выведено на экран. Теперь программа должна подготовить себя для получения информации от пользователя через клавиатуру и мышь. Windows поддерживает "очередь сообщений" для каждой программы, работающей в данный момент в системе Windows. Когда происходит ввод информации, Windows преобразует ее в "сообщение", которое помещается в очередь сообщений программы. Программа извлекает сообщения из очереди сообщений, выполняя блок команд, известный как "цикл обработки сообщений". Вызов функции GetMessage, с которого начинается цикл обработки сообщений, извлекает сообщение из очереди сообщений. Этот вызов передает Windows указатель на структуру msg типа MSG. Второй, третий и четвертый параметры, NULL или 0, показывают, что программа получает все сообщения от всех окон, созданных этой программой. Windows заполняет поля структуры сообщений информацией об очередном сообщении из очереди сообщений. Поля этой структуры следующие:

hwnd — описатель окна, для которого предназначено сообщение.

message — идентификатор сообщения. Это число, которое идентифицирует сообщение. Для каждого сообщения имеется соответствующий ему идентификатор, который задается в заголовочных файлах Windows и начинается с префикса WM (оконное сообщение — window message).

wParam — 32-разрядный параметр сообщения, смысл и значение которого зависят от особенностей сообщения.

lParam — другой 32-разрядный параметр, зависящий от сообщения.

time — время, когда сообщение было помещено в очередь сообщений.

pt — координаты курсора мыши в момент помещения сообщения в очередь сообщений.

Если поле message сообщения, извлеченного из очереди сообщений, равно любому значению, кроме WM_QUIT, то функция GetMessage возвращает ненулевое значение. Сообщение WM_QUIT заставляет программу прервать цикл обработки сообщений. TranslateMessage (&msg) передает структуру msg обратно в Windows для преобразования какого-либо сообщения с клавиатуры. DispatchMessage (&msg) также передает структуру msg обратно в Windows. Windows отправляет сообщение для его обработки соответствующей оконной процедуре — таким образом, Windows вызывает оконную процедуру. После того, как обработано сообщение, оно возвращается в Windows, которая все еще обслуживает вызов функции DispatchMessage.Цикл обработки сообщений в очередной раз возобновляет работу, вызывая GetMessage.

Когда поток вызывает GetMessage или PeekMessage, система проверяет состояние очередей потока и определяет, какое сообщение надо обработать (рис. 11-2):

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

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

3. Если установлен флаг QS_QUIT, GetMessage и PeekMessage возвращают сообщение WM_QUTT и сбрасывают этот флаг.

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

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

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

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

Так, нажатие кнопки мыши могло бы заставить окно, которое обрабатывает сообще-ние WM_LBUTTONDOWN, послать 3 асинхронных сообщения разным окнам. Поскольку эти 3 программных события возникают в результате аппаратного события, система обраба-тывает ук до того, как принимает новое аппаратное событие, инициируемое пользователем. Прекрасный пример такой последовательности событий — вызов функции TranslateMessage. проверяющей, не было ли выбрано из очереди ввода сообщение WM_KEYDOWN или WM_SYSKEYDOWN. Если да, функция проверяет, можно ли информацию о виртуальной клавише преобразовать в символьный эквивалент. Если это возможно, TranslateMessage вызывает PostMessage. чтобы поместить в очередь асинхронных сообщений WM_CHAR или WM_SYSCHAR. При следующем вызове GetMessage система проверяет содержимое очереди асинхронных сообщений и, если в ней есть сообщение, выбирает его и возвращает поток}. Возвращается либо WM_CHAR, либо \VM_SYSCHAR, Но вот GetMessage вызывается еще раз. и система обнаруживает, что очередь асинхронных сообщений пуста Тогда она проверяет очередь ввода, где и находит сообщение WM_(SYS)KEYUP; именно оно и возвращается.

Поскольку система устроена так, а не иначе, приведенная ниже последовательность аппаратных событий:

WM_KEYDOWN

WM_KEYUP

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

WMJCEYDOWN WM_CHAR WM KEYUP