Найти тему
Яков Букреев

Создание окна в WinAPI

Оглавление

Любое графическое приложение Windows начинается с создания окна. В этой статье я расскажу, как создать простейшее окно на C++ с помощью WinAPI.

1. Для начала создадим пустой проект в Visual Studio.

2. Добавим файл main.cpp

Исходные файлы -> Добавить -> Создать элемент

3. Изменим тип подсистемы на Windows.

Проект -> Свойства -> Свойства конфигурации -> Компоновщик -> Система -> Подсистема -> Windows

4. Чтобы начать работу с WinAPI, подключим заголовочный файл Windows.h

#include <Windows.h>

5. Точка входа

Точка входа в WinAPI отличается от точки входа консольного приложения. Она может быть WinMain или wWinMain.

int WINAPI WinMain (
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nShowCmd
);
int WINAPI wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nShowCmd
);

Разберем отдельно каждый параметр.

1. hInstance. Это дескриптор нашего приложения. Каждое приложение, запущенное в Windows, имеет свой дескриптор. На самом деле, это просто адрес в памяти, на котором располагается наше приложение.

2. hPrevInstance. Этот параметр всегда равен нулю. Он использовался во времена 16-разрядного Windows, но сейчас не имеет смысла

3. lpCmdLine. Это указатель на командную строку, с которой запускается приложение. Это то же самое, что argv[0] в консоли. В случае WinMain это ASCII-строка, в случае wWinMain - Unicode-строка.

4. nShowCmd. Это флаг, указывающий, будет ли главное окно приложения свернуто, развернуто или показано обычным образом.

*WINAPI - Это соглашение о вызовах. Оно определяет, каким образом в функцию передаются параметры. То же самое, что __stdcall. Можете об этом не задумываться.

Подробнее про точку входа можно прочитать здесь: https://learn.microsoft.com/RU-RU/windows/win32/learnwin32/winmain--the-application-entry-point

6. Класс окна

Прежде чем создать окно, мы должны зарегистрировать его класс. Вот кусок кода:

WNDCLASSW wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProcW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MAIN_WINDOW_CLASS";

RegisterClassW(&wc);

Разберем отдельно каждую строчку.

1. WNDCLASS. Это структура, которая хранит информацию о классе окна. В именах WinAPI постфикс "W" означает, что мы будем использовать Unicode, а постфикс "A" - ASCII. Мы будем использовать Unicode, значит пишем WNDCLASSW.

2. wc.style - стиль класса окна. CS_HREDRAW означает, что окно перерисовывается, если изменяется ширина окна, а CS_VREDRAW - если изменяется высота

3. wc.lpfnWndProc - указатель на оконную процедуру. Оконная процедура - это процедура, которая обрабатывает сообщения, которые операционная система посылает нашему окну. Саму процедуру мы напишем в следующей статье, а пока укажем в оконную процедуру по умолчанию - DefWindowProc.

4. wc.cbClsExtra - это для прошаренных, просто ставим 0.

5. wc.cbWndExtra - это тоже для прошаренных, просто ставим 0.

6. wc.hInstance - это дескриптор приложения, которое содержит оконную процедуру.

7. wc.hIcon - это дескриптор иконки нашего окна. Функция LoadIconW(NULL, IDI_APPLICATION) загружает иконку по умолчанию.

8. wc.hCursor - это дескриптор курсора нашего окна. Функция LoadCursorW(NULL, IDC_ARROW) загружает курсор по умолчанию.

9. wc.hbrBackground - это дескриптор кисти, которое закрашивает наше окно. Пока ставим NULL.

10. wc.lpszMenuName - это имя меню окна. У нас нет меню, ставим NULL.

11. wc.lpszClassName - это имя класса окна. Мы будем использовать его при создании окна. Префикс "L" означает, что мы используем Unicode.

12. RegisterClassW(&wc) - функция RegisterClass регистрирует в операционной системе класс окна. Функция принимает в качестве аргумента указатель на структуру WNDCLASS.

NB: Если вы используете WNDCLASSA, то регистрируйте класс с помощью RegisterClassA, а если WNDCLASSW, то с помощью RegisterClassW.

7. Создание окна

Окно создается с помощью функции CreateWindow:

HWND hWnd = CreateWindowW(L"MAIN_WINDOW_CLASS", L"Simple Window", WS_OVERLAPPEDWINDOW, 200, 200, 1280, 720, NULL, NULL, hInstance, NULL);
  • Первый параметр - имя класса окна, которое мы указали в wc.lpszClassName.
  • Второй параметр - имя самого окна. Этот текст будет написан в заголовке окна.
  • Третий параметр - стиль окна. WS_OVERLAPPEDWINDOW - стиль перекрывающегося окна. Подробнее про стили окон можно прочитать в официальной документации Microsoft.
  • Четвертый и пятый параметры - координаты левого верхнего угла окна по горизонтали и вертикали соответственно. Точка отсчета располагается в левом верхнем углу экрана
  • Шестой и седьмой параметры - ширина и высота окна соответственно.
  • Восьмой параметр - дескриптор родительского окна. Он используется при создании дочерних окон. Так как наше окно не дочернее, ставим NULL.
  • Девятый параметр - дескриптор меню. Ставим NULL.
  • Десятый параметр - дескриптор нашего приложения.
  • Одиннадцатый параметр - указатель на значение, которое будет передано окну при его создании. Не используем его, ставим NULL.

Функция CreateWindow возвращает дескриптор окна, которое было создано. Дескриптор окна имеет тип HWND. Сохраним его в переменную hWnd - она нам еще понадобится.

NB: Если вы используете WNDCLASSA, то создавайте окно с помощью CreateWindowA, а если WNDCLASSW, то с помощью CreateWindowW.

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

Мы создали наше окно, но мы его еще не видим. Чтобы отобразить окно на экране, необходимо вызвать функцию ShowWindow, которая принимает два аргумента: дескриптор окна и флаг отображения.

ShowWindow(hWnd, nShowCmd);

nShowCmd - это четвёртый параметр точки входа WinMain.

*Не забудьте написать в конце функции WinMain

return 0;

Если вы все сделали правильно, то при запуске приложения должно появиться окно и сразу же исчезнуть.

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

9. Главный цикл

MSG msg;
while (GetMessageW(&msg, hWnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
  • MSG - это структура, которая хранит информацию о сообщении от операционной системы.
  • Функция GetMessage получает от операционной системы сообщение и принимает четыре агрумента: Указатель на структуру сообщения, дескриптор окна и два фильтра сообщений. Мы будем передавать в окно все сообщения, поэтому поставим фильтры на 0.
  • Функция TranslateMessage принимает указатель на полученное сообщение и преобразует некоторые виды сообщений.
  • Функция DispatchMessage отправляет полученное сообщение в оконную процедуру.

Теперь, если вы запустите приложение, то получите рабочее окошко, которое можно перемещать, растягивать, сжимать, сворачивать и закрывать.

Однако наше окно является еще пустышкой: мы не можем никаким особенным образом с ним взаимодействовать.

NB: После закрытия окна приложение продолжит работать, так как мы не обработали сообщение закрытия окна. Не забудьте снять задачу в диспетчере задач

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

10. Полезные ссылки

Официальная документация Microsoft:

Наука
7 млн интересуются