Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OS_-_Laboratornaya_rabota_1.doc
Скачиваний:
1
Добавлен:
23.11.2018
Размер:
1.21 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, чтобы система имела шанс все-таки порисовать без помех, даже если изображение должно меняться по таймеру (например, отображение текущего времени).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]