Уроки начинающим работать с С++ в Windows

Урок 1. Первая программа на С++ в среде Windows  — "Hello. World !"

Программирование в Операционной Системе Windows основано на событиях и сообщениях.
Операционная система Windows общается с программой путем посылки ей сообщений. Каждый раз при запуске
новой программы, Windows создает новый процесс и выделяет для этого процесса свою очередь сообщений
Когда в системе происходит какое либо событие: нажатие клавиши клавиатуры, кнопки мыши, движение мыши и др.
первой получает сообщение об этом операционная система Windows ! Получив сообщение, ОС посылает сообщение
об этом событии программе или, другими словами, направляет это событие в очередь сообщений для данной
программы, в окне которой это событие произошло.
Все приложения Windows должны иметь цикл обработки этих сообщений ( в функции WinMain() )
Этот цикл читает сообщения из очереди сообщений программы и направляет эти сообщения назад ОС Windows,
которая (ОС) затем вызывает оконную функцию программы, передавая ей это сообщение в качестве параметра
Именно так функционируют все программы под Windows.

По традиции первой программой на С++ выступает программа, выводящая на экран окно с текстом "Hello. World"



Любая программа под Windows состоит минимум из двух функций — WinMain() и оконной функции.
Функция WinMain() должна выполнять следующие операции:

  1. Определять класс окна
  2. Регистрировать класс окна
  3. Создавать окно данного класса
  4. Отображать окно
  5. Запускать цикл обработки сообщений
Оконная функция предназначена для обработки сообщений, относящихся к данному окну

Данный пример в ОС Windows состоит примерно из нескольких десятков строк кода на С++
Итак, начинаем...

#include <windows.h>

int PASCAL WinMain ( HANDLE hInstance, HANDLE hPrevInst, LPSTR lpszCmdParam, int nCmdShow )


Функция WinMain().

Каждая программа для Windows имеет главную функцию — точку входа, с которой начинается программа, и которая
называется WinMain(). Функция WinMain() позволяет передавать аргументы командной строки приложениям Windows
через параметр lpszCmdParam

Функция WinMain() имеет четыре аргумента

hInstance — уникальный идентификатор текущего экземпляра приложения
hPrevInstance — идентификатор последненго из запущенных экземпляров приложения, если запущено несколько экземпляров
приложения, если же запущен один экземпляр приложения, то hPrevInstance равен NULL
lpszCmdParam — указатель на строку, которая заканчивается нулевым байтом и содержит все параметры командной строки,
передаваемые данному экземпляру программы.
nCmdShow — целое число, показывающее как отображать запущенную программу — в виде открытого окна или в виде кнопки
на панели задач

Определяем локальные переменные
{
static char szAppName[] = "WinHel"; // szAppName — имя класса окна приложения
HWND hwnd; // hwnd — дескриптор окна приложения
MSG msg; // msg-сообщение (имеет тип MSG — см. описание ниже)
WNDCLASS wcn; // wcn-структура для передачи параметров, определяющая
вид главного окна приложения<
Регистрация класса окна

Здесь возможны два варианта, в зависимости от значания аргумента hPrevInstance.
Первый — в настоящий момент активен хотя бы один экземпляр программы, и тогда регистрировать класс окна не нужно.
Второй — нет ни одного запущенного экземпляра программы, что чаще всего и бывает, т.е. hPrevInstance == NULL, и значит
мы должны зарегистрировать класс окна , в котором хотим вывести надпись "Hello, World".
Заполняем структуру wcn
if(!hPrevInst) // проверяем — если нет ни одного запущенного экземпляра приложения -
// т.е. наш класс окна еще не зарегистрирован — регистрируем класс окна
{
wcn.style = CS_HREDRAW | CS_VREDRAW; // определяем стиль окна приложения
wcn.lpfnWndProc = WndProc; // указатель на функцию WndProc
wcn.cbClsExtra = 0; // целые числа, зарезервированные для
wcn.cbWndExtra = 0; // дополнительной информации о стилях
wcn.hInstance = hInstance; // значение параметра hInstance
wcn.hIcon = LoadIcon(NULL, IDI_APPLICATION); // дескриптор значка приложения
wcn.hCursor = LoadCursor(NULL, IDC_ARROW); // дескриптор указателя мыши
wcn.hbrBackground = GetStockObject(WHITE_BRUSH); // цвет и узор фона главного окна
wcn.lpszMenuName = NULL; // т.к. меню у нас нет, значит NULL
wcn.lpszClassName = szAppName; // имя класса окна, определенное выше
RegisterClass ( &wcn ); // и наконец — регистрируем класс окна
}
wcn.style — в этом поле определяется стиль окна. Установленные флаги CS_HREDRAW и CS_VREDRAW означают,
что всякий раз при изменении ширины или высоты окна будет производиться полная перерисовка рабочей области
окна
wcn.lpfnWndProc — указатель на экспортируемую функцию, обрабатывающую все сообщения. которые Windows посылает
приложению.
wcn.cbClsExtra и wcn.cbWndExtra — целые числа, зарезервированные для указания дополнительной информации о стилях
класса или окна.
wcn.hInstance — значение параметра hInstance, определенного Windows при инициализации первого экземпляра программы,
неизменное для всех последующих экземпляров приложения
wcn.Icon — дескриптор значка приложения (у нас например IDI_APPLICATION - белый квадрат в черной рамке)
wcn.hCursor — дескриптор указателя мыши (у нас например IDC_ARROW — вид стрелки, направленной вверх и влево)
wcn.hbrBackground — дескриптор кисти, определяет цвет и узор фона главного окна (белый фон окна — WHITE_BRUSH)
wcn.lpszMenuName — указатель на строку, содержащую имя меню (у нас нет меню, поэтому NULL)
wcn.lpszClassName — указатель на строку с именем класса окна (как у нас будет назван наш класс окна)
И наконец, вызов API-функции Windows RegisterClass(), предназначенной для регистрации класса окна в Windows

Создание окна приложения

Создается окно приложения функцией CreateWindow()
Функция CreateWindow() возвращает дескриптор созданного окна hwnd, который в дальнейшем может использоваться в
качестве аргумента при вызове других функций, работающих с нашим окном.
hwnd = CreateWindow(
szAppName, // имя класса окна
"My First Window", // заголовок окна-если окно будет без заголовка, то NULL
WS_OVERLAPPEDWINDOW, // стиль окна
CW_USEDEFAULT, // начальная Х-координата окна (или введите значение)
CW_USEDEFAULT, // начальная У-координата окна (или введите значение)
CW_USEDEFAULT, // начальный горизонтальный размер(или введите значение)
CW_USEDEFAULT, // начальный вертикальный размер (или введите значение)
NULL, // дескриптор родительского окна
NULL, // дескриптор меню окна — NULL — меню по умолчанию
hInstance, // дескриптор экземпляра программы
NULL); // параметры создания
Значениями по умолчанию CW_USEDEFAULT являются для начального положения окна — левый верхний угол экрана,
для начального размера окна — размеры по умолчанию.
Дескриптор меню окна — дескриптор системного меню, которое активизируется при нажатии кнопки,
расположенной в левом углу строки заголовка.(а не главное меню программы)

Отображение окна

Итак, окно было создано, теперь его надо отобразить на экране монитора. Делается это функцией ShowWindow(),
которой в качестве параметра передается дескриптор окна, возвращенный функцией CreateWindow() и аргумент
nCmdShow переданный функции WinMain() (см. выше)
ShowWindow( hwnd , nCmdShow ); // отобразить окно на экране
UpdateWindow( hwnd ); // вывести на экран рабочую область окна.
Цикл обработки сообщений

Как уже было сказано ранее, для каждого приложения Windows создает отдельную очередь сообщений.
Каждый раз, когда происходит событие, Windows преобразует это событие в сообщение и помещает это
сообщение в очередь сообщений приложению. Сообщение находится в очереди до тех пор, пока не будет обработано.
Для обработки этих сообщений и нужен цикл обработки сообщений.
Сообщение выбирается из очереди сообщений, принадлежащих данному приложению с помощью функции GetMessage()
 while( GetMessage( &msg, NULL, 0, 0 )) // ф-ция возвращает true пока не выберет WM_QUIT
{
TranslateMessage( &msg ); // ф-ция преобразования сочетания клавиш в другие события
DispatchMessage( &msg ); // распределяет сообщения по функциям окна, именно здесь
} // происходит "вызов" оконной функции
return msg.wParam; // когда цикл обработки сообщений заканчивается, поле
// wParam последнего сообщения возвращается вызывающему
// приложению — рабочему столу системы
} // конец функции WinMain()
&msg — указатель на структуру сообщения, в которой хранятся данные о полученном сообщении, msg имеет тип MSG.
Тип сообщения MSG определен следующим образом:
typedef struct tagMSG {
HWND hwnd; // дескриптор окна, которому адресуется ссобщение
UINT message; // тип Windows-сообщения (например WM_RBUTTONDOWN — нажатие правой кнопки мыши)
WPARAM wparam; // параметр сообщения, зависит от типа сообщения, например координаты указателя мыши
LPARAM lparam; // параметр сообщения, зависит от типа сообщения
DWORD time; // время постановки сообщения в очередь
POINT pt; // координаты мыши в момент time, имеет тип struct ( int x, int y; }
} MSG;
Функция WndProc()

С этой функции и начинается работа нашего приложения. Функция WndProc (ей можно присвоить другое имя)
получает сообщения из операционной системы и определяет реакцию приложения на эти сообщения
Итак, окно создано и выведено, теперь начинается работа приложения.
Мы хотим Давайте сделаем это в нашей оконной функции WndProc()
long FAR PASCAL WndProc ( HWND hwnd, UINT msg, UINT wParam, LONG lParam )
{
HDC hdc; // дескриптор контекста устройства для доступа к рисованию на экран
PAINTSTRUCT ps; // структура для рисования
RECT rect; // структура для указания области рисования switch( msg ) { case WM_PAINT: // выводим надпись "Hello, World !", для этого: hdc = BeginPaint( hwnd, &ps ); // получаем дескриптор(контекст) экрана GetClientRect( hwnd , &rect ); // получаем координаты рабочей области окна DrawText( hdc, "Hello, World !", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ); // выводим надпись EndPaint( hwnd, &ps); // освобождаем дескриптор return 0; case WM_LBUTTONDOWN: // нажатие левой кнопки мыши — выводим "*" hdc = GetDC( hwnd ); TextOut( hdc, LOWORD(lParam), HIWORD(lParam), "*", 1); ReleaseDC( hwnd, hdc ); break; case WM_DESTROY: // завершение приложения PostQuitMessage(0); // помещает в очередь сообщение WM_QUIT, необходимое для break; // прекращения цикла обработки в функции WinMain() default: // если сообщение не обработано в switch, вернуть его Windows return DefWindowProc ( hwnd, msg, wParam, lParam); } return NULL; } // завершение оконной функции
case WM_PAINT: Как правило, сообщение WM_PAINT генерируется неявным образом, например всякий раз, когда окно
приложения создается, перемещается, меняет размер, или например наше окно было закрыто частично или
полностью окном другого приложения (например, программой "Проводник") и требуется перерисовать
рабочую область окна.

case WM_DESTROY: сообщение возникает при попытке закрыть окно приложения, может появляться
и в других случаях
DefWindowProc() — обработчик необработанных или нераспознанных сообщений
В нашей программе мы обрабатываем сообщения WM_PAINT, WM_DESTROY и WM_LBUTTONDOWN
а все другие сообщения передаем обратно в Windows для обработки по умолчанию

case WM_LBUTTONDOWN: сообщение возникает при нажатии левой кнопки мыши
(работа с мышью будет описана в последующих уроках, здесь лишь как пример)
LOWORD(lParam) — координата X мыши
HIWORD(lParam) — координата Y мыши


Краткие итоги:

Итак, ма завершили создание первой программы на С++ в среде Windows "Hello. World !"
Текст и проект для C++Builder 6 получившейся программы можно скачать здесь. ( 7Кб )
Запустите программу, посмотрите как она работает, поэкспериментируйте

В следующих уроках мы поговорим о типах данных в Windows, работе с полосами прокрутки, выводе текста.