Определение размера окна
Программы MS-DOS работали с экраном, который имел фиксированные размеры. В зависимости от видеорежима они использовали либо текстовый экран размером 80 х 25 символов, либо графический экран размером от 640х200 для видеоконтроллеров CGA, до 1024 х 768 или больше для SVGA.
Приложение Windows, как правило, не знает заранее размеров отведенного ей окна (или окон, если их несколько). К тому же в любой момент времени вы можете изменить эти размеры при помощи мыши. Разумеется, приложение может запретить изменять размер окна мышью (указав стиль окна без толстой рамки), но такой подход нельзя приветствовать. Поэтому приложение Windows должно уметь определять размеры своих окон.
Существует два принципиально разных метода определения размера окна. Первый метод основан на том факте, что, когда вы изменяете размер окна мышью, функция окна получает сообщение с кодом WM_SIZE, параметры которого передают функции новые размеры окна. Второй метод заключается в вызове функций, возвращающих размеры окна по его идентификатору. Например, для определения размеров окна можно воспользоваться функцией GetClientRect. Мы приведем исходные тексты приложения WSIZE, демонстрирующего использование обоих методов.
Приложение WSIZE состоит из трех файлов. Первый файл с именем wsize.cpp (листинг 4.9) создает главное окно приложения и запускает цикл обработки сообщений. Файл wndproc.cpp (листинг 4.10) содержит исходный текст функции окна, выполняющий всю интересную для нас работу по определению и отображению текущих размеров главного окна приложения. И наконец, файл wsize.def (листинг 4.11) является файлом определения модуля для нашего приложения.
Листинг 4.9. Файл wsize\wsize.cpp
// ---------------------------------------- // Определение размеров окна // ----------------------------------------
#define STRICT #include <windows.h> #include <mem.h>
BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
char const szClassName[] = "WSIZEAppClass"; char const szWindowTitle[] = "WSIZE Application";
// ===================================== // Функция WinMain // ===================================== #pragma argsused
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения
// Выполняем инициализацию приложения if(!InitApp(hInstance)) return FALSE;
// После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE;
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); } return msg.wParam; }
// ===================================== // Функция InitApp // Вызывается из функции WinMain для // инициализации приложения. // Выполняет регистрацию класса окна // =====================================
BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна
memset(&wc, 0, sizeof(wc)); wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szClassName;
aWndClass = RegisterClass(&wc); return (aWndClass != 0); }
Файл wsize.cpp не имеет никаких дополнительных особенностей по сравнению с аналогичными файлами предыдущих приложений.
Поэтому мы не будем его описывать, а перейдем сразу к функции окна (листинг 4.10).
Листинг 4.10. Файл wsize\wndproc.cpp
// ===================================== // Функция WndProc // Функция выполняет обработку сообщений // главного окна приложения // =====================================
#define STRICT #include <windows.h> #include <stdio.h>
LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования
static WPARAM fwSizeType; static WORD nWidth; static WORD nHeight;
switch (msg) { case WM_PAINT: { char buf[80];
hdc = BeginPaint(hwnd, &ps);
sprintf(buf, "%4.4dx%4.4d (%1.1d)", nWidth, nHeight, fwSizeType);
// Выводим размеры окна TextOut(hdc, 0, 0, buf, 13);
EndPaint(hwnd, &ps); return 0; }
case WM_SIZE: { // Способ изменения размера окна fwSizeType = wParam;
// Ширина внутренней области окна nWidth = LOWORD(lParam);
// Высота внутренней области окна nHeight = HIWORD(lParam);
// Отмечаем все окно как требующее // обновления InvalidateRect(hwnd, NULL, TRUE);
// Посылаем себе сообщение WM_PAINT UpdateWindow(hwnd); return 0; }
case WM_LBUTTONDOWN: { RECT rcWndSize; char buf[80];
hdc = GetDC(hwnd);
// Определяем размеры внутренней // области окна GetClientRect(hwnd, &rcWndSize);
sprintf(buf, "%4.4dx%4.4d", rcWndSize.right, rcWndSize.bottom);
// Выводим размеры внутренней области окна TextOut(hdc, 0, 20, buf, 9);
ReleaseDC(hwnd, hdc); return 0; }
case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); }
В области данных функции окна определены статические переменные, предназначенные для хранения размера окна и способа изменения этих размеров (о способах изменения размеров окна мы расскажем немного позже):
static WPARAM fwSizeType; static WORD nWidth; static WORD nHeight;
При обработке сообщения WM_PAINT содержимое этих переменных выводится в левом верхнем углу окна.
Вам может показаться странным, что перед выводом содержимого переменных по сообщению WM_PAINT сами переменные не инициализируются. Это не совсем так.
Рассмотрим последовательность действий при создании и отображении окна и перечислим сообщения, получаемые функцией окна на разных стадиях создания.
Окно создается функцией CreateWindow. При этом функция окна получает следующие сообщения:
Код сообщения | Описание сообщения |
WM_GETMINMAXINFO | При обработке этого сообщения приложение может изменить заданные по умолчанию размеры, расположение, а также минимальные и максимальные размеры окна, которые могут быть получены при изменении этих размеров с помощью мыши |
WM_NCCREATE | Сообщение оповещает функцию окна о начале процесса создания окна |
WM_NCCALCSIZE | При обработке этого сообщения приложение может определить размеры внутренней области окна (client area) |
WM_CREATE | Это сообщение оповещает приложение о том, что окно создано и что приложение может выполнять дополнительные действия по инициализации данных, связанных с окном |
Для того, чтобы сделать окно видимым, наше приложение вызывает функцию ShowWindow. Эта функция посылает в функцию окна целый каскад сообщений:
Код сообщения | Описание сообщения |
WM_SHOWWINDOW | Сообщение оповещает функцию окна о том, что окно будет отображено или скрыто |
WM_WINDOWPOSCHANGING | Это сообщение посылается окну при изменении его размеров, расположения на экране или взаимного расположения вдоль оси Z (то есть когда окно перекрывается другими окнами или само перекрывает другие окна; воображаемая ось Z направлена перпендикулярно к плоскости экрана, окна могут перекрывать друг друга, при этом считается, что каждое окно имеет свою "Z-координату") |
WM_ACTIVATEAPP | Это сообщение посылается окну верхнего уровня (то есть главному окну приложения) и говорит о создании в Windows нового приложения (нового процесса) |
WM_NCACTIVATE | Сообщение посылается окну, которое должно перерисовать свою внешнюю область (non client area), включающую заголовок, рамку, кнопки изменения размера и т. п.) |
WM_GETTEXT | Копирование текста заголовка окна |
WM_ACTIVATE | Сообщение посылается окну, которое изменяет свое состояние из неактивного в активное или наоборот |
WM_SETFOCUS | Окно получает фокус ввода (все сообщения от клавиатуры направляются в это окно) |
WM_NCPAINT | Сообщение посылается окну, требующему перерисовки рамки. Приложение может перехватить это сообщение и нарисовать вокруг окна собственную рамку |
WM_GETTEXT | Копирование текста заголовка окна |
WM_ERASEBKGND | Сообщение посылается окну при стирании фона его внутренней области |
WM_WINDOWPOSCHANGED | Это сообщение посылается окну, изменившему свои размеры, расположение на экране или расположение вдоль оси Z |
WM_SIZE | Сообщение посылается окну после изменения размеров окна |
WM_MOVE | Сообщение посылается окну после его перемещения |
Все эти сообщения, кроме двух последних, обычно передаются функции DefWindowProc. Если функция окна должна реагировать на изменение размеров или расположения окна, она должна обрабатывать сообщения WM_SIZE и WM_MOVE.
При вызове функции UpdateWindow функция окна получает единственное сообщение WM_PAINT (только в том случае, если окно содержит области, помеченные для обновления).
Так как при отображении окна функцией ShowWindow функция окна получает в числе прочих сообщение WM_SIZE, и наше приложение WSIZE его обрабатывает, мы можем инициализировать переменные fwSizeType, nWidth и nHeight до прихода сообщения WM_PAINT, что мы и делаем в нашем обработчике сообщения WM_SIZE:
case WM_SIZE: { fwSizeType = wParam; nWidth = LOWORD(lParam); nHeight = HIWORD(lParam); InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd); return 0; }
Как мы говорили раньше, вместе с сообщением обычно приходит дополнительная информация, которая передается функции окна через параметры wParam и lParam. До сих пор мы игнорировали эту информацию. Теперь она нам нужна, так как новые размеры окна передаются функции окна вместе с сообщением WM_SIZE именно через параметры wParam и lParam.
В операционной системе Windows версии 3.1 тип WPARAM соответствует 16-разрядному слову, а тип LPARAM - 32-разрядному двойному слову. Однако не следует думать, что так будет и в других версиях Windows. Операционная система Windows NT, например, использует для типа WPARAM 32-разрядное слово.
Через параметр lParam передается два значения. В операционной системе Windows версии 3.1 эти значения соответствуют младшему и старшему слову lParam. Для обеспечения совместимости со следующими версиями Windows не следует самостоятельно извлекать эти два значения. Нужно пользоваться специально предназначенными для этого макросами LOWORD и HIWORD. Эти макросы определены в файле windows.h:
#define LOWORD(l) ((WORD)(l)) #define HIWORD(l) ((WORD)((DWORD)(l) >> 16))
Параметры сообщения WM_SIZE описывают новый размер окна и способ, которым окно изменило свой размер:
Параметр | Описание |
wParam | Способ, при помощи которого окно изменило свой размер |
LOWORD(lParam) | Новая ширина окна |
HIWORD(lParam) | Новая высота окна |
Параметр | Значение | Описание |
SIZE_RESTORED | 0 | Окно изменило свои размеры, но оно не было максимизировано или минимизировано |
SIZE_MINIMIZED | 1 | Размеры окна уменьшены до предела (окно минимизировано) |
SIZE_MAXIMIZED | 2 | Размеры окна увеличены до предела (окно максимизировано) |
SIZE_MAXSHOW | 3 | Сообщение WM_SIZE с этим значением парамера wParam посылается всем временным (pop-up) окнам, когда восстанавливается размер других окон |
SIZE_MAXHIDE | 4 | Сообщение WM_SIZE с этим значением парамера wParam посылается всем временным (pop-up) окнам, когда размер других окон увеличивается до предела |
InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd);
Функция InvalidateRect добавляет прямоугольную область, заданную в качестве параметра, к областям окна, требующим обновления (перерисовки). Эта функция имеет следующий прототип:
void WINAPI InvalidateRect(HWND, const RECT FAR*, BOOL);
Первый параметр указывает идентификатор окна, для которого нужно выполнить операцию добавления области, требующей обновления.
Второй параметр - дальний указатель на структуру типа RECT, описывающую координаты области. Если в качестве этого параметра использовать значение NULL (как в нашем примере), вся внутренняя область окна (client area) будет объявлена как требующая обновления.
Третий параметр указывает, нужно ли при обновлении стирать фон окна. Если указать значение TRUE, фон окна будет стерт. Для того чтобы оставить фон окна без изменения, укажите в качестве третьего параметра значение FALSE.
Функция UpdateWindow вызывается в нашем приложении после того, как вся внутренняя область окна объявлена как требующая обновления, поэтому она передает функции окна сообщение WM_PAINT. Обработчик этого сообщения выводит в левом верхнем углу окна новые координаты окна и код способа, которым были изменены размеры окна.
Функция окна нашего приложения обрабатывает сообщение WM_LBUTTONDOWN. Это сообщение передается функции окна, когда вы располагаете курсор мыши над окном и нажимаете левую клавишу мыши. Обработчик этого сообщения с помощью функции GetClientRect определяет текущие размеры окна и выводит их в левом верхнем углу окна под строкой, выведенной обработчиком сообщения WM_PAINT:
case WM_LBUTTONDOWN: { RECT rcWndSize; char buf[80];
hdc = GetDC(hwnd); GetClientRect(hwnd, &rcWndSize); sprintf(buf, "%4.4dx%4.4d", rcWndSize.right, rcWndSize.bottom); TextOut(hdc, 0, 20, buf, 9); ReleaseDC(hwnd, hdc); return 0; }
Функция GetClientRect предназначена для определения координат внутренней области окна и имеет следующий прототип:
void WINAPI GetClientRect(HWND hwnd, RECT FAR* lprc);
Первый параметр функции (hwnd) определяет идентификатор окна, для которого требуется определить координаты внутренней области.
Второй параметр (lprc) является дальним указателем на структуру типа RECT, в которую записываются координаты внутренней области окна. Эти координаты вычисляются относительно левого верхнего угла внутренней области окна, поэтому в полях left и top структуры RECT всегда записываются нулевые значения. Поля right и bottom содержат соответственно ширину и высоту внутренней области окна.
На рис. 4.1 показан внешний вид главного окна приложения WSIZE после того, как в нем щелкнули левой клавишей мыши.
Рис. 4.1. Главное окно приложения WSIZE
Из рисунка видно, что оба способа определения размера окна (при обработке сообщения WM_SIZE и при помощи функции GetClientRect) дали одинаковые результаты.
Файл определения модуля, который был использован при создании приложения WSIZE, не имеет никаких особенностей и приведен в листинге 4.11.
Листинг 4.11. Файл wsize\wsize.def
; ============================= ; Файл определения модуля ; ============================= NAME WSIZE DESCRIPTION 'Приложение WSIZE, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple