Последней частью программы WinMain() является цикл обработки сообщений. Такой цикл содержится во всех приложениях для Windows. Его целью является получение и обработка сообщений, передаваемых операционной системой. Эти сообщения ставятся в очередь сообщений приложения, откуда они затем (по мере готовности программы) выбираются функцией API GetMessage():
BOOL GetMessage(LPMSG msg, HWND hwnd,
UINT min, UINT max).
Выбираемые из очереди сообщения сохраняются в структуре, указатель на которую представляет параметр msg. Все сообщения Windows имеют структуру типа MSG, определяемую следующим образом:
// Структура сообщения
typedef struct tagMSG
{
HWND hwnd; // Окно для направленного сообщения
UINT message;// Собственно сообщение
WPARAM wParam; // Дополнительная информация
LPARAM lParam; // к сообщению
DWORD time; // Время посылки сообщения
POINT pt; // Положение курсора мыши
} MSG;
В поле hwnd структуры MSG записывается дескриптор окна, которому адресовано сообщение; тип сообщения записывается в 32-разрядное поле message, а дополнительная информация (параметры сообщения) – в поля wParam и lParam. Типы WPARAM и LPARAM определены при помощи typedef соответственно как UINT и LONG.
Время посылки сообщения записывается в поле time в миллисекундах. Поле pt содержит координаты курсора мыши в момент посылки сообщения. Структура POINT, в которую записываются эти координаты, определяется так:
typedef struct tagPOINT
{
LONG x,y;
} POINT;
Если очередь сообщений приложения пуста, вызов GetMessage() возвратит управление Windows. (Более детально сообщения рассматриваются в следующей главе.)
Параметр hwnd функции GetMessage() определяет окно, которому направляется получаемое сообщение. Возможно, и даже скорее всего, Ваше приложение будет содержать несколько окон, а Вы захотите получать сообщения, адресованные только одному фиксированному окну. Если же Вы хотите получать все сообщения, адресованные Вашему приложению, параметр hwnd должен быть равен NULL.
Остальные два параметра функции GetMessage() определяют диапазон получаемых сообщений. Чаще всего Вам нужно будет получать все сообщения. Для этого параметры min и max должны быть равны 0, как это сделано в каркасной программе.
GetMessage() возвращает нуль, когда пользователь завершает программу. Иначе возвращается ненулевое значение.
Внутри цикла обработки сообщений вызываются две функции. Вначале вызывается функция API TranslateMessage(), которая транслирует виртуальные коды клавиш, генерируемые Windows, в клавиатурные сообщения. (Виртуальные коды клавиш рассматриваются в гл. 4.) Хотя это и не является необходимым, большинство приложений вызывают TranslateMessage(), поскольку это позволяет использовать в программе ввод с клавиатуры.
Когда сообщение прочитано и преобразовано, функция API DispatchMcssage() возвращает его обратно к Windows. Windows хранит это сообщение до тех пор, пока оно не будет послано оконной функции приложения.
Когда цикл обработки сообщений завершается, функция WinMain() также заканчивается, возвращая значение msg.wParam. Это значение будет кодом возврата программы.
Оконная функция
Другая функция в каркасной программе является оконной функцией. В нашей программе она названа WindowFunc(), но она может иметь и любое другое имя. В качестве параметров этой функции передаются первые четыре поля структуры MSG. В нашей каркасной программе единственным используемым параметром является собственно сообщение. Однако в следующей главе Вы узнаете, как используются другие параметры этой функции.
В нашем примере оконная функция самостоятельно обрабатывает единственное сообщение WM_DESTROY. Это сообщение посылается, когда пользователь завершает программу. Обрабатывая его, оконная функция должна вызвать функцию API PostQuitMessage(). Значение параметра этой функции будет использовано как код возврата программы. Вызов PostQuitMessage() приводит к посылке Вашему приложению сообщения WM_QUIT, получив которое, функция GetMessage() возвращает нулевое значение и завершает тем самым цикл обработки сообщений и Вашу программу.
Все остальные сообщения, получаемые оконной функцией, направляются Windows с помощью вызова функции DefWindowProc() для обработки по умолчанию. Это необходимо, поскольку все сообщения должны быть обработаны тем или иным способом.
Соглашения об именах. Если Вы не знакомы с программированием под Windows, некоторые имена и описания, употребляемые в каркасной программе, могут показаться несколько необычными. Однако они соответствуют соглашениям, представленным фирмой Microsoft для программирования под Windows. Для функций используются имена, построенные из глаголов и существительных, причем первые буквы этих слов – заглавные. Мы будем следовать этому соглашению в отношении большинства имен функций, упомянутых в книге.
Для имен переменных Microsoft предлагает более сложную систему, предусматривающую обозначение именуемых типов данных. Для этого используется небольшой префикс из строчных букв, а собственно имя начинается с заглавной буквы. Типы префиксов представлены в нижеследующей таблице 2.6. Откровенно говоря, использование префиксов, обозначающих тип данных, спорно и не всегда адекватно. Поскольку большинство Windows-программистов прибегают к такой системе именования, в данной книге будет использоваться именно эта система; Вы же в своих программах можете поступать по своему усмотрению.
Таблица 2.6
Типы префиксов
№ | Префикс | Тип данных |
b | Булевский (байт) | |
c | Символ (байт) | |
dw | Длинное беззнаковое целое (DWORD) | |
f | 16-битный флаг (битовая карта) | |
fn | Функция | |
h | Дескриптор (handle) | |
l | Длинное целое (long) | |
lp | Длинный указатель (long pointer) | |
n | Целое (16 бит) | |
p | Указатель (pointer) | |
pt | Точка (два 32-битных целых) | |
w | Целое без знака (WORD, 16 бит) | |
sz | Указатель на строку, заканчивающуюся 0 (string zero) | |
lpsz | Длинный указатель на sz (long pointer string zero) | |
rgb | Длинное целое, содержащее цветовую комбинацию RGB |