Окт 13 2009

Обработка сообщений WM_CLOSE и WM_DESTROY

Сообщение WM_CL0SE появляется, когда пользователь щелкает мышью на кнопке закрытия окна или нажимает комбинацию клавиш Alt+F4. Вообще, обработка этого сообщения не обязательна: если она отсутствует, то функция DefWindowProc вызовет по умолчанию функцию DestroyWindow. Но если вы хотите предусмотреть вывод каких-либо предупреждающих сообщений типа «А вы точно уверены, что хотите это сделать?», то данное место — самое подходящее. Ибо окно еще не разрушено, а вот после сообщения WM_DESTROY окна на экране уже нет.
Функция DestroyWindow, оправдывая свое имя, разрушает указанное в ее параметре окно. Для этого она посылает окну сообщение WM_DESTROY. Если у данного окна есть дочерние окна, то функция посылает WM_DESTROY сначала ему, а потом уже дочерним окнам. Функция завершает свою работу только после уничтожения всех дочерних окон.
Когда главное окно приложения получает сообщение WM_DESTROY, то оконная процедура должна позаботиться о том, чтобы приложение корректно «покинуло сцену». Для этого вызывается функция PostQuitMessage, посылающая сообщение WM_QUIT. Ну а что происходит в цикле обработки сообщений при появлении WM_QUIT, вы уже знаете.
Разрешите вас поздравить: мы завершили анализ текста программы Hellol — первого варианта приложения «Hello, World!».
Как, будет еще и второй вариант?.. — Не волнуйтесь, пояснения к нему будут гораздо короче.


Окт 13 2009

Атрибуты цвета и фона для выводимого текста

В приложении может использоваться функция SetTextColor для установки цвета текста (text color) в клиентской области окна. Функция SetBkColor используется для установки цвета фона графического элемента (background color), то есть цвета, который отображается позади каждого символа. Еще можно регулировать режим смешивания фона (background mix mode), вызывая функцию SetBkMode. Ее второй параметр может принимать значение OPAQUE1, если цвет фона графического элемента выводится поверх существующего фона окна, прежде чем будет выведен символ. Также можно использовать значение TRANSPARENT2, при котором цвет фона графического элемента игнорируется, а символ выводится на существующем фоне.


Окт 13 2009

Обработка сообщения WM_PAINT

Обработка сообщения WM_PAINT крайне важна для программирования под Windows. Это сообщение уведомляет программу, что часть или вся клиентская область окна недействительна (invalid) и ее следует перерисовать. Во второй главе тема обработки сообщения WM_PAINT будет рассмотрена более подробно, поэтому здесь излагается тот минимум, который необходим для понимания работы программы Hellol.
В каких ситуациях клиентская область окна становится недействительной? Очевидно, что при создании окна недействительна вся его клиентская область, поскольку в ней еще ничего не нарисовано. Это раз.
Если вы меняете размеры окна, клиентская область также объявляется системой недействительной, что обусловлено комбинацией стилей CS_HREDRAW | CS_VREDRAW в поле style структуры класса окна. Это два.
Когда вы минимизируете окно программы Hellol, сворачивая его на панель задач, а затем снова разворачиваете до начального размера, то Windows объявляет клиентскую область окна недействительной. Это три.
Если вы перемещаете окна так, что они перекрываются, а затем закрытая часть окна вновь открывается, то Windows помечает требующую восстановления клиентскую область как недействительную. Это четыре.
Можно было бы продолжить, но оставим подробности для второй главы.
Во всех перечисленных случаях, кроме первого, операционная система автоматически помечает клиентскую область окна как недействительную, что влечет за собой посылку сообщения WM_PAINT. В первом случае, когда окно только что было создано, аналогичный эффект достигается вызовом функции ShowWindow. В процессе ее выполнения генерируются сообщения WM_SIZE и WM_M0VE, а обрабатывая WM_SIZE, система автоматически генерирует сообщение WM_PAINT.
Завершив этот маленький теоретический экскурс, вернемся к анализу нашего кода.
Обработку сообщения WM_PAINT рекомендуется всегда начинать с вызова функции Begin Paint:
hDC - BeginPainUhWnd, &ps);
Первый параметр функции содержит дескриптор окна, полученный через аргумент оконной процедуры, а второй — адрес структуры ps типа PAINTSTRUCT. Поля этой структуры, заполняемые в результате выполнения функции BeginPaint, в дальнейшем используются операционной системой1. Для нас сейчас более важно возвращаемое функцией значение — это дескриптор так называемого контекста устройства.
Контекст устройства (device context) описывает физическое устройство вывода информации, например дисплей или принтер. Этот важнейший объект графической подсистемы Windows рассматривается подробно во второй главе. Сейчас достаточно понимания того, что контекст устройства — это некоторая внутренняя структура данных, сохраняющая часто используемые графические атрибуты, такие как цвет фона, перо, кисть, шрифт и им подобные параметры. Эти атрибуты используются при вызове всех рисующих функций, получающих дескриптор hDC в качестве параметра.
Прежде чем перейти к следующей строке программного текста, отметим два побочных эффекта выполнения функции BeginPaint.
Во-первых, обрабатывая вызов BeginPaint, система Windows обновляет фон клиентской области, если обновляемый регион помечен для стирания1. По умолчанию для этого используется кисть, заданная в поле hbrBackground структуры WNDCLASSEX.
Во-вторых, вызов функции BeginPaint делает всю клиентскую область действительной (valid), то есть не требующей перерисовки. Это предотвращает повторную генерацию системой сообщения WM_PAINT до тех пор, пока вновь не произойдет одно из событий, требующих перерисовки окна.
После вызова BeginPaint в рассматриваемой программе следует вызов функции GetClientRect, предназначенной для получения размеров клиентской области окна:
GetClientRect(hWnd. Srect);
Результат работы функции помещается в переменную rect типа RECT. Структура RECT, описывающая прямоугольник (rectangle), определена в заголовочных файлах Windows следующим образом:
typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom; } RECT;
Поля этой структуры задают координаты левого верхнего угла (left, top) и правого нижнего угла (right, bottom) прямоугольника.
После выполнения функции GetClientRect поля left и top всегда получают нулевое значение, а поля right и bottom содержат ширину и высоту клиентской области в пикселах. Информация о размерах клиентской области, сохраненная в переменной rect, используется далее в «рисующей» функции DrawText.
В нашей программе все «рисование» сводится к выводу текста «Hello, World!» при помощи функции DrawText, имеющей следующий прототип:
BOOL DrawText(
HDC hdc. // дескриптор контекста устройства
LPCTSTR IpString. // указатель на символьную строку
int nCount, // длина текста
LPRECT lpRect, // указатель на ограничивающий прямоугольник
UINT uFormat // флаги форматирования текста );
Функция2 выводит текст из строки IpString в прямоугольную область, заданную структурой типа RECT, используя метод форматирования, заданный параметром uFormat Количество символов в выводимой строке задается параметром nCount. Если установить значение nCount в -1, то система сама определит длину строки IpString по завершающему нулевому символу. Но в этом случае программист должен позаботиться о том, чтобы строка действительно завершалась нулевым байтом.
На месте последнего параметра функции задан набор флагов DT_SINGLELINE | DT_CENTER | DT_VCENTER, значения которых определяются в заголовочных файлах Windows. Флаги показывают, что текст будет выводиться в одну строку, по центру относительно горизонтали и вертикали внутри прямоугольной области, заданной четвертым параметром. Благодаря этому текст «Hello, World!» выводится в центре клиентской области.
Когда клиентская область становится недействительной (например, при изменении размеров окна), WndProc получает новое сообщение WM_PAINT. Обрабатывая его, программа вновь вызывает функцию GetClientRect и поэтому рисует текст опять в центре окна.
После окончания работы с графическими функциями в блоке обработки сообщения WM_PAINT необходимо освободить полученный контекст устройства с помощью функции EndPaint.
Следует отметить, что при выводе текста используются текущие установки для шрифта, цвета фона и цвета текста. О том, как управлять этими атрибутами, будет рассказано во второй главе. Для тех, кто хочет экспериментировать с цветами текста и фона немедленно, ниже приводится краткая справка.


Окт 13 2009

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

Реальная работа приложения осуществляется в оконной процедуре (window procedure). Оконная процедура определяет то, что выводится в клиентскую область окна, и то, как окну реагировать на пользовательский ввод.
Заголовок оконной функции всегда имеет следующий вид:
LRESULT CALLBACK WndProcCHWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Четыре параметра оконной процедуры идентичны первым четырем полям структуры MSG. Первый параметр функции содержит дескриптор окна, получающего сообщение. Во втором параметре указывается идентификатор сообщения. Для системных сообщений в Win32 зарезервированы номера от 0 до 1024. Третий и четвертый параметры содержат дополнительную информацию, которая распознается системой в зависимости от типа полученного сообщения.
Вместо имени WndProc можно использовать любое другое имя, но оно должно совпадать со значением поля IpfnWndProc структуры wc.
В рассматриваемой программе имеется только одно окно класса MyClass. Дескриптор окна hMainWnd получен вызовом функции CreateWindow. Поэтому при вызове оконной процедуры ее параметру hWnd всегда передается значение hMainWnd. Если же в программе создается несколько окон на основе одного и того же класса окна (и, следовательно, имеющих одну и ту же оконную процедуру), тогда параметру hWnd будет передаваться дескриптор конкретного окна, получающего сообщение.
Обычно программисты используют оператор switch для определения того, какое сообщение получено и как его обрабатывать. Если сообщение обрабатывается, то оконная процедура обязана вернуть нулевое значение. Все сообщения, не обрабатываемые оконной процедурой, должны передаваться системной функции DefWindowProc. В этом случае оконная процедура должна вернуть то значение, которое возвращает DefWindowProc.
Таким образом, программист пишет код только для тех сообщений, которые нуждаются в нестандартной обработке. В программе Hellol таких сообщений всего три: WM_PAINT, WM_CL0SE и WM_DESTR0Y.


Окт 13 2009

Обработка сообщений

После вывода окна на экран программа должна подготовить себя для получения информации от пользователя. Эта информация обычно вводится с помощью клавиатуры или мыши. Windows поддерживает «очередь сообщений» (message queue) для каждой программы, работающей в системе. Любое действие пользователя система Windows преобразует в «сообщение», которое помещается в указанную очередь.
Функция GetMessage возвращает значение TRUE при извлечении любого сообщения, кроме одного — WM_QUIT. Получив сообщение WM_QUIT, функция возвращает значение FALSE. В результате этого происходит немедленный выход из цикла, и приложение завершает работу, возвращая операционной системе код возврата msg.wParam.
Но какова дальнейшая судьба принятого сообщения?
В теле цикла обработки сообщений можно увидеть вызов двух функций: TranslateMessage и DispatchMessage.
Строго говоря, вызов TranslateMessage нужен только в тех приложениях, которые должны обрабатывать ввод данных с клавиатуры. Дело в том, что для обеспечения независимости от аппаратных платформ и различных национальных раскладок клавиатуры в Windows реализована двухуровневая схема обработки сообщений от символьных клавиш. Сначала система генерирует сообщения о так называемых виртуальных клавишах, например: сообщение WM_KEYDOWN — когда клавиша нажимается, и сообщение WM_KEYUP — когда клавиша отпускается. В сообщении WM_KEYDOWN содержится также информация о так называемом скан-коде1 нажатой клавиши.
Функция TranslateMessage преобразует пару сообщений, WM_KEYDOWN и WM_KEYUP, в сообщение WM_CHAR, которое содержит код символа (wParam) в виде значения типа TCHAR2. Сообщение WM_CHAR помещается в очередь, а на следующей итерации цикла функция GetMessage извлекает его для последующей обработки.
Наконец, функция DispatchMessage передает структуру msg обратно в Windows. Очередной ход — за Windows. Windows отправляет сообщение для его обработки соответствующей оконной процедуре, вызывая ее как функцию обратного вызова.
Следует обратить внимание на то, что сообщение передается оконной процедуре именно того окна, которому оно было адресовано. Это может быть или WndProc (если сообщение адресовано главному окну), или оконная процедура некоторого диалогового окна, или оконная процедура одного из стандартных элементов управления, спрятанная в недрах Windows (то есть в DLL).
После возврата из оконной процедуры Windows передает управление оператору, указанному за вызовом DispatchMessage, то есть на начало цикла while, и работа цикла продолжается.
Как уже говорилось ранее, цикл обработки сообщений функционирует до тех пор, пока из очереди не поступит сообщение WM_QUIT. Появление этого сообщения вызывает немедленный выход из цикла и прекращение работы программы.


Окт 13 2009

Отображение окна на экране

Для отображения на экране созданного окна вызывается функция ShowWindow, имеющая следующий прототип:
BOOL ShowWindow(HWND hWnd, int nCmdShow);
Первым параметром функции является дескриптор окна, а второй параметр определяет, в каком виде будет показано окно.
При начальном отображении главного окна рекомендуется присваивать второму параметру то значение, которое передается приложению через параметр nCmdShow функции WinMain.


Окт 13 2009

Создание окна

Если регистрация класса окна прошла успешно, то следующий этап — создание окна.
Рассматривая назначение параметров, не будем забывать, что функция Create Window позволяет создавать не только основное окно приложения, но и любые другие окна, включая окна предопределенных классов, с помощью которых реализуются стандартные элементы управления Windows.
Первый параметр, IpQassName, — это указатель на строку, содержащую допустимое имя класса окна. Таким именем может быть либо имя класса, зарегистрированного ранее при помощи функции RegisterClassEx или RegisterClass, либо имя одного из предопределенных классов.
Второй параметр, LpWindowName, — это указатель на строку, содержащую имя окна. Место отображения имени зависит от вида окна. Например, для главного окна приложения оно выводится как заголовок окна, а для окна предопределенного класса BUTTON размещается по центру кнопки.
Третий параметр, dwStyle, позволяет указывать стиль окна.
Четвертый параметр, х, задает горизонтальную позицию левого верхнего угла окна. Для главного окна позиции х и у определяются в экранных координатах, а для дочерних окон и элементов управления координаты отсчитываются относительно левого верхнего угла родительского окна.
Если позиция х не важна, то можно установить значение CW_USEDEFAULT. В этом случае Windows использует значения х и у по умолчанию. Это означает, что система располагает следующие друг за другом перекрывающиеся окна, равномерно отступая по горизонтали и вертикали от левого верхнего угла экрана.
Пятый параметр, у, задает вертикальную позицию левого верхнего угла окна. Если параметр х имеет значение CWJJSEDEFAULT, то значение параметра у игнорируется.
Шестой параметр, пWidth, задает ширину окна в пикселах. Если ему присвоено значение CWJJSEDEFAULT, то система будет использовать значения nWidth и nHight по умолчанию.
Седьмой параметр, nHight, задает высоту окна в пикселах. Если параметр nWidth установлен в CWJJSEDEFAULT, то значение nHight игнорируется.
Восьмой параметр, hWnd Parent, в рассматриваемой программе имеет значение NULL, поскольку у главного окна программы отсутствует родительское окно. Заметим, что, если между двумя окнами существует связь типа родительское — дочернее, дочернее окно всегда появляется только на поверхности родительского.
Девятый параметр, hMenu, содержит дескриптор меню окна или идентификатор элемента управления. Интерпретация значения параметра зависит от вида окна.
Если приложение использует меню, определенное в классе окна (поле IpszMenu-Name в структуре WNDCLASSEX), то параметру hMenu необходимо передать значение NULL
Если создаваемое окно относится к элементу управления, то параметру hMenu передается целочисленное значение, которое далее используется как идентификатор созданного элемента. Этот идентификатор, например, будет содержаться в сообщениях WMJZOMMAND, поступающих от элемента управления.
Десятому параметру, hlnstance, должен быть присвоен дескриптор экземпляра приложения, переданный программе в качестве аргумента функции WinMain.
Последний параметр, IParam, в рассматриваемом примере тоже имеет значение NULL. При необходимости он может быть использован в качестве указателя на некие дополнительные данные, передаваемые окну в момент его создания с помощью сообщения WM JIREATE.
Итак, функция CreateWindow вызвана. Отработав, она возвращает дескриптор созданного окна. Если по какой-то причине создать окно не удалось, то функция возвращает значение NULL Поэтому рекомендуется проверять возвращаемое функцией значение и в случае неудачи выдавать соответствующее сообщение, используя уже знакомую функцию MessageBox.


Окт 13 2009

Регистрация класса окна

Сразу после входа в функцию WinMain создается и регистрируется класс главного окна приложения. Для этого необходимо заполнить структуру типа WNDCLASSEX, а затем передать адрес этой структуры в виде аргумента функции RegisterClassEx.
В заголовочных файлах Windows идентификаторы, начинающиеся с префикса CS_, задаются в виде 32-разрядной константы, в которой только один разряд установлен в единичное значение. Например, константа CS_VREDRAW задана как 0x0001, a CS_HREDRAW — как 0x0002. Подобные константы иногда называют поразрядными флагами (bit flags). Они могут объединяться с помощью операции ИЛИ языка C++.
В рассматриваемой программе используется комбинация стилей CS_HREDRAW | CS_VREDRAW. Это означает, что все окна этого класса должны целиком перерисовываться при изменении как горизонтального, так и вертикального размеров окна. Если попробовать изменить размеры окна A Hellol Application, то можно увидеть, что строка текста переместится в новый центр окна. Механизм уведомления оконной процедуры об изменении размеров окна будет рассмотрен позже.
Третье поле, IpfnWndProc, содержит адрес оконной процедуры (в нашей программе это — WndProc).
Назначение полей 4 — 6 представляется очевидным1.
Седьмое поле, hlcon, содержит дескриптор пиктограммы, которая предназначена для этого класса окна. Пиктограмма (синонимы: иконка, значок) — это маленькая битовая картинка, которая появляется на панели задач Windows и в левой части заголовка окна. Значение hlcon обычно получают вызовом функции Loadlcon, которая имеет следующий прототип:
HICON LoadlconCHINSTANCE hlnstance, LPCTSTR IpIconName);
Эта функция загружает ресурс пиктограммы, заданный параметром IpIconName, из экземпляра приложения, указанного параметром hlnstance2. Функцию можно использовать также для загрузки одной из системных (предопределенных) пиктограмм, если передать первому аргументу значение NULL. В этом случае второй аргумент должен содержать константу, идентификатор которой начинается с префикса IDI_ («идентификатор значка» — ID for icon).


Окт 13 2009

Файл исходного текста программы

В файле Hellol.cpp расположены только две функции: WinMain и WndProc. WinMain — это точка входа в программу. WndProc — это оконная процедура для главного окна
программы. Оконная процедура инкапсулирует код, отвечающий за ввод информации, обычно осуществляемый при помощи клавиатуры или мыши, а также за вывод информации на экран.
В Hellol.cpp отсутствуют инструкции для непосредственного вызова WndProc — оконная процедура вызывается только из Windows. Однако в WinMain имеется ссылка на WndProc (адрес оконной процедуры присваивается полю wc.lpfnWndProc), поэтому прототип функции WndProc объявлен в самом начале файла, еще до определения функции WinMain.


Окт 13 2009

Программа «Hello, World!» — первый вариант

Полноценная программа для Win32 должна содержать как минимум две функции:
WinMain — главную функцию, в которой создается основное окно программы и запускается цикл обработки сообщений;
WndProc — оконную процедуру, обеспечивающую обработку сообщений для основного окна программы. На некотором псевдоязыке каркас Windows-программы можно представить
следующим образом:
WinMain (список аргументов)
{
Подготовить и зарегистрировать класс окна с требуемыми характеристиками; Создать экземпляр окна зарегистрированного класса;
Пока не произошло необходимое для выхода событие { Извлечь очередное сообщение из очереди сообщений; Передать его через Windows оконной функции;
}
Возврат из программы;
}
WndProc (список аргументов)
{
Обработать полученное сообщение; Возврат; }
Не будем отступать от традиции и покажем, как написать приложение, которое создает окно и выводит в нем строку «Hello, World!».


« Предыдущая страницаСледующая страница »