Смекни!
smekni.com

Ознакомление с приложениями Windows (стр. 8 из 18)

Функция GetMessage возвращает во всех случаях, кроме одного, ненулевое значение, указывающее, что цикл надо продолжать. Только в одном случае эта функция возвратит 0 — если она извлечет из очереди сообщение WM_QUIT. Это сообщение посылается только при окончании работы приложения.

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

И остается еще одно дело: так как WinMain возвращает результат, то мы должны вернуть какое–либо значение. В Windows принято, что возвращаемое значение является параметром wParam сообщения WM_QUIT, завершившего цикл обработки сообщений. Таким образом мы пишем:

return msg.wParam;

Оконная процедура

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

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

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

LRESULT WINAPI _export proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
}

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

LRESULT WINAPI _export proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// описание внутренних переменных

switch ( uMsg ) {
case WM_...:
// обработка нужного сообщения
break;

// обработка других сообщений...

default:
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
return 0L;
}

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

Для упрощения написания оконной функции Windows предлагает специальную функцию

LRESULT DefWindowProc( hWnd, uMsg, wParam, lParam );

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

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

Сообщение WM_CREATE

Самым первым мы рассмотрим сообщение WM_CREATE. Это сообщение посылается окну в тот момент, когда оно создается. Реальным созданием окна ведает функция CreateWindow, а не обработчик сообщения WM_CREATE. Вы в этот момент должны инициализировать свои переменные, выполнить необходимые настройки, создать требуемые объекты и прочее. При создании окно еще невидимо — поэтому Вы можете менять его размеры, устанавливать в нужное положение, менять цвета не опасаясь мелькания на экране. Часто здесь создаются необходимые структуры и выделяются требуемые окном ресурсы.

Стандартная обработка этого сообщения необязательна — функция DefWindowProc просто возвращает 0 в ответ на это сообщение.

Параметр wParam не используется, а параметр lParam содержит указатель[11] на структуру CREATESTRUCT. В этой структуре передается основная информация о создаваемом окне.

typedef struct tagCREATESTRUCT {
void FAR* lpCreateParams; // указатель на дополнительные данные,
// переданный как параметр lpParam в вызове
// функции CreateWindow или CreateWindowEx
HINSTANCE hInstance; // хендл копии приложения, создавшей окно
HMENUhMenu; // хендл меню (или NULL, если нет)
HWND hwndParent; // хендл родительского окна (или NULL)
intcy, cx; // размеры окна
inty, x; // положение окна
LONGstyle; // стили окна
LPCSTRlpszName; // заголовок окна
LPCSTRlpszClass; // имя класса, к которому принадлежит окно
DWORDdwExStyle; // расширенные стили окна (см. CreateWindowEx)
} CREATESTRUCT;

Поля x, y, cx и cy в момент обработки сообщения WM_CREATE могут быть еще не определены. При необходимости получить информацию о размере или положении окна надо пользоваться функциями GetWindowRect или GetClientRect, которые возвращают корректный результат

Возвращаемое обработчиком значение:

не 0 — возникла ошибка, окно надо уничтожить (далее, при уничтожении, будет получено сообщение WM_DESTROY), функция CreateWindow или CreateWindowEx вернет NULL.

0 — окно успешно создано; функция CreateWindow или CreateWindowEx вернет хендл окна.


Сообщения WM_DESTROY и WM_QUIT

Еще одно сообщение, интересующее нас — WM_DESTROY. Это сообщение посылается окну в момент его уничтожения. Это одно из последних сообщений, которое получает окно — можно считать, что после этого оно уже не существует, причем в момент получения этого сообщения окно уже невидимо. В этот момент вы можете выполнить необходимые операции по освобождению выделенных при создании окна ресурсов. Оба параметра сообщения WM_DESTROY не используются.

Как и WM_CREATE сообщение WM_DESTROY является информационным — реальное уничтожение окна осуществляется функцией DestroyWindow, а не обработчиком сообщения WM_DESTROY. Независимо от способа обработки этого сообщения окно будет уничтожено.

Если ваша оконная функция обслуживает главное окно приложения, то в момент уничтожения окна вы должны принять меры для завершения работы всего приложения в целом. Для этого вы должны послать сообщение WM_QUIT, при извлечении которого из очереди закончится цикл обработки сообщений. Если этого сообщения не послать, то цикл обработки сообщений продолжит работу дальше после закрытия всех имеющихся окон. На практике это означает что приложение вообще перестанет получать сообщения и “зависнет” на функции GetMessage, которая будет ждать, пока не придет новое сообщение. В случае Windows 3.x есть единственный способ удалить такое приложение — перезапуск всего Windows (в Windows–95 или Windows NT такое приложение можно снять самому с помощью менеджера задач). Сообщение WM_QUIT посылается с помощью функции:

void PostQuitMessage( nExitCode );

Параметр nExitCode будет передан как wParam сообщения WM_QUIT и позже возвращен функцией WinMain в качестве параметра завершения.

Обработчиков для сообщения WM_QUIT писать не надо, так как:

Во–первых, функция GetMessage, получив это сообщение просто вернет FALSE, так что цикл обработки сообщений будет закончен без трансляции и диспетчеризации этого сообщения.

Во–вторых, это сообщение адресовано не окну, а приложению. То есть, даже если воспользоваться функцией PeekMessage для извлечения сообщения WM_QUIT из очереди (в отличие от GetMessage это получится), оно не будет отправлено никакому окну, так как хендл окна–получателя равен NULL.

Сообщение WM_PAINT

Последнее рассматриваемое нами сообщение — WM_PAINT. Оба параметра сообщения WM_PAINT не используются. С этим сообщением нам придется разбираться подробнее. Раньше, когда мы обсуждали разделение экрана между разными задачами, говорилось о том, что в Windows невозможно полностью виртуализовать всю работу с экраном — то есть содержимое невидимой в данной момент части окна для Windows остается неизвестным.

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

Для этого вводится специальное сообщение WM_PAINT, которое посылается окну тогда, когда часть окна (или все окно) нуждается в перерисовке. Часто перерисовка окна занимает значительное время, а обработку сообщений рекомендуется выполнять как можно быстрее; поэтому Windows особым образом обрабатывает WM_PAINT, что бы задержки в обработке этого сообщения не оказывали такого влияния на работу системы.

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

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

Основы рисования в окне

Еще раз вспомним, каким образом появляется сообщение WM_PAINT: это происходит когда все окно, или только его часть становятся видимыми. Та часть окна, которая нуждается в перерисовке, является неверной — ее содержимое не соответствует требуемому. Эта часть получила название неверный прямоугольник (invalidrectangle).

Соответственно, после перерисовки она перестает быть неверной — то есть становится верным прямоугольником (validrectangle).

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

Неверный прямоугольник возникает, если:

· скрытая область окна (например, закрытая другим окном) становиться видимой.

· область окна “прокручивается” (с помощью функций ScrollWindow или ScrollDC). В этом случае та часть окна, в которой должен появиться новый, невидимый ранее, текст, объявляется неверным прямоугольником.