netlib.narod.ru | < Назад | Оглавление | Далее > |
На этом я заканчиваю обсуждение теории, лежащей в основе программирования для Windows. Пришло время написать вашу первую программу для Windows. Во всех, рассматриваемых в этой книге примерах, я использую компилятор Microsoft Visual C++ 6.0. Если у вас еще нет этой программы, я настоятельно рекомендую пойти и приобрести ее.
Давайте продолжим. Запустите вашу копию Visual C++ 6.0. Вы должны увидеть экран, аналогичный представленному на рис. 2.2.
Рис. 2.2. Интерфейс Visual C++ 6.0
В интерфейсе, представленном на рис. 2.2, нет ничего особенного. Основные элементы управления сосредоточены в верхнем меню: File, Edit, View, Insert и т.д. Все, что вы делаете в Visual C++, должно быть связано с проектом. Проект — это верхний уровень иерархии в среде разработки.
Лучший способ обучения — практика, так что давайте создадим новый проект, для чего щелкнем по меню File и выберем пункт New. Вам будет предоставлено множество вариантов, изображенных на рис. 2.3.
Рис. 2.3. Диалоговое окно создания нового проекта в Visual C++ |
Диалоговое окно, которое вы видите, используется для указания типа создаваемого проекта. Как видно Visual C++ используется для создания всех типов программ, включая элементы управления ActiveX, COM-объекты, надстройки DevStudio и конечно же приложения Windows. Нас интересует тип приложений, который называется Win32 Application. Этот тип используется при создании программ для Windows. Выделите в списке пункт Win32 Application чтобы выбрать этот тип.
Перед тем, как двинуться дальше, вы должны сообщить Visual C++ имя вашего проекта и где он будет храниться. Эта информация вводится в текстовые поля Project name и Location. Для нашего примера в качестве имени проекта укажите CreateWindow.
После того, как вы введете требуемую информацию, щелкните по кнопке OK. Вам будет предложено диалоговое окно, показанное на рис. 2.4.
Рис. 2.4. Диалоговое окно выбора типа Windows-приложения |
Диалоговое окно, представленное на рис. 2.4, предлагает три варианта:
Первый вариант, пустой проект, используется наиболее часто. Его выбор приводит к созданию пустого проекта в который не включены никакие объекты. Фактически создается чистый холст для последующей работы.
Второй вариант, простое приложение Win32, используется реже, поскольку все, что он делает — это создает программу, которая ничего не делает. В этом случае для вас создаются исходные файлы, но они делают не слишком много.
Третий вариант, типичное приложение «Hello World!», делает больше, чем два предыдущих. Он создает законченую программу для Windows, которая выводит текст «Hello World!». Я не использую этот вариант слишком часто, поскольку создаваемый код на мой вкус выглядит слишком загроможденным.
Выберите первый переключатель и щелкните по кнопке Finish, чтобы открыть диалоговое окно, изображенное на рис. 2.5.
Рис. 2.5. Диалоговое окно подтверждения выбранных параметров |
Представленное диалоговое окно не содержит ничего необычного. Оно выводится только для того, чтобы вы могли удостовериться, что будет создан проект желаемого типа. Для продолжения работы щелкните по кнопке OK.
Итак, вот ваш проект во всей славе! Теперь, после того как проект создан, настало время подробнее поговорить о нем. Взгляните на рис. 2.6.
Рис. 2.6. Пустое пространство проекта для вашей новой программы
Взгляните на рабочее пространство — область в левой части, содержащую имя вашего проекта со знаком «+» рядом с ним. Перейдите на вкладку FileView и разверните дерево, щелкнув по знаку «+». Вы увидите три подпапки в дереве файлов проекта:
Надеюсь, вы знаете как программировать на С или С++ и для чего предназначены исходные и заголовочные файлы. Если нет, я рекомендую вам приобрести книгу по программированию на С++ и изучить ее перед тем, как продолжать чтение. Без этих знаний вам придется очень трудно.
Если вы умеете программировать, но являетесь новичком в программировании для Windows, вам может быть непонятно назначение папки Resource Files.
В программировании для Windows ресурсами называются элементы, используемые для расширения или добавляемые к приложению. Примерами ресурсов могут служить значки, растровые изображения, звуковые файлы и текстовые строки. Прелесть ресурсов в том, что они компилируются в вашу программу. Вам не надо беспокоиться об их установке на компьютер пользователя, ведь они являются частью исполняемого файла.
Я не хочу потеряться среди связанных с ресурсами тем, так что давайте отправимся дальше. В вашем проекте пока нет файлов, поэтому в папках ничего не перечислено. Давайте исправим это, добавив в проект файл с исходным кодом на С++.
Раскройте меню Project и выберите пункт Project | Add To Project | New. В результате на экран будет выведено диалоговое окно, изображенное на рис. 2.7.
Рис. 2.7. Диалоговое окно для выбора типа добавляемого к проекту файла |
Фактически это то же самое диалоговое окно, что и изображенное на рис. 2.3, просто в нем выбрана другая вкладка. Вместо того, чтобы выбирать тип создаваемого проекта, сейчас мы будем выбирать тип добавляемого файла. Больше всего нас должны интересовать типы C/C++ Header File и C++ Source File. Перед тем, как продолжить, выберите тип C++ Source File.
Мы уже на финишной прямой, но еще не закончили. Перед тем, как щелкнуть по кнопке OK, вы должны задать имя создаваемого файла. Введите в качестве имени файла CreateWindow и щелкните по кнопке OK.
Теперь, когда файл создан и добавлен к проекту, вы можете видеть его в окне рабочего пространства. Взгляните на рис. 2.8.
Рис. 2.8. Рабочее пространство с добавленным файлом
Я раскрыл папку Source Files, чтобы показать только что созданный файл CreateWindow.cpp. Когда вы дважды щелкаете по любому файлу, представленному в окне рабочего пространства, он загружается и выводится в области редактирования. На рис. 2.8 загружен файл CreateWindow.cpp. В данный момент он пуст и ждет, что вы добавите в него какой-нибудь текст.
Теперь, когда у вас есть чистый исходный файл, призывающий вас, настало время написать действительный код для Windows. Не волнуйтесь слишком много, я проведу вас через каждый шаг. Взгляните на приведенный ниже листинг. В нем полностью приведена рабочая программа для Windows.
// Стандартный включаемый файл Windows #include <windows.h> // Прототип функции обратного вызова для обработки сообщений LRESULT CALLBACK fnMessageProcessor (HWND, UINT, WPARAM, LPARAM); // Функция вызывается автоматически, когда программа запускается int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hWnd; MSG msg; WNDCLASSEX wndclass; // Настройка класса окна wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = fnMessageProcessor; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "Window Class"; // Имя класса wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // Регистрация класса окна if(RegisterClassEx(&wndclass) == 0) { // Сбой программы, выход exit(1); } // Создание окна hWnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, "Window Class", // Имя класса "Create Window Example", // Текст заголовка WS_OVERLAPPEDWINDOW, 0, 0, 320, 200, NULL, NULL, hInstance, NULL); // Отображение окна ShowWindow(hWnd, iCmdShow); // Обработка сообщений, пока программа не будет прервана while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (msg.wParam); } // Функция обратного вызова для обработки сообщений // (НЕОБХОДИМА ВСЕМ ПРОГРАММАМ ДЛЯ WINDOWS) LRESULT CALLBACK fnMessageProcessor (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch(iMsg) { // Вызывается, когда впервые создается окно case WM_CREATE: return(0); // Вызывается, когда окно обновляется case WM_PAINT: return(0); // Вызывается, когда пользователь закрывает окно case WM_DESTROY: PostQuitMessage(0); return(0); default: return DefWindowProc(hWnd, iMsg, wParam, lParam); } }
В нескольких первых строках кода указываются включаемые заголовочные файлы и приводятся прототипы функций. В программировании для Windows необходим только один заголовочный файл с именем windows.h.
Для нашего примера требуется единственный прототип функции для обработчика сообщений. Любая программа для Windows должна содержать функцию, которая будет вызываться для обработки сообщений и должна соответствовать следующему прототипу:
LRESULT CALLBACK fnMessageProcessor (HWND, UINT, WPARAM, LPARAM);
Имя функции может отличаться, но параметры должны оставаться неприкосновенными. Это вызвано тем, что Windows автоматически вызываеет данную функцию и не сможет работать правильно, если вы измените параметры.
Первый элемент, упоминаемый в функции WinMain() - это объект, используемый для создания окна. Объект является структурой типа WNDCLASSEX и очень важен для создания окна вашей программы.
Если в прошлом вы уже занимались программированием для Windows, то могли использовать структуру WNDCLASS. Между ней и объектом WNDCLASSEX есть только пара отличий. Во-первых добавлен апраметр, задающий размер структуры в байтах. Во-вторых, добавлен параметр задающий дескриптор маленького значка для окна.
Ниже приведен прототип структуры WNDCLASSEX:
typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX;
Нравится ли вам встеча со сложной структурой, назначение членов которой надо запомнить? Нет! Хорошо хоть Microsoft позаботилась, чтобы имена этих переменных было просто читать. Первая переменная типа UINT называется cbSize. Она используется для указания размера структуры данных. Обычно для инициализации этого члена структуры используется выражение sizeof(WNDCLASSEX). Инициализацию этого члена данных вы можете увидеть взглянув на приведенный выше листинг.
Второй элемент структуры также имеет тип UINT и называется style. Как указывает имя, член style используется для задания стиля создаваемого окна. Удобство данного члена данных в том, что вы можете указывать комбинации одних стилей с другими, используя несколько флагов, объединеных поразрядной операцией ИЛИ (|). Доступные стили перечислены в таблице 2.2.
Таблица 2.2. Стили окон | |
Значение | Действие |
CS_BYTEALIGNCLIENT | Выравнивает клиентскую область окна (область с содержимым) по границе байта в направлении оси X. Оказывает влияние на позицию окна по горизонтали и на его ширину. Лично я никогда не использовал этот стиль. |
CS_BYTEALIGNWINDOW | Выравнивает окно по границе байта в направлении оси X. Оказывает влияние на границу окна по горизонтали и на его ширину. Этот стиль я также не использую. |
CS_CLASSDC | Размещает в структуре класса единый контекст устройства для использования всеми окнами. Я пока не рассказывал о контекстах устройств, но сделаю это позже, так что нет причин для волнения. В Windows можно создать несколько классов окон одного и того же типа в разных потоках. Так как у вас может быть несколько копий класса, несколько потоков могут попытаться использовать контекст устройства одновременно. Это вызовет блокировку всех потоков, кроме одного, пока этот поток не завершит работу с контекстом. |
CS_DBLCLKS | Очень простой стиль. Когда он указан, Windows будет посылать вашему окну сообщение о двойном щелчке каждый раз, когда пользователь выполняет двойной щелчок кнопкой мыши в пределах области окна. Это может показаться глупым, но многие приложения запоминают время каждого щелчка кнопки мыши, чтобы определить был ли выполнен двойной щелчок. Я, чтобы добиться того же, просто использую данный стиль. |
CS_GLOBALCLASS | Этот стиль разрешает создание глобального класса окна. Чтобы получить дополнительную информацию, обратитесь к документации, поставляемой с Microsoft Visual C++. При разработке игр нет необходимости использовать данный стиль. |
CS_HREDRAW | Этот стиль заставляет перерисовывать все окно в случае изменения его ширины. |
CS_NOCLOSE | Запрещает выполнение закрытия окна через системное меню. |
CS_OWNDC | Выделяет уникальный контекст устройства для каждого окна, созданного с использованием класса. |
CS_PARENTDC | Устанавливается, когда дочернее окно использует ту же самую область отсечения, что и родительское. Это позволяет дочернему окну рисовать на родительском. Это не означает, что потомок использует контекст устройства родителя. В действительности потомок получает свой собственный контекст устройства из системного пула. Единственное, что делает этот флаг — увеличение производительности приложения. |
CS_SAVEBITS | Сохраняет в памяти растровое изображение находящейся под окном области экрана. В дальнейшем при перемещении окна скрытая область копируется на экран. Это предотвращает отправку сообщения WM_PAINT всем окнам, которые были скрыты данным. |
CS_VREDRAW | Заставляет перерисовывать все содержимое окна в случае изменения высоты окна. |
Третий член структуры имеет тип WNDPROC, является указателем на функцию и называется lpfnWndProc. Помещаемый сюда указатель должен указывать на функцию обработки сообщений Windows, которую окно использует чтобы принимать сообщения. Это очень важно, и функция, на которую ссылаются здесь должна полностью соответствовать прототипу, приведенному в моем коде. Взгляните на рис. 2.9, чтобы увидеть взаимоотношения между классом и обработчиком сообщений.
Рис. 2.9. Взаимоотношения между классом окна и обработчиком сообщений
Четвертый член структуры относится к типу int и называется cbClsExtra. Это целое число задает количество байт, которые будут выделены сразу за структурой данных класса окна. Я не имею ни малейшего представления, для чего это может быть нужно, поскольку во всех примерах, которые я видел, это значение устанавливалось равным 0. Я рекомендую вам поступать точно так же. Если желаете, можете просто игнорировать этот член данных, поскольку система сама по умолчанию присваивает ему нулевое значение.
Пятый элемент также относится к типу int и называется cbWndExtra. Это целое число задает количество байтов, которые будут выделены сразу за экземпляром окна. Работа с ним аналогична обращению с предыдущим членом структуры, и система также по умолчанию присваивает ему нулевое значение.
Шестой член структуры имеет тип HANDLE и называется hInstance. Дескриптор, который вы задаете здесь, является дескриптором экземпляра к которому относится окнонная процедура класса. В большинстве случаев можно задать значение дескриптора hInstance, получаемого функцией WinMain() в одном из параметров.
Седьмой параметр называется hIcon и имеет тип HICON. Тип HICON это ни что иное, как замаскированный тип HANDLE, поэтому он не должен смущать вас. Данный дескриптор указывает на класс значка, используемого окном. Класс значка в действительности является ресурсом значка. Не присваивайте этой переменной значение NULL, поскольку если сделать так, то программа будет перерисовывать изображение значка, при каждом свертывании окна.
Если вы еще раз взглянете на код, то увидите, что для инициализации седьмого параметра я использую вызов функции LoadIcon().
Функция LoadIcon() загружает ресурс значка из исполняемой программы. Хотя ресурсы компилируются внутрь вашей программы для Windows, вам все равно необходимо загружать их, поэтому и требуется вызов данной функции. Вот как выглядит ее прототип:
HICON LoadIcon( HINSTANCE hInstance, LPCTSTR lpIconName );
К счастью, у этой функции всего два прарметра — HINSTANCE и LPCTSTR. Первый параметр с именем hInstance, содержит дескриптор экземпляра модуля чей исполняемый файл содержит значок, который вы желаете использовать. В моих примерах программ я присваиваю этому параметру значение NULL. Делая так вы разрешите использование встроенных значов Windows, которые часто также называют стандартными значками.
Второй параметр является указателем на строку, содержащую имя загружаемого значка. Взглянув на разбираемый пример, вы увидите, что для инициализации данного параметра я использую константу IDI_APPLICATION. Ее значение соответствует значку, используемому по умолчанию для приложений, который вы могли видеть во многих программах для Windows. Остальные стандартные значения перечислены в таблице 2.3.
Таблица 2.3. Константы для стандартных значков | |
Значение | Описание |
IDI_APPLICATION | Значок, используемый по умолчанию для приложений. Он применяется и в рассматриваемом примере. В большинстве случаев вы можете использовать это значение, если только не хотите, чтобы у вашего приложения был нестандартный значок. |
IDI_ASTERISK | Данное значение создает для вашей программы значок в виде небольшого овала с буквой «i» внутри. |
IDI_ERROR | Красный круг с крестом внутри. |
IDI_EXCLAMATION | Желтый треугольник с восклицательным знаком внутри. |
IDI_HAND | Я понятия не имею, для чего нужны эти дубли, но этот значок выглядит точно так же, как IDI_ERROR. |
IDI_INFORMATION | Еще один дубль. Это значение задает небольшой овальный значок, аналогичный задаваемому значением IDI_ASTERISK. |
IDI_QUESTION | Использование этого значения даст вашему приложению значок с вопросительным знаком. |
IDI_WARNING | О, опять дублирование. На этот раз значок выглядит так же как для значения IDI_EXCLAMATION. |
IDI_WINLOGO | Если использовать эту константу, значок вашего приложения будет выглядеть как небольшой аккуратный логотип Windows. |
Вот и все, что можно сказать о функции LoadIcon(). Теперь вернемся к разбираемой программе. Ох, подождите, прежде чем продолжить, взгляните на рис. 2.10, где изображены некоторые стандартные значки.
Рис. 2.10. Некоторые стандартные значки Windows. ©2002 Microsoft, All Rights Reserved.
Восьмой член структуры данных WNDCLASSEX очень похож на седьмой, за исключением того, что он задает используемый окном курсор. Его тип HCURSOR, а имя — hCursor. Тип HCURSOR это еще один замаскированный дескриптор. Обычно здесь указывается значение дескриптора класса курсора, который будет использован в вашей программе для ее собственного курсора. Но вместо того, чтобы создавать нестандартный курсор, я воспользуюсь функцией LoadCursor().
Функция LoadCursor() похожа на функцию LoadIcon() за исключением того, что она загружает ресурсы курсора, а не ресурсы значка. Вот ее прототип:
HCURSOR LoadCursor( HINSTANCE hInstance, LPCTSTR lpCursorName );
Первый параметр называется hInstance, и содержит дескриптор экземпляра модуля, чей исполняемый файл содержит курсор, который вы собираетесь использовать. В своем примере я присваиваю данному параметру значение NULL. Это позволяет использовать встроенные курсоры Windows. Их также часто называют стандартными курсорами. Не чувствуете ли вы, что уже читали что-то похожее?
Второй параметр — это указатель на строку, содержащую имя загружаемого курсора. Как можно видеть, в рассматриваемом примере я присваиваю этому параметру значение IDC_ARROW. Оно соответствует стандартному курсору Windows, который вы могли видеть во многих программах. Оставшиеся стандартные значения перечислены в таблице 2.4.
Таблица 2.4. Константы для стандартных курсоров | |
Значение | Описание |
IDC_APPSTRING | Это курсор в форме стандартной стрелки с присоединенными к ней песочными часами. Обычно, данный курсор устанавливается, когда ваша программа занята. |
IDC_ARROW | Стандартный курсор Windows. |
IDC_CROSS | Создает курсов, выглядящий как перекрестье прицела. |
IDC_HELP | Этот курсор выглядит как стандартная стрелка с присоединенным к ней вопросительным знаком. Его хорошо использовать, когда пользователю предоставляется возможность задать вопрос. |
IDC_IBEAM | Курсор в форме буквы «I». Обычно используется в режиме ввода и редактирования текста. |
IDC_NO | Курсор в виде перечеркнутого круга. Его можно использовать, когда пользователь наводит курсор на область, которая не реагирует на щелчки кнопок мыши. |
IDC_SIZEALL | Курсор с перекрещенными стрелками. Применяется, когда пользователь изменяет размер окна или графического элемента. |
IDC_SIZENESW | Еще один курсор для изменения размера. В отличие от предыдущего курсора, у которого стрелки направлены во все четыре стороны, здесь стрелки направлены только на северо-восток и юго-запад. |
IDC_SIZENS | То же, что и предыдущий курсор, но стрелки направлены на север и на юг. |
IDC_SIZENWSE | То же, что и предыдущие два курсора, но стрелки направлены на северо-запад и юго-восток. |
IDC_SIZEWE | Еще один курсор со стрелками. В данном случае они направлены на запад и на восток. |
IDC_UPARROW | Курсор в виде стрелки, направленной вверх. |
IDC_WAIT | Курсор в виде песочных часов. Я рекомендую использовать этот курсор в тех случаях, когда ваша программа занята и пользователь может только ждать пока она не закончит работу. |
После того, как вы освоились с относящейся к курсорам частью структуры данных окна, пришло время поговорить о цвете фона. Чтобы задать цвет фона вашего окна, вы просто указываете желаемый цвет в члене данных hbrBackground. Это девятый элемент структуры, и он имеет тип HBRUSH. Возможно, вы подумали, что тип HBRUSH это всего лишь дескриптор класса кисти. Но этот член данных более гибкий и позволяет задать как дескриптор кисти, которая будет использоваться, так и значение используемого цвета. Существует одно требование — задавая цвет, вы должны использовать одно из следующих значений:
В рассматриваемом примере я не использую этот метод. Но если вы захотите установить для фона один из стандартных цветов, просто замените задающую фоновый цвет строку кода примера на ту, которая приведена ниже:
wndclass.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
В результате выполнения этого кода фон окна будет окрашен в серый цвет. Вы можете заменить COLOR_GRAYTEXT на любое из перечисленных выше значений. В моем коде вместо этого используется другой метод, включающий вызов функции GetStockObject().
Функция GetStockObject() часто используется для того, чтобы получить дескриптор одной из встроенных кистей, шрифтов, палитр или перьев. Дело в том, что в Windows есть несколько предопределенных типов, которые могут быть использованы в ваших приложениях. Давайте взглянем на прототип функции:
HGDIOBJ GetStockObject( int fnObject );
Если вам нравятся функции у которых только один параметр, то вот одна из них. Ее единственный параметр представляет собой целое число идентифицирующее предопределенный объект системы, который вы хотите использовать. Если вы укажете значение, соответствующее существующему объекту, то возвращенное функцией значение не будет равно нулю.
Моей программе посчастливилось использовать встроенный объект WHITE_BRUSH. Он окрашивает фон окна в белый цвет. В таблице 2.5 приведены еще несколько значений, которые можно использовать в качестве аргумента функции GetStockObject().
Таблица 2.5. Предопределенные объекты Windows | |
Значение | Описание |
BLACK_BRUSH | Соответствует названию. Объект предоставляет кисть, рисующую черным цветом. |
DKGRAY_BRUSH | Темно-серая кисть. |
GRAY_BRUSH | Серая кисть. |
HOLLOW_BRUSH | Возможно вы смотрели фильм «Человек-невидимка»? (Если не смотрели, не волнуйтесь — все равно фильм плохой.) Эта кисть невидима для пользователя, так же как и человек-невидимка. Это означает, что при использовании данной кисти не появляется никаких цветов. Аналогичным образом действует кисть NULL_BRUSH. |
LTGRAY_BRUSH | Светло-серая кисть. |
NULL_BRUSH | То же самое, что и HOLLOW_BRUSH. |
WHITE_BRUSH | Белая кисть. Именно она используется в моем примере. |
BLACK_PEN | Черное перо. Перья не влияют на цвет фона. Вы должны использовать их для задания цвета текста. |
WHITE_PEN | Белое перо. |
ANSI_FIXED_FONT | Объект устанавливает моноширинный системный шрифт. |
ANSI_VAR_FONT | Объект устанавливает системный шрифт с переменной шириной символов. |
DEFAULT_GUI_FONT | Устанавливается заданный по умолчанию системный шрифт. |
DEFAULT_PALETTE | Объект установленной по умолчанию палитры. |
Десятый член структуры данных WNDCLASSEX — это завершающаяся нулевым символом строка с именем lpszMenuName. Она содержит имя ресурса меню, используемого окном. В моих окнах нет меню, поэтому я присваиваю данной переменной значение NULL.
Одиннадцатый член структуры данных также является строкой, которая должна завершаться нулевым символом. Его имя — lpszClassName. Как сказано в имени, эта строка используется для задания имени класса окна. Имя класса является уникальным идентификатором типа класса. Поэтому очень важно, чтобы вы не использовали заданное здесь имя для других классов вашей программы.
Двенадцатый, и последний, член структуры WNDCLASSEX — это переменная с именем hIconSm. Она аналогична члену данных hIcon, за исключением того, что здесь задается используемый программой маленький значок. В моем примере для этого применяется уже знакомая вам функция LoadIcon().
Ну что? Мы завершили изучение структуры данных WNDCLASSEX! Теперь пришло время зарегистрировать ее.
Сейчас мы достигли момента, когда класс окна должен быть зарегистрирован. Ниже приведен фрагмент кода, выполняющий это действие.
if(RegisterClassEx(&wndclass) == 0) { // Сбой программы, выход exit(1); }
Вызов функции RegisterClassEx() необходим, чтобы потом мы смогли создать окно. Эта функция регистрирует класс в системе Windows. Если класс не зарегистрирован, то вы не сможете использовать его для создания окна.
Функция очень проста. Вот ее прототип:
ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );
Первый и единственный необходимый для нее параметр является указателем на структуру данных WNDCLASSEX. Здесь все просто, — в нашем примере достаточно только передать функции значение &wndclass.
Не следует слишком волноваться из-за того, что функция возвращает значение типа ATOM. Важно только проверить равно возвращаемое функцией значение NULL или нет. Если возвращаемое функцией RegisterClassEx() значение не равно нулю, ее выполнение завершилось успешно.
Итак, класс окна зарегистрирован, и программа переходит к действительному созданию окна.
Для создания окна используется функция CreateWindowEx(). Ее можно применять для создания дочерних, всплывающих или перекрывающихся окон. При создании окна вы указываете используемый класс, имя приложения и некоторые другие параметры. Прототип функции выглядит следующим образом:
HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
Первый параметр имеет тип DWORD и называется dwExStyle. Он похож на определяющий стиль член структуры WNDCLASSEX, но задает дополнительные стили окна. Список доступных дополнительных стилей приведен в таблице 2.6.
Таблица 2.6. Дополнительные стили окна | |
Стиль | Описание |
WS_EX_ACCEPTFILES | Окно может получать файлы с использованием механизма перетаскивания. |
WS_EX_APPWINDOW | Заставляет окно перемешаться на верхний уровень панели задач, когда оно видимо. |
WS_EX_CLIENTEDGE | Окно обрамлено рамкой, чтобы клиентская область выглядела углубленной. |
WS_EX_CONTROLPARENT | Позволяет переключаться между дочерними окнами с помощью клавиши TAB. |
WS_EX_DLGMODALFRAME | Окно будет обрамлено двойной рамкой. |
WS_EX_LEFT | Окно выровнено по левой границе. Это значение по умолчанию. |
WS_EX_LEFTSCROLLBAR | Для некоторых языков полосу прокрутки следует располагать слева от текста. В таких случаях и применяется этот стиль. |
WS_EX_LTRREADING | Отображаемый в окне текст читается слева направо. Это значение по умолчанию. |
WS_EX_NOPARENTNOTIFY | Подавляет сообщение WM_PARENTNOTIFY отправляемое дочерним окном родительскому при создании или уничтожении. Применяется для дочерних окон. |
WS_EX_OVERLAPPEDWINDOW | Комбинация флагов WS_EX_CLIENTEDGE и WS_EX_WINDOWEDGE. |
WS_EX_PALETTEWINDOW | Комбинация флагов WS_EX_WINDOWEDGE, WS_EX_TOOLWINDOW и WS_EX_TOPMOST. |
WS_EX_RIGHT | Содержимое окна выравнивается по правой границе. Это необходимо для некоторых языков. |
WS_EX_RIGHTSCROLLBAR | Полоса прокрутки располагается справа от клиентской части окна. Это значение по умолчанию. |
WS_EX_RTLREADING | В некоторых языках текст читают справа налево. Для таких языков использование данного стиля позволит системе отображать символы в окне справа налево. |
WS_EX_STATICEDGE | Создает окно, предназначенное для элементов, которые не будут получать входные данные от пользователя. |
WS_EX_TOOLWINDOW | Создает окно, выглядящее как панель инструментов. |
WS_EX_TOPMOST | Окно будет оставаться самым верхним на рабочем столе, независимо от того, активно оно или нет. |
WS_EX_WINDOWEDGE | У окна будет рамка с рельефной гранью. |
В рассматриваемом примере я указываю в первом параметре дополнительный стиль WS_EX_OVERLAPPEDWINDOW. Благодаря использованию этого стиля, окно программы будет выглядеть подобно большинству приложений Windows.
Второй параметр представляет собой завершающуюся нулевым символом строку, содержащую имя класса для создаваемого окна. Все, что необходимо сделать — указать то же имя, которое вы использовали при инициализации члена lpszClassName структуры WNDCLASSEX. В моем примере я использую строку «Window Class».
Третий параметр — это еще одна завершающаяся нулевым символом строка. Вместо того, чтобы определять имя класса, она задает текст, который будет выведен в заголовке окна. Вы можете назвать свою программу как пожелаете, а я свою назвал «Create Window Example».
Четвертый параметр, dwStyle, позволяет задать различные комбинации стилей для вашего окна. Доступные стили перечислены в таблице 2.7.
Таблица 2.7. Cтили окна | |
Стиль | Описание |
WS_BORDER | Окно обрамлено тонкой рамкой. |
WS_CAPTION | У окна есть заголовок. |
WS_CHILD | Окно является дочерним. Этот стиль нельзя использовать вместе со стилем WS_POPUP. |
WS_CHILDWINDOW | То же самое, что и WS_CHILD. |
WS_CLIPCHILDREN | Предотвращает рисование в тех областях окна, которые заняты дочерними окнами. |
WS_CLIPSIBLINGS | Используется только для дочерних окон. Если дочернее окно, для которого был установлен этот стиль перекрывается другими дочерними окнами, то при обновлении содержимого окна будет перерисовываться только та часть, которая не закрыта другими окнами. |
WS_CLIPSIBLINGS | Окно заблокировано и не может принимать данные от пользователя. |
WS_DLGFRAME | Стиль для диалоговых окон. |
WS_GROUP | Указывает, что данное окно является первым в группе окон. Используется для упорядочивания окон. Первое окно, у которого установлен стиль WS_GROUP начинает группу, после чего все последующие окна будут добавляться в ту же группу, пока снова не встретится окно с установленным стилем WS_GROUP (оно начнет следующую группу). |
WS_HSCROLL | Окно с горизонтальной полосой прокрутки. |
WS_ICONIC | После создания окно отображается в свернутом виде. |
WS_MAXIMIZE | После создания окно отображается развернутым на весь экран. |
WS_MAXIMIZEBOX | В заголовке окна есть кнопка для развертывания на весь экран. |
WS_MINIMIZE | То же самое, что и стиль WS_ICONIC. |
WS_MINIMIZEBOX | В заголовке окна есть кнопка для свертывания окна. |
WS_OVERLAPPED | Перекрывающееся окно с заголовком и рамкой. |
WS_OVERLAPPEDWINDOW | Окно с комбинацией стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX. |
WS_POPUP | Стиль для всплывающих окон. |
WS_POPUPWINDOW | Всплывающее окно с комбинацией стилей WS_BORDER, WS_POPUP и WS_SYSMENU. |
WS_SIZEBOX | Окно может изменять размер. |
WS_SYSMENU | У окна есть системное меню, вызываемое при щелчке правой кнопкой мыши по заголовку. Чтобы этот стиль работал правильно, он должен использоваться совместно со стилем WS_CAPTION. |
WS_TABSTOP | Окно может стать активным при нажатии на клавишу TAB. |
WS_THICKFRAME | То же самое, что и стиль WS_SIZEBOX. |
WS_TILED | То же самое, что и стиль WS_OVERLAPPED. |
WS_VISIBLE | После создания окно будет видимым. |
WS_VSCROLL | Окно с вертикальной полосой прокрутки. |
В программе CreateWindow в этом параметре я указываю стиль WS_OVERLAPPEDWINDOW. Это придает окну вид, похожий на большинство других приложений Windows.
Пятый параметр функции CreateWindowEx() — это целое число, задающее позицию окна на экране по горизонтали. Если вы создаете дочернее окно, координата отсчитывается относительно координаты по горизонтали родительского окна. Поскольку в примере нет дочерних окон, используемое значение координаты, 0, приведет к размещению окна вплотную к левой границе экрана.
Шестой параметро задает позицию окна на экране по вертикали. Подобно позиции по горизонтали, позиция по вертикали представляет координату на экране только в том случае, если окно не является дочерним. Для дочернего окна это значение задает смещение относительно позиции по вертикали родительского окна. Чтобы лучше понять эту концепцию взгляните на рис. 2.11.
Рис. 2.11. Координаты окна определяют его позицию в зависимости от того, является окно дочерним или нет |
Значения координат обоих окон на рис. 2.11 равны (10,10). Большее окно является родительским, поэтому его координаты (10,10) задают размещение окна почти вплотную к левой и верхней границам экрана. Меньшее окно является дочерним. Поэтому его координаты являются смещениями относительно родительского окна. Это означает, что координаты (10,10) в действительности представляют (parentx+10,parenty+10) или (20,20) в системе координат экрана.
Седьмой параметр создающей окно функции задает ширину окна в пикселах. В нашем примере ширина окна равна 320 пикселам.
Восьмой параметр задает высоту окна в пикселах. В примере высота окна равна 200 пикселам.
Девятый параметр функции указывает родителя данного окна. Он полезен, когда создается приложение с несколькими окнами. Поскольку в рассматриваемом примере есть только одно главное окно, данному параметру присваивается значение NULL чтобы указать, что у окна нет родителя.
Десятый параметр используется для задания дескриптора связанного с окном меню. В примере нет никаких меню, поэтому данному параметру присваивается значение NULL.
Одиннадцатый параметр используется для задания дескриптора экземпляра модуля. В этом параметре вы передаете дескриптор экземпляра, полученный функцией WinMain() в одном из ее параметров.
Последний параметр используется для указания дополнительных данных создаваемого окна. Мне очень редко приходилось использовать этот параметр, поэтому в большинстве приложений я присваиваю ему значение NULL.
Ничего себе! Вы проделали почти всю работу, необходимую для отображения окна на экране. На рис. 2.12 показан ход выполнения программы до настоящего момента.
Рис. 2.12. Ход выполнения кода приложения до вызова функции CreateWindowEx() |
Верите или нет, но факт создания окна не приводит к его отображению. (Эти парни из Microsoft иногда такие странные!) Чтобы окно было действительно выведено на экран вы должны вызвать функцию ShowWindow(). К счастью, она достаточно прямолинейна. Вот ее прототип:
BOOL ShowWindow( HWND hWnd, int nCmdShow );
Первый параметр задает дескриптор отображаемого окна. Это действительно просто, поскольку дескриптор уже подготовлен функцией, создавшей окно. Взгляните на пример, чтобы увидеть как я передаю дескриптор, возвращенный функцией CreateWindow().
Второй параметр — это целое число, определяющее как будет отображаться окно. Вот и пришло время для еще одной таблицы. Значения, которые можно использовать в этом параметре перечислены в таблице 2.8.
Таблица 2.8. Значения для функции ShowWindow() | |
Значение | Описание |
SW_HIDE | Окно скрыто и затем окно активируется. |
SW_MAXIMIZE | Окно развернуто на весь экран. |
SW_MINIMIZE | Окно свернуто и затем окно активируется. |
SW_RESTORE | Восстанавливает окно из свернутого или развернутого состояния. Это полезно, если требуется вернуть окну его первоначальные размеры и местоположение. |
SW_SHOW | Активирует окно и отображает его. |
SW_SHOWMAXIMIZED | Окно активируется и отображается развернутым на весь экран. |
SW_SHOWMINIMIZED | Окно активируется и отображается свернутым. |
SW_SHOWNA | Окно отображается в его текущем состоянии. Не оказывает никакого влияния если окно в данный момент активно. |
SW_SHOWNORMAL | Отображает окно в его нормальном состоянии. Это значение используется, когда функция ShowWindow() вызывается в первый раз. |
Хотя есть много значений, которые вы можете указать во втором параметре, простейший способ заключается в передаче значения целочисленной переменной iCmdShow, которая является одним из параметров функции WinMain(). Именно так я и поступаю в коде примера.
Итак, ваша программа полностью завершила свою основную задачу — отображение окна. Однако, она пока не может принимать никаких входных данных. Здесь в игру вступает код цикла обработки сообщений.
Чтобы приложения Windows знали когда их окна закрываются, перемещаются или изменяют размеры, они должны принимать сообщения Windows. В общем случае ваши приложения для Windows должны всегда иметь цикл сообщений. Вспомните сведения о событиях и цикле сообщений, которые я приводил в начале главы. В примере я использую простейший метод проверки наличия сообщений и обработки любых обнаруженных сообщений.
Чтобы проверить наличие ожидающих обработки сообщений вызывается функция GetMessage(). Вот ее прототип:
BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );
В первом параметре функция ожидает указатель на объект MSG. Структура данных MSG содержит всю информацию о любом обнаруженном сообщении.
Второй параметр указывает для какого окна проводится проверка наличия сообщений. Он необходим потому, что ваша программа может управлять несколькими окнами. В рассматриваемом примере есть только одно окно, поэтому я просто передаю дескриптор окна, созданного функцией CreateWindow().
Третий параметр задает нижнюю границу кодов получаемых сообщений. Мы хотим получать все сообщения, поэтому присваиваем данному параметру значение 0.
Четвертый параметр позволяет задать верхнюю границу кодов получаемых сообщений. Поскольку мы хотим получать все сообщения, значение этого параметра также равно 0.
Перед тем, как отправить сообщение в вашу очередь сообщений, вы должны транслировать его в символьные данные. Это делает функция TranslateMessage(). Для нее требуется единственный параметр — указатель на транслируемое сообщение. В примере я передаю функции адрес переменной, содержащей сообщение.
После того, как сообщение транслировано в символьные данные, вы можете поместить его в очередь сообщений с помощью функции DispatchMessage().
Подобно функции TranslateMessage(), функция DispatchMessage() требует единственный параметр. Цель этой функции — отправить прошедшее трансляцию сообщение в очередь сообщений программы. После вызова этой функции сообщение попадает в функцию обработки сообщений.
Последняя строка кода функции WinMain() возвращает значение wParam последнего сообщения Windows извлеченного функцией получения сообщений. Как говорил поросенок Порки, «Вот и все, ребята!».
Вы вероятно задаетесь вопросом, где обрабатываются сообщения. Когда функция DispatchMessage() отправляет сообщения, они направляются функции обработки сообщений, указанной в классе окна. В рассматриваемом примере все сообщения отправляются написанной мной функции fnMessageProcessor().
Взгляните на код функции обработки сообщений, и вы увидите, что она представляет собой одну инструкцию switch средних размеров. Это вызвано тем, что единственная задача данной функции — перебрать все типы сообщений, которые интересуют нас и проверить, соответствует ли полученное сообщение одному из них.
Мой пример проверяет сообщения WM_CREATE, WM_DESTROY и WM_PAINT. Дополнительная работа производится только для сообщения WM_DESTROY.
Когда приложение получает сообщение WM_DESTROY, оно вызывает функцию PostQuitMessage(), которая завершает работу программы Windows. Это происходит, когда вы завершаете работу, щелкнув по кнопке закрытия окна.
Если полученное сообщение не соответствует ни одному из проверяемых программой, оно возвращается функции WinMain() с помощью функции DefWindowProc(). Это стандартная практика, поэтому я не рекомендую пытаться изменить ее.
Рассматриваемый пример проверяет лишь несколько типов сообщений, но примеры, встечающиеся дальше в этой книге проверяют гораздо больше сообщений. Помните об этом, когда будете просматривать код последующих примеров — вы можете столкнуться с абсолютно незнакомыми типами сообщений.
Итак, ваша программа целиком введена и вы готовы идти дальше. Чтобы скомпилировать программу в Visual C++ 6.0 вам необходимо сделать активным окно с компилируемой программой и нажать клавишу F7. Если все закончится успешнно, то в окне с выходными данными компилятора вы увидите следующий текст:
---------Configuration: CreateWindow - Win32 Debug------------ Compiling... CreateWindow.cpp Linking... CreateWindow.exe - 0 error(s), 0 warning(s)
Если во время компиляции будут обнаружены какие-либо ошибки, заново проверьте код и убедитесь, что все набрано правильно. Когда будут исправлены все ошибки, вы сможете запустить программу, нажав комбинацию клавиш Ctrl+F5. В результате начнет выполняться программа активного проекта. На рис. 2.13 показано, как должна выглядеть работающая программа рассматриваемого примера.
Рис. 2.13. Результат выполнения программы CreateWindow
netlib.narod.ru | < Назад | Оглавление | Далее > |