Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
04-09-2015_19-17-13 / Конспект лекций.doc
Скачиваний:
98
Добавлен:
01.03.2016
Размер:
1.31 Mб
Скачать

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

Цикл обработки сообщений в простейшем случае состоит из одного предложения

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

DispatchMessage(&msg);

В этом бесконечном (если его не разорвать изнутри) цикле вызывается функция Windows GetMessage() и, если она возвращает ненулевое значение, вызывается функция DispatchMessage().

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

Далее в цикле while вызывается функция DispatchMessage(). Ее назначение – вызов оконной функции для того окна, которому предназначено очередное сообщение. После того, как оконная функция обработает очередное сообщение, возврат из нее приводит к возврату из функции DispatchMessage() на продолжение цикла while.

Функция GetMessage()требует 4 параметра. Первый из них – адрес структуры msg, в которую GetMessage() должна передать изъятое из очереди сообщение. Второй параметр типа HWND позволяет определить окно, чьи сообщения будут изыматься функцией GetMessage(). Если этот параметр равен NULL, GetMessage() работает со всеми сообщениями данного приложения.

Два последние параметра определяют диапазон сообщений, которые анализируются функцией GetMessage(). Если, например, в качестве этих параметров указать константы WM_KEYFIRST и WM_KEYLAST, GetMessage() будет забирать из очереди только сообщения, относящиеся к клавиатуре; константы WM_MOUSEFIRST и WM_MOUSELAST позволяют работать только с сообщениями от мыши. Чаще всего надо анализировать все сообщения. Чтобы исключить фильтрацию сообщений, оба параметра должны быть равны 0.

Особая ситуация возникает, если функция GetMessage() обнаруживает в очереди сообщение WM_QUIT с кодом 0x12. В этом случае GetMessage() сразу же завершается с возвратом значения FALSE. Однако цикл while выполняется лишь, если GetMessage() возвращает TRUE. Возврат FALSE приводит к завершения цикла и переходу на предложение

Return msg.wParam;

т. е. к завершению функции WinMain() и всего приложения. Таким образом, условием завершения приложения является появление сообщения WM_QUIT. Как оно возникает, будет рассмотрено ниже.

17.8 Оконная функция

Оконная функция предназначена для организации адекватной реакции со стороны Windows-приложения на действия пользователя и поддержания в актуальном состоянии того окна приложения, сообщения которого она обрабатывает. Приложение может иметь несколько оконных функций, их количество определяется количеством классов окон, зарегистрированных в системе функцией RegisterClass(). Функции RegisterClass() посредством экземпляра структуры типа WNDCLASS передается указатель на определенную оконную функция Windows-приложения. Данная функция до конца работы приложения связана с экземплярами окон, которые, в свою очередь, создаются другой функцией API – CreateWindow().

Как было показано в предыдущем разделе, оконная функция вызывается, как только в структуру msg попадает очередное сообщение. Задача оконной функции – определить природу сообщения и обработать его следующим образом. Ранее мы для упрощения говорили, что единственный источник появления сообщений – очередь сообщений приложения, но это не совсем так. Сообщения, в зависимости от источника их появления в оконной функции, могут быть двух типов: синхронные и асинхронные. К синхронным относятся те сообщения, которые помещаются в очередь сообщений приложения и терпеливо ждут момента, когда они будут выбраны функцией GetMessage(). После этого поступившие сообщения поступают в оконную функцию, где и производится их обработка. Асинхронные сообщения попадают в оконную функцию в экстренном порядке, минуя при этом все очереди. Они, в частности, инициируются некоторыми функциями Win32 API, такими как CreateWindow() и UpdateWindow(). Координацию синхронных и асинхронных сообщений осуществляет Windows. Если рассматривать синхронное сообщение, то его извлечение производится функцией GetMessage() с последующей передачей его обратно в Windows функцией DispatchMessage(). Асинхронное сообщение, независимо от источника, который инициирует его появление, сначала попадает в Windows и затем в нужную оконную функцию.

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

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam)

видно, что она получает при активации ее (функцией DispatchMessage()) 4 параметра. Первый параметр (hwnd) – дескриптор окна, которому предназначено данное сообщение. Это – тот самый дескриптор, который был получен нами как результат работы функции CreateWindow(), и в нашем варианте программы тут же потерян, так как был объявлен как локальная для функции Create() переменная. Теперь этот же дескриптор вернулся к нам из Windows как параметр оконной функции. Он особенно полезен в тех случаях, когда на базе одного класса создается несколько различающихся чем-то окон. Если класс один, то и оконная функция для всех этих окон одна; анализируя тогда параметр hwnd, программа может определить, в какое именно окно пришло сообщение. У нас окно одно, однако, аргумент hwnd все же понадобится. Второй параметр (msg) определяет код пришедшего сообщения. Поскольку сообщений много, оконная функция должна, прежде всего, проанализировать этот код и осуществить переход на фрагмент обработки соответствующего сообщения. wParam и lParam – дополнительные параметры, являющиеся копиями соответствующих полей структуры поступившего сообщения. Оставшиеся два поля структуры msg - time и POINT используются достаточно редко, и при необходимости их значения можно извлечь непосредственно из экземпляра структуры сообщения.

Центральным местом оконной функции является синтаксическая конструкция, в задачу которой входит распознавание поступившего сообщения по его типу и передача управления на ту ветвь кода оконной функции, которая продолжает далее работу с параметрами сообщения. Для этого используется оператор switch…case (переключатель).

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

switch(msg)

{

case WM_CREATE:

. . . //Обработка сообщения WM_CREATE

case WM_DESTROY:

. . . //Обработка сообщения WM_DESTROY

case WM_KEYDOWN:

. . . // Обработка сообщения WM_KEYDOWN

case WM_MOUSEMOVE:

. . . // Обработка сообщения WM_MOUSEMOVE

case WM_TIMER:

. . . // Обработка сообщения WM_TIMER

// и т. д. для всех возможных сообщений

}

Оконная функция, обрабатывающая сообщения, имеет одну точку входа и множество точек выхода. Выход из оконной функции осуществляется из той ее ветви, где обрабатывалось сообщение. Однако реально в любой программе обрабатываются далеко не все сообщения. Например, если программа управляется только мышью, в ней нет необходимости обрабатывать сообщения от клавиатуры; совсем не обязательно программа имеет дело с таймером и т. п. Надо еще иметь в виду, что приложение получает огромное количество сообщений системного характера, например, о перерисовке отдельных элементов окна, определения его размеров, изменении положения окна и т. п. Все эти сообщения мы, возможно, даже не умеет обрабатывать должным образом. Для того, чтобы дать возможность программисту работать только с теми сообщениями, которые ему нужны, в Windows предусмотрена специальная функция DefWindowProc(), которая умеет правильно обрабатывать практически все сообщения Windows. Единственным сообщением, не входящим в “юрисдикцию” DefWindowProc(), является сообщение об уничтожении окна WM_DESTROY. Поэтому в простейшем случае, когда мы вообще не предполагаем работать с сообщениями, оконная функция должна включать в себя обработку только одного сообщения WM_DESTROY:

switch(msg)

{

case WM_DESTROY:

PostQuitMessage(0);

return 0;

default:

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

}

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

Соседние файлы в папке 04-09-2015_19-17-13