Окно (window) – один из наиболее важных объектов в ОС Windows. Окно представляет собой прямоугольную область, в которой приложение отображает выводимую информацию и из которой получает вводимую. Кроме того, окно участвует и в программных интерфейсах – оно является получателем сообщений. Существует много разновидностей окон: диалоговые, окна многодокументных интерфейсов (MDI), окна сообщений, окна элементов управления. Присутствующие в системе окна образуют иерархию, в которой они могут находиться в отношениях владелец – подчиненный и предок – потомок. Окна имеют заголовки (Title, Caption), но в большинстве случаев обращение к ним происходит по описателям – Handle (системный тип HWND)
Класс окна (Window Class) определяет общие свойства и особенности поведения группы окон. Классы делятся на системные глобальные (к ним относятся, например, стандартные элементы управления), прикладные глобальные (реализуются в DLL, требуют регистрации, после чего могут использоваться любыми приложениями) и прикладные локальные (регистрируются как доступные единственному приложению). Именно класс связывает окно с его оконной процедурой. Классы идентифицируются, как правило, по их именам.
Для создания и регистрации классов служат функции RegisterClass() и RegisterClassEx(), для создания окон любых видов – функции CreateWindow() и CreateWindowEx().
Оконная процедура (Window Procedure или WndProc) является получателем всех сообщений, адресованных окну. Обычно представляет собой конструкцию вида switch – case, распознающую поступившие сообщения и выполняющую их обработку:
LRESULT CALLBACK
MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case M_CLOSE: //команда на закрытие окна
DestroyWindow(hWnd);
break;
case WM_DESTROY: //подтверждение о готовности деинициализации
PostQuitMessage(0);
break;
case …
break;
default: //для всех остальных сообщений
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
Примечание. В данном примере правильнее было бы проверять адресата сообщений и завершать приложение, только если это его главное окно.
Сообщения (Window Message) лежат в основе механизмов событийного управления приложениями. Они представляют собой структуры, передаваемые между приложениями и несущими информацию о событиях, команды, данные и так далее. Для представления сообщений служит структура MSG, поля которой содержат: тип сообщения, пару параметров wParam, lParam, адресата сообщения, время и экранные координаты его возникновения (имеет смысл для некоторых сообщений).
Сообщения передаются в приложение либо напрямую, либо через очередь. Прямая передача сообщения оконной процедуре является фактически косвенным вызовом этой процедуры, в том числе и из другого приложения. Для этого служит системная функция SendMessage(). Важно, что выполнение отправителя сообщения блокируется до завершения оконной процедуры.
Многие сообщения передаются не напрямую, а через очередь, что избавляет программы от взаимной зависимости и блокировок. Для этого служит функция PostMessage(). Кроме того, есть некоторые другие функции, сочетающие свойства синхронной и асинхронной отсылки, например SendNotifyMessage(), которая передает сообщение минуя очередь, но и не ожидает окончания его обработки, поэтому блокирующей не является.
Цикл обработки сообщений выбирает сообщения из очереди и перенаправляет их в соответствующие оконные процедуры. В упрощенном представлении, цикл образуют три основные функции:
MSG msg;
while (GetMessage(&msg,NULL,0,0)) //выборка сообщений из очереди
{
TranslateMessage(&msg); //доп. обработка (трансляция) сообщения
DispatchMessage(&msg); //передача сообщения в оконную процедуру
}
Здесь выбираются все сообщения для всех окон приложения, однако параметры GetMessage() позволяют накладывать на них фильтры.
Функция GetMessage() является блокирующей – при отсутствии сообщений в очереди она переводит приложение в состояние ожидания. При наличии подходящего по фильтрам сообщения оно удаляется из очереди, и его содержимое переносится в структуру MSG. Функция возвращает нулевое значение при получении единственного сообщения WM_QUIT, для всех остальных – ненулевое. Как следствие цикл прекращается после приема этого сообщения.
Типичное приложение Windows строится по следующей общей схеме:
1) WinMain() — головной модуль программы;
2) инициализация внутренних переменных и прочие подготовительные операции;
3) RegisterClass() – регистрация локального класса;
4) CreateWindow() – создание главного окна приложения (если требуется, могут быть и другие классы и окна;
5) ShowWindow(), UpdateWindow() – отображение главного окна;
6) цикл обработки сообщений: GetMessage() – TranslateMessage() – DispatchMessage().
7) освобождение выделенных ресурсов, деинициализация внутренних переменных;
8) оконная процедура, прямых вызовов нет, так как предполагается «обратный вызов» (callback) со стороны системы.
Контрольные вопросы
1. Стандартная структура оконного приложения, его основные функции.
2. Окно. Функция создания нового окна и ее параметры.
3. Что такое оконный класс и каким образом он может быть создан.
4. Каким образом для окна можно указать собственные курсор и иконку.
5. За что отвечают функции UpdateWindow() и ShowWindow().
6. Сообщение Windows (Win Message), его основные параметры. Очередь сообщений.
7. Способы отправки сообщений другому окну.
8. Цикл обработки сообщений, функция GetMessage(), завершение цикла.
9. Функции TranslateMessage(), DispatchMessage().
10. Назначение, логика работы и параметры оконной процедуры.
11. Сообщение WM_PAINT. Функция вывода текста в окне, ее параметры.
12. Сообщение WM_COMMAND. Данные, передаваемые в wParam и lParam в данном сообщении.
11. Создание собственного пункта меню и обработка нажатия на него.
12. Функции GetWindowRect() и InvalidateRect().
13. Таймер. Создание и удаление таймера. Обработка сообщений таймера.
Задание
Написать программу, которая создает окно с собственными курсором и иконкой. В главное меню добавить подменю с двумя пунктами Start и Stop. При нажатии на пункт меню Start в окне должна появиться движущаяся по середине окна надпись. После этого выбор пункта меню Stop должно приостанавливать движение надписи, а пункта Start – возобновлять. Для реализации движения использовать обработчик сообщения WM_PAINT и таймер.
Лабораторная работа №7
Создание и использование элементов управления
Цели работы:
1) изучить основной набор элементов управления;
2) научиться создавать элементы управления;
3) научиться обрабатывать события элементов управления.