netlib.narod.ru | < Назад | Оглавление | Далее > |
Давайте исследуем приведенный код сверху донизу, уделяя внимание каждому встретившемуся вызову функции. Читая следующие подразделы, смотрите на приведенный выше код программы Hello World.
Первое, что мы должны сделать — включить заголовочный файл windows.h. Включив этот файл мы получаем доступ к объявлениям структур, типов и функций, необходимых для использования базовых элементов Win32 API.
#include <windows.h>
Вторая инструкция — это объявление глобальной переменной с типом HWND. Это сокращение обозначает «дескриптор окна» (handle to a window). Программируя для Windows вы часто будете пользоваться дескрипторами для ссылок на внутренние объекты Windows. В данном примере мы используем HWND чтобы ссылаться на главное окно приложения, управление которым осуществляют внутренние механизмы Windows. Мы должны сохранить дескриптор нашего окна потому что многие вызовы API требуют, чтобы им был передан дескриптор того окна, над которым они должны произвести действия. Например, функция UpdateWindow получает один параметр типа HWND, используемый для того, чтобы сообщить ей, какое окно должно быть обновлено. Если мы не передадим дескриптор, функция не будет знать какое окно ей обновлять.
HWND MainWindowHandle = 0;
В следующих трех строках находятся объявления функций. Говоря кратко, InitWindowsApp создает и инициализирует главное окно приложения, Run является оберткой для цикла обработки сообщений нашего приложения, а WndProc — это оконная процедура главного окна нашей программы. Мы подробно исследуем эти функции, когда дойдем до того места кода, где они вызываются.
bool InitWindowsApp(HINSTANCE instanceHandle, int show); int Run(); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
WinMain в мире Windows является аналогом функции main в обычном программировании на C++. Прототип WinMain выглядит так:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
hInstance — Дескриптор экземпляра данного приложения. Он предназначен для идентификации конкретного приложения и ссылок на него. Помните, что в Windows могут одновременно работать несколько приложений и поэтому необходим механизм, позволяющий идентифицировать каждое из них.
hPrevInstance — В 32-разрядных системах не используется и равно 0.
lpCmdLine — Строка с аргументами командной строки, указанными при запуске программы.
nCmdShow — Вариант отображения окна приложения. Наиболее часто используются варианты SW_SHOW (отображение окна указанных размеров в заданной позиции), SW_SHOWMAXIMIZED (отображение окна, развернутого на весь экран) и SW_SHOWMINIMIZED (отображение свернутого окна). Полный список вариантов отображения и соответствующих констант приведен в библиотеке MSDN.
Если работа функции WinMain завершается успешно, она должна вернуть член wParam сообщения WM_QUIT. Если работа функции завершена до входа в цикл обработки сообщений, она должна вернуть 0. Идентификатор WINAPI определен следующим образом:
#define WINAPI __stdcall
Он задает правила вызова функций и определяет, как функция будет обращаться к размещенным в стеке параметрам.
Из WinMain мы обращаемся к функции InitWindowsApp. Как уже сообщалось, эта функция выполняет действия, необходимые для инициализации программы. Давайте перейдем к ее коду и исследуем его. InitWindowsApp возвращает true или false — true, если инициализация успешно завершена, false если что-то не получилось. Как видно из кода WinMain, мы передаем функции InitWindowsApp копию дескриптора экземпляра нашего приложения и переменную, задающую режим отображения окна. Сама функция WinMain получает эти два значения в своих параметрах.
if(!InitWindowsApp(hInstance, nShowCmd))
Первая задача, с которой мы сталкиваемся при инициализации окна, — описать параметры окна и зарегистрировать его в Windows. Параметры окна задаются с помощью структуры данных WNDCLASS. Вот ее определение:
typedef struct _WNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS;
style — Задает стиль окна. В нашем примере мы используем комбинацию флагов CS_HREDRAW и CS_VREDRAW. Она означает, что окно будет перерисовываться при изменении его размеров по горизонтали или по вертикали. Полный список стилей с их описанием приведен в библиотеке MSDN.
wc.style = CS_HREDRAW | CS_VREDRAW;
lpfnWndProc — Указатель на оконную процедуру. Именно здесь устанавливается связь оконной процедуры с окном. Таким образом окна, созданные на основе одного и того же экземпляра структуры WNDCLASS будут совместно использовать одну и ту же оконную процедуру. Сама оконная процедура будет рассмотрена чуть позже в разделе «Оконная процедура».
wc.lpfnWndProc = WndProc;
cbClsExtra и cbWndExtra — Это дополнительные области памяти, которые вы можете использовать в своих собственных целях. В рассматриваемой программе дополнительные области памяти не нужны и поэтому обоим параметрам присваивается 0.
wc.cbClsExtra = 0; wc.cbWndExtra = 0;
hInstance — Поле для дескриптора экземпляра нашего приложения. Вспомните, что этот дескриптор был передан нам через функцию WinMain.
wc.hInstance = instanceHandle;
hIcon — Дескриптор значка, используемого для окон, создаваемых на основе данного класса. Существует несколько стандартных значков операционной системы и вы можете выбрать один из них. Более подробно этот вопрос рассматривается в MSDN.
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
hCursor — Тут, аналогично полю hIcon, вы задаете дескриптор курсора приложения, определяющий как будет выглядеть указатель мыши, когда он находится в клиентской области окна. Здесь также есть несколько встроенных типов курсоров. За дополнительной информацией обращайтесь к MSDN.
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
hbrBackground — Это поле определяет цвет фона клиентской области окна. В нашем примере мы вызываем функцию GetStockObject, которая возвращает дескриптор кисти указанного нами цвета. Описание других встроенных кистей можно найти в MSDN.
wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
lpszMenuName — Задает меню окна. В нашем приложении нет меню, поэтому значение данного поля равно 0.
wc.lpszMenuName = 0;
lpszClassName — Определяет имя создаваемого класса окна. Вы можете выбрать любое имя. В нашем приложении класс называется «Hello». Имя используется для идентификации структуры данных класса, чтобы мы могли обращаться к ней в дальнейшем.
wc.lpszClassName = "Hello";
После того, как мы описали параметры класса нашего окна, нам надо зарегистрировать его в Windows. Это выполняется с помощью функции RegisterClass, которая получает указатель на структуру WNDCLASS. В случае успешного завершения функция возвращает 0.
if(!::RegisterClass(&wc))
После того, как мы зарегистрировали в Windows переменную WNDCLASS, можно на основании содержащегося в ней описания класса создать новое окно. Для ссылки на структуру WNDCLASS, описывающую окно, которое мы хотим создать, используется заданное в члене lpszClassName имя класса. Для создания окна мы используем функцию CreateWindow, прототип которой выглядит следующим образом:
HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, LPVOID lpParam );
lpClassName — Имя класса (завершающаяся нулем строка), которое было указано в зарегистрированной структуре WNDCLASS, описывающей параметры окна, которое мы хотим создать. Передавайте имя класса, указанное в той структуре WNDCLASS, которую вы хотите использовать при создании окна.
lpWindowName — Имя (завершающаяся нулем строка), которое мы присваиваем нашему окну; это имя будет также отображаться в заголовке окна.
dwStyle — Описывает стиль создаваемого окна. Используемое в примере Hello World значение WS_OVERLAPPEDWINDOW является комбинацией флагов WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX. Имена флагов описывают соответствующие характеристики окна. Полный список стилей приведен в MSDN.
x — Позиция по горизонтали верхнего левого угла окна в экранной системе координат.
y — Позиция по вертикали верхнего левого угла окна в экранной системе координат.
nWidth — Ширина окна в пикселях.
nHeight — Высота окна в пикселях.
hWndParent — Дескриптор окна, которое является родителем данного. Создаваемое в примере окно не имеет взаимоотношений с другими окнами, поэтому данному параметру присваивается 0.
hMenu — Дескриптор меню. В приложении Hello World нет меню, поэтому данному параметру присваивается 0.
hInstance — Дескриптор экземпляра приложения с которым связано данное окно.
lpParam — указатель на определяемые пользователем данные.
Рис. 4. Экранное пространство |
Функция CreateWindow возвращает дескриптор созданного ею окна (значение типа HWND). Если создать окно не удалось, значение дескриптора равно нулю. Помните, что дескриптор — это способ сослаться на конкретное окно, управляемое Windows. Многие вызовы API требуют указания HWND, чтобы знать с каким окном производятся действия.
Последние два обращения к функциям API из функции InitWindowsApp предназначены для отображения окна. Сперва мы вызываем функцию ShowWindow и передаем ей дескриптор только что созданного окна, чтобы Windows знала, какое окно должно быть показано. Мы также передаем число, определяющее в каком виде будет показано окно (обычным, свернутым, развернутым на весь экран и т.д.). Здесь лучше всего указать значение nShowCmd, которое было передано нам в одном из аргументов WinMain. Конечно, вы можете жестко задать значение в коде, но это не рекомендуется. После отображения окна мы должны обновить его. Это делает функция UpdateWindow; она получает один аргумент, являющийся дескриптором обновляемого окна.
::ShowWindow(MainWindowHandle, show); ::UpdateWindow(MainWindowHandle);
Если в функции InitWindowsApp были выполнены все описанные выше действия, значит инициализация завершена; мы возвращаем true, чтобы сообщить об успешном завершении функции.
Успешно завершив инициализацию, мы переходим к сердцу программы — циклу обработки сообщений. В программе Hello World, мы заключили цикл обработки сообщений в функцию с именем Run.
int Run() { MSG msg; ::ZeroMemory(&msg, sizeof(MSG)); while(::GetMessage(&msg, 0, 0, 0) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } return msg.wParam; }
Начинается функция Run с объявления переменной msg типа MSG, являющейся структурой данных, представляющей сообщения Windows. Ее определение выглядит следующим образом:
typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG;
hwnd — Идентифицирует окно, которому предназначено сообщение.
message — Одна из предопределенных констант, идентифицирующих сообщение (например, WM_QUIT).
wParam — Дополнительная информация о сообщении. Зависит от конкретного сообщения.
lParam — Дополнительная информация о сообщении. Зависит от конкретного сообщения.
time — Время, когда сообщение было помещено в очередь.
pt — Координаты указателя мыши (x, y) в экранном пространстве в тот момент, когда сообщение было помещено в очередь.
Затем мы входим в цикл обработки сообщений. Функция GetMessage будет возвращать true до тех пор, пока из очереди не будет извлечено сообщение WM_QUIT; следовательно цикл будет выполняться, пока не будет получено сообщение WM_QUIT. Функция GetMessage извлекает сообщение из очереди и заполняет члены нашей структуры MSG. Если GetMessage возвращает true, будут вызваны еще две функции: TranslateMessage и DispatchMessage. TranslateMessage выполняет трансляцию сообщений Windows, в частности преобразование виртуальных кодов клавиш в коды символов. DispatchMessage направляет сообщение соответствующей оконной процедуре.
Мы уже упоминали ранее, что оконная процедура — это то место, где мы указываем код, который должен выполняться при получении окном приложения каких-либо сообщений. В программе Hello World оконная процедура называется WndProc. Ее прототип выглядит так:
LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
Функция возвращает значение типа LRESULT (в действительности это целое число типа long), сообщающее успешно или нет завершена работа функции. Идентификатор CALLBACK сообщает, что это функция обратного вызова (callback function). Это означает, что вызов данной функции осуществляют внутренние механизмы Windows. Посмотрите на исходный код приложения Hello World: вы нигде не найдете явно указанного нами вызова оконной процедуры; Windows вызывает ее за нас, когда окну требуется обработать поступившее сообщение.
В сигнатуре оконной процедуры указано четыре параметра:
hwnd — Идентифицирует окно, которому предназначено сообщение.
uMsg — Предопределенная константа, идентифицирующая конкретное сообщение. Например, сообщению о выходе из приложения соответствует константа WM_QUIT. Префикс WM означает «оконное сообщение» (Window Message). Существуют сотни предопределенных оконных сообщений, описание которых можно найти в MSDN.
wParam — Дополнительная информация о сообщении. Зависит от конкретного сообщения.
lParam — Дополнительная информация о сообщении. Зависит от конкретного сообщения.
Наша оконная процедура обрабатывает три сообщения: WM_LBUTTONDOWN, WM_KEYDOWN и WM_DESTROY. Сообщение WM_LBUTTONDOWN посылается когда пользователь нажимает левую кнопку мыши и при этом указатель мыши находится внутри клиентской области окна. Сообщение WM_KEYDOWN отправляется если нажата какая-нибудь клавиша на клавиатуре. Сообщение WM_DESTROY будет получено в том случае, если окно должно быть уничтожено. Наш код очень простой; получив сообщение WM_LBUTTONDOWN мы выводим окно сообщений с текстом «Hello, World»:
case WM_LBUTTONDOWN: ::MessageBox(0, "Hello, World", "Hello", MB_OK); return 0;
Когда окно получает сообщение WM_KEYDOWN мы проверяем какая именно клавиша была нажата. Параметр wParam, передаваемый в оконную процедуру, содержит виртуальный код клавиши (virtual key kode), указывающий какая клавиша нажата. Вы можете думать о виртуальном коде клавиши как об идентификаторе конкретной клавиши на клавиатуре. В заголовочном файле Windows содержится список виртуальных кодов клавиш, которые мы можем использовать при проверке того, какая именно клавиша была нажата (например, чтобы проверить была ли нажата клавиша Esc, мы используем константу виртуального кода клавиши VK_ESCAPE).
case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(MainWindowHandle); return 0;
Когда наше окно должно быть уничтожено, мы отправляем сообщение о выходе из приложения (которое прерывает работу цикла сообщений).
case WM_DESTROY: ::PostQuitMessage(0); return 0;
В самом конце оконной процедуры мы вызываем функцию DefWindowProc. Это стандартная оконная процедура. В приложении Hello World мы обрабатываем только три сообщения; стандартная оконная процедура определяет поведение при получении всех остальных сообщений, которые мы получаем, но решили не обрабатывать самостоятельно. Например, окно приложения Hello World может быть свернуто, развернуто на весь экран, закрыто, может быть изменен его размер. Вся эта функциональность предоставляется нам стандартной оконной процедурой, и нам не надо писать свои обработчики сообщений, реализующие эти функции. Обратите внимание, что функция DefWindowProc является частью Win32 API.
Последняя функция API, которую мы сейчас рассмотрим — это функция MessageBox. Она очень полезна в тех случаях, когда надо показать какую-то информацию пользователю и получить от него ответ. Прототип функции MessageBox выглядит так:
int MessageBox( HWND hWnd, // Дескриптор владельца окна, // можно указать null LPCTSTR lpText, // Текст, выводимый в окне LPCTSTR lpCaption, // Текст, выводимый в заголовке окна UINT uType // Стиль окна сообщения. );
Значение, возвращаемое функцией MessageBox зависит от типа окна сообщения. Стили окон сообщений и соответствующие им возвращаемые значения перечислены в MSDN.
netlib.narod.ru | < Назад | Оглавление | Далее > |