Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОС лабы / ОС - Лабораторная работа 1.doc
Скачиваний:
63
Добавлен:
01.06.2015
Размер:
1.18 Mб
Скачать
      1. Оконные функции

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

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.

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

Теперь рассмотрим, откуда и какими путями сообщения попадают в функцию 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.

Рис. 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, чтобы система имела шанс все-таки порисовать без помех, даже если изображение должно меняться по таймеру (например, отображение текущего времени).