netlib.narod.ru< Назад | Оглавление | Далее >

Пишем первую программу для Windows

На этом я заканчиваю обсуждение теории, лежащей в основе программирования для Windows. Пришло время написать вашу первую программу для Windows. Во всех, рассматриваемых в этой книге примерах, я использую компилятор Microsoft Visual C++ 6.0. Если у вас еще нет этой программы, я настоятельно рекомендую пойти и приобрести ее.

СОВЕТ
Когда я проверял последний раз, версия Visual C++ 6.0 Standard стоила около 100 долларов США. Вероятно, вы сможете приобрести ее еще дешевле, присоединившись к программе скидок для студентов. Это хорошее вложение денег.

Начало работы с Visual C++ 6.0

Давайте продолжим. Запустите вашу копию Visual C++ 6.0. Вы должны увидеть экран, аналогичный представленному на рис. 2.2.


Рис. 2.2. Интерфейс Visual C++ 6.0

Рис. 2.2. Интерфейс Visual C++ 6.0


В интерфейсе, представленном на рис. 2.2, нет ничего особенного. Основные элементы управления сосредоточены в верхнем меню: File, Edit, View, Insert и т.д. Все, что вы делаете в Visual C++, должно быть связано с проектом. Проект — это верхний уровень иерархии в среде разработки.

Как создать проект

Лучший способ обучения — практика, так что давайте создадим новый проект, для чего щелкнем по меню File и выберем пункт New. Вам будет предоставлено множество вариантов, изображенных на рис. 2.3.


Рис. 2.3. Диалоговое окно создания нового проекта в Visual C++

Рис. 2.3. Диалоговое окно создания нового проекта в Visual C++


Диалоговое окно, которое вы видите, используется для указания типа создаваемого проекта. Как видно Visual C++ используется для создания всех типов программ, включая элементы управления ActiveX, COM-объекты, надстройки DevStudio и конечно же приложения Windows. Нас интересует тип приложений, который называется Win32 Application. Этот тип используется при создании программ для Windows. Выделите в списке пункт Win32 Application чтобы выбрать этот тип.

Перед тем, как двинуться дальше, вы должны сообщить Visual C++ имя вашего проекта и где он будет храниться. Эта информация вводится в текстовые поля Project name и Location. Для нашего примера в качестве имени проекта укажите CreateWindow.

СОВЕТ
Я храню весь исходный код в папке C:\Source\. Рекомендую вам создать аналогичную структуру каталогов, где будут храниться исходные тексты ваших программ. Лучше хранить весь код в единственном месте, чем разбрасывать его по всему жесткому диску.

После того, как вы введете требуемую информацию, щелкните по кнопке OK. Вам будет предложено диалоговое окно, показанное на рис. 2.4.


Рис. 2.4. Диалоговое окно выбора типа Windows-приложения

Рис. 2.4. Диалоговое окно выбора типа Windows-приложения


Диалоговое окно, представленное на рис. 2.4, предлагает три варианта:

Первый вариант, пустой проект, используется наиболее часто. Его выбор приводит к созданию пустого проекта в который не включены никакие объекты. Фактически создается чистый холст для последующей работы.

Второй вариант, простое приложение Win32, используется реже, поскольку все, что он делает — это создает программу, которая ничего не делает. В этом случае для вас создаются исходные файлы, но они делают не слишком много.

Третий вариант, типичное приложение «Hello World!», делает больше, чем два предыдущих. Он создает законченую программу для Windows, которая выводит текст «Hello World!». Я не использую этот вариант слишком часто, поскольку создаваемый код на мой вкус выглядит слишком загроможденным.

Выберите первый переключатель и щелкните по кнопке Finish, чтобы открыть диалоговое окно, изображенное на рис. 2.5.


Рис. 2.5. Диалоговое окно подтверждения выбранных параметров

Рис. 2.5. Диалоговое окно подтверждения выбранных параметров


Представленное диалоговое окно не содержит ничего необычного. Оно выводится только для того, чтобы вы могли удостовериться, что будет создан проект желаемого типа. Для продолжения работы щелкните по кнопке OK.

Рабочее пространство

Итак, вот ваш проект во всей славе! Теперь, после того как проект создан, настало время подробнее поговорить о нем. Взгляните на рис. 2.6.


Рис. 2.6. Пустое пространство проекта для вашей новой программы

Рис. 2.6. Пустое пространство проекта для вашей новой программы


Взгляните на рабочее пространство — область в левой части, содержащую имя вашего проекта со знаком «+» рядом с ним. Перейдите на вкладку FileView и разверните дерево, щелкнув по знаку «+». Вы увидите три подпапки в дереве файлов проекта:

Надеюсь, вы знаете как программировать на С или С++ и для чего предназначены исходные и заголовочные файлы. Если нет, я рекомендую вам приобрести книгу по программированию на С++ и изучить ее перед тем, как продолжать чтение. Без этих знаний вам придется очень трудно.

ПРИМЕЧАНИЕ
Рабочее пространство — это второй уровень иерархии в среде разработки. В начале цепочки расположен проект, затем следует рабочее пространство. Файлы вашего проекта расположены в конце цепочки. В одном проекте может быть несколько рабочих пространств, точно также как в одном рабочем пространстве может быть несколько исходных файлов.
Файлы ресурсов

Если вы умеете программировать, но являетесь новичком в программировании для Windows, вам может быть непонятно назначение папки Resource Files.

В программировании для Windows ресурсами называются элементы, используемые для расширения или добавляемые к приложению. Примерами ресурсов могут служить значки, растровые изображения, звуковые файлы и текстовые строки. Прелесть ресурсов в том, что они компилируются в вашу программу. Вам не надо беспокоиться об их установке на компьютер пользователя, ведь они являются частью исполняемого файла.

СОВЕТ
Ресурсы удобны, но не стоит включать в программу слишком много их. Если поступать так, полученные исполняемые файлы будут слишком большими. Это вызовет проблемы, когда для пользователей будет выпускаться обновление исполняемых файлов. Вместо того, чтобы загрузить компактный исполняемый файл, им придется загружать огромный файл, полный ресурсов.

Я не хочу потеряться среди связанных с ресурсами тем, так что давайте отправимся дальше. В вашем проекте пока нет файлов, поэтому в папках ничего не перечислено. Давайте исправим это, добавив в проект файл с исходным кодом на С++.

Добавление исходных файлов в проект

Раскройте меню Project и выберите пункт Project | Add To Project | New. В результате на экран будет выведено диалоговое окно, изображенное на рис. 2.7.


Рис. 2.7. Диалоговое окно для выбора типа добавляемого к проекту файла

Рис. 2.7. Диалоговое окно для выбора типа добавляемого к проекту файла


Фактически это то же самое диалоговое окно, что и изображенное на рис. 2.3, просто в нем выбрана другая вкладка. Вместо того, чтобы выбирать тип создаваемого проекта, сейчас мы будем выбирать тип добавляемого файла. Больше всего нас должны интересовать типы C/C++ Header File и C++ Source File. Перед тем, как продолжить, выберите тип C++ Source File.

Мы уже на финишной прямой, но еще не закончили. Перед тем, как щелкнуть по кнопке OK, вы должны задать имя создаваемого файла. Введите в качестве имени файла CreateWindow и щелкните по кнопке OK.

ПРИМЕЧАНИЕ
Вы не обязаны давать исходному файлу и проекту одинаковые имена. Я делаю это только ради организации информации. Не стесняйтесь называть файл с исходным кодом, как вы желаете. (Некоторые люди используют имя Main.)
СОВЕТ
Когда вы создаете новый файл для проекта к нему добавляется требуемое расширение. Вам не надо вводить его. Например, если вы создадите файл с исходным кодом C++ и укажете имя Main, на жестком диске он будет сохранен как файл Main.cpp.

Теперь, когда файл создан и добавлен к проекту, вы можете видеть его в окне рабочего пространства. Взгляните на рис. 2.8.


Рис. 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()

Первый элемент, упоминаемый в функции WinMain() - это объект, используемый для создания окна. Объект является структурой типа WNDCLASSEX и очень важен для создания окна вашей программы.

Структура данных 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 — оно обозначает целое число без знака. Это означает, что переменная может принимать только положительные значения.

Второй элемент структуры также имеет тип 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. Взаимоотношения между классом окна и обработчиком сообщений

Рис. 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

Рис. 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! Теперь пришло время зарегистрировать ее.

Функция RegisterClassEx()

Сейчас мы достигли момента, когда класс окна должен быть зарегистрирован. Ниже приведен фрагмент кода, выполняющий это действие.

if(RegisterClassEx(&wndclass) == 0)
{
    // Сбой программы, выход
    exit(1);
}

Вызов функции RegisterClassEx() необходим, чтобы потом мы смогли создать окно. Эта функция регистрирует класс в системе Windows. Если класс не зарегистрирован, то вы не сможете использовать его для создания окна.

Функция очень проста. Вот ее прототип:

ATOM RegisterClassEx(
    CONST WNDCLASSEX *lpwcx
);

Первый и единственный необходимый для нее параметр является указателем на структуру данных WNDCLASSEX. Здесь все просто, — в нашем примере достаточно только передать функции значение &wndclass.

Не следует слишком волноваться из-за того, что функция возвращает значение типа ATOM. Важно только проверить равно возвращаемое функцией значение NULL или нет. Если возвращаемое функцией RegisterClassEx() значение не равно нулю, ее выполнение завершилось успешно.

Итак, класс окна зарегистрирован, и программа переходит к действительному созданию окна.

Функция CreateWindowEx()

Для создания окна используется функция 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. Координаты окна определяют его позицию в зависимости от того, является окно дочерним или нет


Значения координат обоих окон на рис. 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()

Рис. 2.12. Ход выполнения кода приложения до вызова функции CreateWindowEx()


Функция ShowWindow()

Верите или нет, но факт создания окна не приводит к его отображению. (Эти парни из 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(). Именно так я и поступаю в коде примера.

Итак, ваша программа полностью завершила свою основную задачу — отображение окна. Однако, она пока не может принимать никаких входных данных. Здесь в игру вступает код цикла обработки сообщений.

Получение сообщений функцией GetMessage()

Чтобы приложения Windows знали когда их окна закрываются, перемещаются или изменяют размеры, они должны принимать сообщения Windows. В общем случае ваши приложения для Windows должны всегда иметь цикл сообщений. Вспомните сведения о событиях и цикле сообщений, которые я приводил в начале главы. В примере я использую простейший метод проверки наличия сообщений и обработки любых обнаруженных сообщений.

Чтобы проверить наличие ожидающих обработки сообщений вызывается функция GetMessage(). Вот ее прототип:

BOOL GetMessage(
    LPMSG lpMsg,
    HWND hWnd,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax
);

В первом параметре функция ожидает указатель на объект MSG. Структура данных MSG содержит всю информацию о любом обнаруженном сообщении.

Второй параметр указывает для какого окна проводится проверка наличия сообщений. Он необходим потому, что ваша программа может управлять несколькими окнами. В рассматриваемом примере есть только одно окно, поэтому я просто передаю дескриптор окна, созданного функцией CreateWindow().

Третий параметр задает нижнюю границу кодов получаемых сообщений. Мы хотим получать все сообщения, поэтому присваиваем данному параметру значение 0.

Четвертый параметр позволяет задать верхнюю границу кодов получаемых сообщений. Поскольку мы хотим получать все сообщения, значение этого параметра также равно 0.

Трансляция сообщений функцией TranslateMessage()

Перед тем, как отправить сообщение в вашу очередь сообщений, вы должны транслировать его в символьные данные. Это делает функция TranslateMessage(). Для нее требуется единственный параметр — указатель на транслируемое сообщение. В примере я передаю функции адрес переменной, содержащей сообщение.

После того, как сообщение транслировано в символьные данные, вы можете поместить его в очередь сообщений с помощью функции DispatchMessage().

Помещение сообщений в очередь функцией 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

Рис. 2.13. Результат выполнения программы CreateWindow



netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz