Механизм сообщений в Windows

ФАВТ

Таганрог 2006

УДК 681.3.066(076.5)

Составители: М.В. Болотов, С.Н. Дроздов

Руководство к лабораторной работе № 1 по курсу «Операционные системы». – Таганрог: Изд-во ТРТУ, 2006. – 32 с.

Руководство содержит описание лабораторной работы «Исследование обмена сообщениями в Windows». Рассматриваются основные типы сообщений, способы их посылки и обработки. Приводится основной теоретический материал, требования к отчету о лабораторной работе, варианты заданий, контрольные вопросы. Приведен пример выполнения одного из вариантов задания.

Рецензент В.Н.Лутай, канд.техн.наук, доцент каф. МОП ЭВМ ТРТУ.

Лабораторная работа №1
ИССЛЕДОВАНИЕ ОБМЕНА СООБЩЕНИЯМИ В WINDOWS

Цель работы

Целью данной работы является ознакомление с механизмом сообщений в операционной системе Windows, способами генерации и обработки сообщений, а также приобретение навыков исследования потока сообщений с помощью программы Spy++.

Основные сведения

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

Процессы и нити

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

Программа (приложение)– это набор исполняемых команд, хранящийся в EXE-файле и загружаемый в оперативную память при выполнении.

Процесс – это контейнер для набора ресурсов, используемых для выполнения экземпляра программы. Процесс включает в себя:

· виртуальное адресное пространство – диапазон адресов виртуальной памяти, которым может пользоваться процесс;

· исполняемую программу – коды команд и данные, размещаемые в виртуальном адресном пространстве;

· список открытых описателей (“хэндлов”) различных системных ресурсов, доступных всем нитям данного процесса;

· уникальный идентификатор процесса;

· первичную нить;

· маркер доступа, идентифицирующий пользователя и привилегии, сопоставленные процессу.

Нить (thread; иногда переводится также «поток»)– это последовательность выполнения команд программы в рамках процесса. Каждый процесс содержит, как минимум, одну нить, создаваемую при старте приложения (первичную нить). Первичная нить может порождать дополнительные нити, выполняемые параллельно с нею. Windows выделяет кванты процессорного времени для каждой нити. Все нити работают в едином виртуальном адресном пространстве процесса и могут использовать все его глобальные переменные и ресурсы.

Типы сообщений

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

Сообщения от клавиатуры. Их можно разбить на две группы. В первую группу входят сообщения о нажатии/отпускании клавиши WM_KEYDOWN и WM_KEYUP, а также WM_SYSKEYDOWN и WM_SYSKEYUP. Два последних сообщения посылаются, если в момент нажатия/отпускания клавиши была также нажата клавиша Alt. Параметры сообщений содержат информацию о том, какая именно клавиша была нажата, а также счетчик повторений (если пользователь долго удерживал клавишу нажатой).

Во вторую группу входят сообщения о введенном символе: WM_CHAR и WM_SYSCHAR. Эти сообщения не посылаются клавиатурой (от клавиатуры в систему поступают только коды клавиш), а генерируются функцией TranslateMessage на основании анализа «что было нажато/отпущено». Например, если была нажата клавиша Shift, а затем клавиша с буквой «a» и при этом выключен режим Caps Lock, то будет сгенерировано сообщение WM_CHAR с кодом заглавной буквы «A». Сгенерированные сообщения не заменяют, а дополняют собой поток аппаратных сообщений, предоставляя прикладной программе выбрать наиболее подходящий для нее способ работы с клавиатурой. Другими словами, программист выбирает, будет ли программа реагировать на «то, что нажато» или на «то, что введено», программирует обработку соответствующих сообщений, а остальные сообщения передает на обработку операционной системе.

Сообщения от мыши. Из этой группы наиболее важными являются сообщения о нажатии, отпускании и двойном щелчке кнопок мыши в пределах активного окна: WM_xBUTTONDOWN, WM_xBUTTONUP, WM_xBUTTONDBLCLK (здесь x – одна из букв L, R или M – для левой, правой или средней кнопки), а также WM_MOUSEMOVE – сообщение о перемещении курсора мыши в пределах активного окна. Когда курсор мыши входит в пределы окна, этому окну посылается сообщение WM_SETCURSOR (это позволяет прикладной программе, если надо, сменить вид курсора на время, пока он находится над данным окном). Если кнопка мыши нажата на неактивном окне, то подается сообщение WM_MOUSEACTIVATE (его обработка обычно вызывает активизацию окна).

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

Сообщения при создании окна. Одним из первых сообщений, которые получает только что созданное (еще невидимое) окно, является сообщение WM_CREATE. Это сообщение содержит информацию о параметрах окна. Затем окно получает еще ряд сообщений, в том числе WM_SIZE, которое сообщает окну его текущий размер, и WM_MOVE, содержащее текущие координаты окна. Когда окно, остающееся пока невидимым, полностью подготовлено к работе, оно обычно получает сообщение WM_SHOWWIN, переводящее окно в видимое состояние.

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

Многие из описанных здесь и далее сообщений могут предваряться сообщениями, имена которых начинаются с префикса NC (например, WM_NCCREATE, WM_NCSIZE и т.п.). Этот префикс расшифровывается как “non-client” и посылается системой в тех случаях, когда соответствующее сообщение касается неклиентской части окна, включающей рамку, заголовок, меню (рис. 1).

Механизм сообщений в Windows - student2.ru

Рис. 1. Клиентская часть окна

Дублирование неклиентских сообщений позволяет гибко распределять обработку сообщений между прикладной программой и системой. Как правило, прикладная программа не реагирует на неклиентские сообщения, оставляя их обработку системе.

Сообщения об изменениях состояния окна. В ходе работы с приложениями Windows каждое окно обычно многократно претерпевает изменения своего состояния. Может измениться размер или координаты окна, окно может быть максимизировано (распахнуто на весь экран) или минимизировано (сжато в значок), может быть закрыто другими окнами или выйти на передний план. Окно может получить фокус ввода (т.е. связь с клавиатурой) или потерять его. Все подобные изменения система сопровождает соответствующими сообщениями, что позволяет прикладной программе отреагировать на изменения согласно логике своей работы. Большая часть подобных сообщений может вообще не обрабатываться пользовательской программой, поскольку система лишь уведомляет: «Произошло то-то; я могу сделать все сама; но, может быть, желаете вмешаться?».

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

При изменении размера окна, его координат или Z-порядка (т.е. положения за или перед другими окнами) окно до начала изменения получает сообщение WM_WINDOWPOSCHANGING, а после окончания – сообщение WM_WINDOWPOSCHANGED. Оба этих сообщения содержат указатель на структуру данных, описывающую положение окна. Кроме того, после окончания изменения могут быть посланы сообщения WM_SIZE или WM_MOVE. Сообщение WM_NCCALCSIZE содержит координаты трех прямоугольников: прежнего положения окна, прежнего положения клиентской области и нового положения окна. На основании этих данных прикладная программа может пересчитать, какие координаты должна теперь иметь клиентская область. Сообщение WM_GETMINMAXINFO позволяет системе запросить у прикладной программы, какие максимальные и минимальные размеры допустимы для данного окна. На основании полученного ответа система может ограничить возможности пользователя, пытающегося мышью изменить размер окна.

Сообщение WM_SHOWWIN посылается окну, которое только что стало видимым или, наоборот, невидимым либо с помощью программного переключения видимости, либо в результате максимизации или минимизации этого или другого окна. В случае необходимости перерисовки части окна система посылает сообщение WM_PAINT с указанием координат прямоугольника, подлежащего перерисовке (например, если эта часть окна была закрыта другим окном, а теперь стала видимой).

Сообщение WM_ACTIVATE посылается при смене активного окна системы, например, при щелчке кнопкой мыши на неактивном окне или при нажатии Alt+Tab. Сначала такое сообщение получает прежнее активное окно, причем один из параметров сообщения указывает, что окно теряет активность, а другой параметр содержат хэндл нового активного окна. Затем сообщение WM_ACTIVATE посылается активизируемому окну. Параметры сообщения указывают, что окно получает активность, и содержат хэндл прежнего активного окна. Обработка WM_ACTIVATE по умолчанию включает передачу фокуса ввода от прежнего активного окна к новому, после чего этим окнам посылаются соответственно сообщения WM_KILLFOCUS и WM_SETFOCUS, уведомляющие о переходе фокуса. Если прежнее и новое активные окна принадлежат разным приложениям, то оба окна получают также сообщения WM_ACTIVATEAPP: одно приложение перестает быть активным (становится фоновым), а другое, наоборот, активизируется (выходит на передний план).

Небольшое уточнение к сказанному. Когда пользователь щелкает кнопкой мыши на неактивном окне, это окно сначала получает сообщение WM_MOUSEACTIVATE. Параметры сообщения уточняют, какой это был щелчок (правой или левой кнопкой, одинарный или двойной) и на какой части окна находился курсор. Обрабатывая это сообщение, программа может сообщить системе, следует ли активизировать окно или надо игнорировать данный щелчок. Только если программа разрешает активацию, система посылает окну WM_ACTIVATE.

Дочерние окна (элементы управления) шлют родительскому окну сообщения WM_NOTIFY, сообщающие о каком-либо событии, связанном с элементом (нажатии кнопки, выбор элемента в списке и т.п.), или запрашивающие у родителя какие-либо данные. Сообщение WM_GETTEXT запрашивает у окна связанную с ним строку текста (для поля ввода это редактируемая строка, для кнопки – надпись на ней, для окон верхнего уровня – название окна). Сообщение WM_SETTEXT посылает окну новое значение связанной с ним строки. Для некоторых сложных элементов управления определены целые наборы сообщений, начинающиеся не с WM_, а со специального префикса для данного типа элементов.

Сообщения о командах. Сообщение WM_COMMAND генерируется при подаче команды с помощью меню, комбинации клавиш или командной кнопки. Этот тип сообщений обрабатывается прикладной программой в соответствии с логикой ее работы. Параметр wNotifyCode (старшее слово параметра wParam) указывает вид полученной команды. Если команда подана с помощью комбинации клавиш, то wNotifyCode равен 1; если команда подана через меню, wNotifyCode равен 0; если команда подана через элемент управления (кнопку и т.п.), то этот параметр принимает другие значения, заранее выбранные прикладным программистом. Сообщение WM_SYSCOMMAND генерируется командами системного меню (в левом верхнем углу окна), а также кнопками максимизации/минимизации окна. Как правило, оно обрабатывается системой.

Сообщения при закрытии окна. Когда пользователь закрывает окно приложения, окно получает сообщение WM_CLOSE. В ходе обработки этой команды генерируется и обрабатывается еще ряд сообщений: WM_WINDOWPOSCHANGING и WM_WINDOWPOSCHANGED (теперь позиция окна будет «никакая»), WM_ACTIVATE (окно перестает быть активным), если это последнее окно приложения – WM_ACTIVATEAPP (умирающее приложение перестает быть активным). Одно из последних сообщений, которые достигают окна ­– WM_DESTROY, которое посылается уже после того, как окно убрано с экрана. Если данное окно имеет дочерние окна, то они также получают сообщение WM_DESTROY. Сообщение WM_QUIT вызывает завершение цикла приема сообщений и оконной функции уже не передается.

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

Сообщение WM_TIMER. Это сообщение генерируется, если данная нить установила таймер с помощью функции SetTimer, и для этого таймера истек заданный интервал времени.

UINT_PTR SetTimer(

HWND hWnd, // дескриптор окна, с которым связан таймер

UINT_PTR nIDEvent, // идентификатор таймера

UINT uElapse, // период срабатывания таймера

TIMERPROC lpTimerFunc // функция обработки события

);

С помощью этой функции можно либо создать новый таймер, либо перезапустить ранее установленный. Если хотя бы один из параметров hWnd или nIDEvent равен NULL, то создается новый таймер, идентификатор которого возвращается в качестве значения функции. Если же при вызове функции SetTimer параметр hWnd имеет значение, отличное от NULL, и окно с указанным дескриптором (hWnd) уже имеет установленный таймер с идентификатором nIDEvent, то тогда существующий таймер сбрасывается и заменяется новым.

Период срабатывания таймера (uElapse) устанавливается в миллисекундах.

Если параметр lpTimerFunc установлен в NULL, то сообщение будет передаваться на обработку в оконную функцию, но только тогда, когда в очереди уже других нет сообщений. Для более оперативной реакции программист может предусмотреть свою функцию обработки сообщения таймера, и указать ее в качестве параметра (lpTimerFunc). Эта функция будет вызываться при срабатывании таймера сразу, в обход оконной функции.

Отметим, что после того, как таймер установлен, он начинает генерировать сообщения через интервал, установленный в uElapse. Остановить его можно с помощью функции KillTimer:

BOOL KillTimer(

HWND hWnd,

UINT_PTR uIDEvent

);

Как и WM_PAINT, сообщение WM_TIMER может быть только в одном экземпляре, даже если таймер успел сработать несколько раз.

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

Сообщение проходит в ОС путь от источника, создавшего это сообщение, до приемника, обрабатывающего его. Описание этого пути удобнее начать с конца.

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

Работа нити процесса, владеющей окнами, основана на цикле приема сообщений, который начинает работу после инициализации (создания окон, присвоения начальных значений и т.п.) и продолжается до завершения работы нити. При использовании современных систем программирования (Delphi, Visual C++ и т.п.) цикл приема сообщений может оказаться скрытым от пользователя в теле методов системных классов, однако он тем не менее существует.

Примерная структура цикла приема сообщений выглядит так:

while(GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

Первый параметр функции GetMessage содержит адрес структуры, куда будет приниматься сообщение. Второй параметр содержит хэндл окна, чьи сообщения будут выбираться из очереди. Значение NULL означает все окна данной нити. Третий и четвертый параметры – нижняя и верхняя границы идентификаторов принимаемых сообщений. Пара значений 0,0 означает все сообщения. Функция возвращает 0 в единственном случае: если принято сообщение WM_QUIT, которое означает завершение нити. Если в очереди нет требуемых сообщений, функция ожидает их появления. В это время могут работать другие процессы и нити.

Вместо GetMessage может также использоваться функция PeekMessage, основное отличие которой заключается в том, что она позволяет только проверить наличие сообщений, не выполняя ожидание в случае их отсутствия. Кроме того, пользователь может выбирать, следует ли при выполнении PeekMessage изымать сообщение из очереди или нужно пока оставить его в очереди. Эти возможности позволяют нестандартным образом строить цикл приема сообщений, достигая в некоторых случаях большей гибкости программы.

Функции GetMessage и PeekMessage не дают информации о времени посылки сообщения и о координатах курсора. Если эти данные нужны для правильной обработки сообщения, приложение может вызвать функции GetMessageTime и GetMessagePos.

Функция TranslateMessage необходима для программ, обрабатывающих клавиатурные сообщения о введенном символе WM_CHAR и WM_SYSCHAR. Как было сказано выше, эти сообщения помещаются в очередь на основании анализа сообщений о нажатии/отпускании клавиш.

Функция DispatchMessage(msg) выясняет, какому из окон данной нити адресовано сообщение, вызывает оконную функцию данного окна для обработки сообщения и передает в нее структуру сообщения msg. После того, как оконная функция обработает сообщение, цикл приема сообщений продолжает свою работу.

В цикл приема сообщений могут входить дополнительные операторы, выявляющие сообщения, адресованные не окну, а самой нити (сообщения с хэндлом окна, равным NULL), и выполняющие обработку таких сообщений без вызова DispatchMessage.

Еще одной функцией, которая может использоваться в нестандартном цикле приема сообщений, является WaitMessage. Эта функция просто проверяет, имеется ли в очереди к данной нити какое-либо сообщение, которое могла бы принять функция GetMessage. Если нет, то работа нити блокируется, как и при выполнении GetMessage. Если сообщение имеется, то WaitMessage завершает свое выполнение, но не выбирает сообщение из очереди.

Оконные функции

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

LRESULT CALLBACK имя_функции(

HWND hwnd, // хэндл окна, чье сообщение обрабатывается

UINT uMsg, // тип сообщения

WPARAM wParam, // первый параметр сообщения

LPARAM lParam // второй параметр сообщения

);

Тип LRESULT определен как целое число со знаком. Возвращаемое значение может иметь различный смысл в зависимости от типа сообщения. Описатель CALLBACK указывает, что функция может (и должна) вызываться не прикладной программой, а операционной системой. Для всех типов сообщений, специальная обработка которых в оконной функции не предусмотрена, следует вызвать системную функцию обработки сообщений по умолчанию DefWindowProc и вернуть ее результат как результат оконной функции, например:

return(DefWindowProc(hwnd, uMsg, wParam, lParam));

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

Оконная функция для диалоговых окон отличается тем, что возвращать должна булевское значение TRUE (т.е. число 1) для обработанных сообщений и значение FALSE (число 0) для сообщений, обработка которых перепоручается системе. Исключение составляет сообщение WM_INITDIALOG, для которого следует всегда возвращать TRUE.

Структура очереди сообщений

Теперь рассмотрим, откуда и какими путями сообщения попадают в функцию GetMessage. Обычное представление «сообщения выбираются из очереди» является большим упрощением ситуации. На самом деле, эта очередь устроена достаточно сложно.

Имеется два принципиально разных способа посылки сообщений: синхронный (send) и асинхронный (post). Посылая сообщение синхронно, отправитель дожидается окончания его обработки, прежде чем продолжить работу. Асинхронная посылка напоминает опускание письма в почтовый ящик: опустил и забыл. Система синхронным способом посылает окну сообщения о создании, изменении состояния и закрытии (например, WM_CREATE, WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_DESTROY), а также многие другие сообщения. К асинхронным сообщениям относятся сообщения от клавиатуры и мыши, с некоторой оговоркой к ним можно отнести также WM_PAINT и WM_TIMER. Программа пользователя может посылать любые сообщения синхронным или асинхронным способом, как сочтет нужным разработчик. Выбор способа посылки зависит от того, насколько важно для программы-отправителя прежде, чем она продолжит работу, убедиться, что посланное сообщение получено и обработано адресатом.

В ранних версиях Windows синхронная посылка сообщений была реализована просто как вызов одним приложением оконной функции некоторого окна, пусть даже это окно принадлежало другому процессу. Программная архитектура 32-разрядных версий Windows не допускает подобных «простых» решений по нескольким причинам:

· если отправитель сообщения и принимающее окно принадлежат разным процессам, то вызов оконной функции как подпрограммы просто невозможен, ибо процессы «не видят» друг друга, работая в разных виртуальных пространствах памяти;

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

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

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

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

Общая схема аппаратного ввода показана на рис. 2. Когда пользователь нажимает кнопку на клавиатуре или мыши, драйвер соответствующего устройства добавляет соответствующее событие в очередь аппаратного ввода (SHIQ). При появлении события в SHIQ пробуждается нить необработанного ввода (так называемая системная нить RIT), которая обычно бездействует. Эта нить преобразует событие в соответствующие сообщение (WM_KEY*, WM_xBUTTON или WM_MOUSEMOVE) и ставит последнее в соответствующую виртуальную очередь ввода (VIQ) нити, владеющей окном. Далее RIT возвращается в начало цикла и ждет появления следующего события в SHIQ.

Механизм сообщений в Windows - student2.ru

Рис. 2. Модель аппаратного ввода

В случае с сообщениями от мыши, нить RIT направляет их в ту нить, которая владеет окном, на котором находился курсор в момент генерации сообщения. Исключение составляет случай, когда одно из окон с помощью функции SetCapture «захватило» мышь, т.е. хочет получать от нее сообщения со всего экрана. В современных версиях Windows захват в пределах всего экрана может действовать, только пока нажата какая-либо кнопка мыши. Это позволяет, например, пользователю «перетаскивать» объекты из активного окна в любое другое окно. После отпускания кнопок захват продолжает действовать только в пределах окон данной нити, поскольку разные приложения не должны мешать друг другу.

Сообщения от клавиатуры ведут себя несколько по-иному. В каждый момент времени нить RIT работает только с одной нитью (foreground thread), которой и принадлежит то окно, где работает пользователь. Как только пользователь переключается с одного окна приложения на окно другого приложения и это окно становится активным, RIT также переподключается к нити, владеющей этим окном.

Отметим, что ввод поступает именно в виртуальную очередь ввода нити, которой может принадлежать несколько окон, и для того чтобы сообщение было «доставлено» функцией DispatchMessage именно нужному окну, RIT добавляет дополнительную информацию в параметры сообщения. Существуют также сообщения, которые не направляются окнам приложений (например, известные комбинации клавиш Alt+Tab, Alt+Esc, Ctrl+Esc, Ctrl+Alt+Del и др.). Они обрабатываются непосредственно самой RIT: при поступлении такой комбинации с клавиатуры, нить RIT сама активизирует необходимое окно и переподключает его нить к себе, делая его активным.

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

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

Варианты заданий

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

1. Минимизированное окно программы «Блокнот» активизируется по Alt+Tab.

2. В окне редактора «Блокнот» выполняется копирование фрагмента текста через буфер обмена.

3. В программе «Калькулятор» выполняется операция «2*2=4». Клавиши калькулятора нажимаются с помощью мыши.

4. Некоторый файл перетаскивается с помощью мыши из одного окна «Мой компьютер» или «Проводник» в другое такое же окно.

5. В программе «Блокнот» открывается для редактирования существующий файл.

6. В редакторе «WordPad» набирается любой текст. Для него с помощью соответствующих элементов панели инструментов устанавливаются следующие атрибуты: гарнитура «Arial», кегль – 20 pt и цвет – «красный». Все сохраняется в файл.

7. Имеются два окна программы «Блокнот». Каждое из окон по очереди максимизируется, минимизируется, восстанавливает нормальные размеры.

8. В редакторе «Microsoft Word» открыты два окна. Одно из окон закрывается с помощью команды меню Файл+Закрыть.

6. Контрольные вопросы

1. Что такое процесс и нить? В чем их отличие?

2. Что такое «система, управляемая событиями»?

3. Какую информацию содержит сообщение в Windows?

4. Каковы основные источники сообщений?

5. На какие группы можно разделить сообщения от клавиатуры?

6. Какие сообщения посылает мышь?

7. Какие сообщения получает окно при создании?

8. Что означает префикс NC в именах сообщений?

9. Какие сообщения сопровождают смену активного окна?

10. Что такое сообщение WM_COMMAND?

11. В каких случаях подается сообщение WM_SYSCOMMAND?

12. Какие сообщения получает окно при закрытии?

13. В каких случаях подается сообщение WM_PAINT?

14. В каких случаях подается сообщение WM_TIMER?

15. Какие идентификаторы может пользователь присваивать своим сообщениям?

16. Каково назначение цикла приема сообщений?

17. В чем назначение функции TranslateMessage?

18. В чем назначение функции DispatchMessage?

19. Как отличаются сообщения, посланные нити, от сообщений, посланных окнам нити?

20. Чем различаются функции GetMessage и PeekMessage?

21. Как приложение может узнать время и координаты, связанные с сообщением?

22. Что делает функция WaitMessage?

23. Что такое оконная функция?

24. Чем отличается оконная функция диалогового окна?

25. Чем различаются синхронный и асинхронный способы посылки сообщений?

26. В каком порядке выбираются для обработки разные типы сообщений?

27. Что такое виртуальная очередь ввода?

28. Какие функции используются для асинхронной посылки сообщений?

29. Какая функция используется для синхронной посылки сообщений?

30. Чем различается обработка синхронных сообщений от нити, владеющей окном, и от других нитей?

31. До каких пор нить, пославшая синхронное сообщение, остается заблокированной?

32. Какую роль играет функция ReplyMessage?

33. Как оконная функция может отличить синхронное сообщение от асинхронного?

34. Как устраняется опасность блокировки нити-отправителя при некорректной работе нити-получателя синхронного сообщения?

35. Какие основные возможности предоставляет программа Spy++?

36. Как узнать хэндл окна, видимого на экране?

37. Как найти на экране окно, зная его хэндл?

38. Какие свойства отображаются для окна?

39. Какие свойства отображаются для сообщения?

40. Как выбрать окно для отслеживания сообщений?

41. Как отследить сообщения для двух окон?

Болотов Максим Викторович

Дроздов Сергей Николаевич

Руководство

К лабораторной работе № 1

По курсу

Операционные системы

Ответственный за выпуск Дроздов С.Н.

Редактор Чиканенко Л.В.

Корректор

ЛР № 020565 от 23 июня 1997 г. Подписано к печати

Формат 80 ´ 64 Механизм сообщений в Windows - student2.ru Бумага офсетная.

Офсетная печать Усл. п. л. – 1,9 Уч.-изд. л. – 1,7

Заказ № Тираж экз.

<< C >>

Издательство Таганрогского государственного
радиотехнического университета

ГСП 17А, Таганрог, 28, Некрасовский, 44

Типография Таганрогского государственного
радиотехнического университета

ГСП 17А, Таганрог, 28, Энгельса, 1

ФАВТ

Таганрог 2006

УДК 681.3.066(076.5)

Составители: М.В. Болотов, С.Н. Дроздов

Руководство к лабораторной работе № 1 по курсу «Операционные системы». – Таганрог: Изд-во ТРТУ, 2006. – 32 с.

Руководство содержит описание лабораторной работы «Исследование обмена сообщениями в Windows». Рассматриваются основные типы сообщений, способы их посылки и обработки. Приводится основной теоретический материал, требования к отчету о лабораторной работе, варианты заданий, контрольные вопросы. Приведен пример выполнения одного из вариантов задания.

Рецензент В.Н.Лутай, канд.техн.наук, доцент каф. МОП ЭВМ ТРТУ.

Лабораторная работа №1
ИССЛЕДОВАНИЕ ОБМЕНА СООБЩЕНИЯМИ В WINDOWS

Цель работы

Целью данной работы является ознакомление с механизмом сообщений в операционной системе Windows, способами генерации и обработки сообщений, а также приобретение навыков исследования потока сообщений с помощью программы Spy++.

Основные сведения

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

Процессы и нити

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

Программа (приложение)– это набор исполняемых команд, хранящийся в EXE-файле и загружаемый в оперативную память при выполнении.

Процесс – это контейнер для набора ресурсов, используемых для выполнения экземпляра программы. Процесс включает в себя:

· виртуальное адресное пространство – диапазон адресов виртуальной памяти, которым может пользоваться процесс;

· исполняемую программу – коды команд и данные, размещаемые в виртуальном адресном пространстве;

· список открытых описателей (“хэндлов”) различных системных ресурсов, доступных всем нитям данного процесса;

· уникальный идентификатор процесса;

· первичную нить;

· маркер доступа, идентифицирующий пользователя и привилегии, сопоставленные процессу.

Нить (thread; иногда переводится также «поток»)– это последовательность выполнения команд программы в рамках процесса. Каждый процесс содержит, как минимум, одну нить, создаваемую при старте приложения (первичную нить). Первичная нить может порождать дополнительные нити, выполняемые параллельно с нею. Windows выделяет кванты процессорного времени для каждой нити. Все нити работают в едином виртуальном адресном пространстве процесса и могут использовать все его глобальные переменные и ресурсы.

Механизм сообщений в Windows

Все операционные системы семейства Windows относятся к типу систем, управляемых событиями. Это означает, что работа прикладных программ строится не в виде непрерывного потока выполнения операций, а в виде реакции программы на определенный набор событий, таких как движение мыши, нажатие клавиши, срабатывание таймера и др., т.е. изменений в состоянии системы и внешних устройств. В системе Windows эти события называются сообщениями (Message), и с каждым сообщением связана структура данных, содержащая информацию, полезную для обработки сообщения. Эта структура на языке C++, описанная в файле WINUSER.H, определяется следующим образом:

/* структура сообщения */

typedef struct tagMSG

{

HWND hwnd; // хэндл окна, которому предназначено

// сообщение

UINT message; // идентификатор (тип) сообщения

WPARAM wParam; // дополнительная информация

LPARAM lParam; // к сообщению

DWORD time; // время посылки сообщения

POINT pt; // положение курсора мыши

} MSG;

Здесь hwnd – числовой описатель (хэндл, handle) окна-получателя. Он присваивается окну при создании. Есть также возможность определить хэндл окна по координатам точки на экране. Допускается специальное значение HWND_BROADCAST, означающее посылку сообщения всем окнам верхнего уровня, существующим в системе (но не дочерним окнам, к которым относятся кнопки, поля ввода и прочие управляющие элементы). Если сообщение послано не окну, а самому процессу (точнее, одной из его нитей), то значение hwnd устанавливается равным NULL.

Идентификатор (тип) сообщения message определяет, что, собственно говоря, случилось. Как правило, в тексте программ тип сообщения указывается в виде одной из многочисленных системных констант, например WM_MOUSEMOVE (0x0200) или WM_TIMER (0x0113).

Идентификаторы сообщений могут принадлежать одному из нескольких диапазонов.

· С 0х0000 до 0x03FF – системные сообщения, их идентификаторы изначально заданы в OC и не могут быть переопределены.

· С 0х0400 (константа WM_USER) до 0x7FFF – пользовательские сообщения, которые определяются в приложении и используются для обмена сообщениями между окнами этого приложения. В этом же диапазоне располагаются идентификаторы тех сообщений, которые элементы управления (кнопки, поля ввода, по<

Наши рекомендации