- PVSM.RU - https://www.pvsm.ru -
Здравствуйте. Эта статья посвящена самому началу работы с Direct2D(Первая в серии, дальше будут продолжения), поскольку в документации от Microsoft мало что сказано о том, что вообще нужно сделать для создания окна и прочих вещей, а также слишком много «воды». Таким образом, это альтернатива началу документации от Microsoft.
Структура будет построена как ряд вопросов и ответов на них. Так как всё сводится к понятию окно, первым делом будет дано объяснение, что это такое в Windows.
Если просто, - как и всё остальное, - это N структура. Правильно заполнив, зарегистрировав и вызвав соответствующие функции, вы получите на экране белый прямоугольник определённого стиля.
Но прямого доступа к структуре окна мы не имеем, лишь к её части - структуре класса окна _WNDCLASS. Обратившись к MSDN [1], получим следующее:
typedef struct _WNDCLASS {
UINT style; // Стили класса (CS_*)
WNDPROC lpfnWndProc; // Оконная процедура
int cbClsExtra; // Доп. память класса
int cbWndExtra; // Доп. память окна
HINSTANCE hInstance; // Экземпляр приложения
HICON hIcon; // Иконка
HCURSOR hCursor; // Курсор
HBRUSH hbrBackground; // Фон
LPCWSTR lpszMenuName; // Имя меню
LPCWSTR lpszClassName; // Имя класса
HICON hIconSm; // Маленькая иконка
} WNDCLASS;
Прежде чем начать заполнение структуры, важно рассказать об этих двух вещах. Каждое ваше действие в Windows - это сообщение. Сообщение обрабатывается оконной процедурой (функцией). Её синтаксис:
LRESULT CALLBACK WindowProc(
HWND hwnd, // Получатель (может отличаться от MSG.hwnd для дочерних окон)
UINT uMsg, // Идентификатор сообщения
WPARAM wParam, // Дополнительные данные
LPARAM lParam // Дополнительные данные
);
hwnd - это ID (идентификатор) окна; uMsg - число, по которому определяется тип сообщения (системные - от 0x0000 до 0x03FF, пользовательские, возникающие из-за действий пользователя, - от 0x0400 до 0x7FFF и от 0xC000 до 0xFFFF). Ссылка подробней [2]; в wParam и lParam передаются дополнительные данные (например, если uMsg == WM_MOUSEMOVE, то в wParam - состояние кнопок мыши, а в lParam - координаты x и y (младшее и старшее слово)). Список всех сообщений можно найти в документации. Пример функции:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
// Инициализация при создании окна
return 0;
case WM_PAINT:
// Обработка перерисовки
return 0;
case WM_DESTROY:
PostQuitMessage(0); //Отправляет сообщение WM_QUIT
return 0;
default:
// Сообщения, которые мы не обработали, передаем на обработку по умолчанию
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
Зная, что для регистрации нужна заполненная структура, произведём необходимое:
1.Стиль окна определяет как визуальное отображение, так и его поведение. Список стилей [3].
2.Ссылка на оконную процедуру (описана выше).
3. и 4. Название говорит само за себя, но четвёртый параметр - для каждого отдельного дочернего окна.
5. HInstance - это базовый адрес в памяти, по которому загружен исполняемый модуль (EXE или DLL); его значение передаётся через точку входа.
6. и 7. и 8. Названия говорят сами за себя.
9. То самое меню, которое отображается у программ.
10. Название окна (оно будет отображаться) в Unicode.
11. Иконка окна.
Минимальный пример заполнения:
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX); // Размер структуры (ОБЯЗАТЕЛЬНО!)
wcex.lpfnWndProc = WindowProc; // Указатель на оконную процедуру
wcex.hInstance = hInstance; // Экземпляр приложения
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // Курсор - стрелка
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Фон - цвет окна
wcex.lpszClassName = L"MinimalWindowClass"; // Уникальное имя класса
WindowProc и hInstance указаны как бы вне; полноценный пример будет позже.
Для регистрации достаточно вызвать RegisterClassEx, передав в аргументы ссылку на структуру _WNDCLASS, то есть: RegisterClassEx(&wcex).
Теперь создание окна происходит при помощи вызова функции CreateWindowEx, результатом которого является ID окна - HWND. Аргументы функции:
dwExStyle - дополнительный стиль окна, изменяющий поведение. Список [4];
lpClassName - тип создаваемого окна или элемента управления;
lpWindowName - название окна;
dwStyle - определяет внешний вид и поведение окна. Таблица [5](прокрутить вниз);
X - позиция окна по оси X;
Y - позиция окна по оси Y;
nWidth - ширина окна;
nHeight - высота окна;
hWndParent - родительское окно;
hMenu - меню или ID контрола;
hInstance - экземпляр приложения;
lpParam - дополнительные параметры (указатель на любые данные).
Зная все нужные параметры, можно просто написать:
HWND hwnd = CreateWindowEx(0, wc.lpszClassName, L"Minimal Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
400, 300, NULL, NULL, hInst, NULL);
Если при создании не указать стиль его видимости, то это необходимо сделать вручную через ShowWindow. Данная функция управляет отображением окна: первый аргумент - это ID окна (HWND), второй - тип видимости (список). Также чтобы не было проблем с отображением окна, вызывается функция UpdateWindow, которая немедленно перерисовывает окно и принимает один аргумент - ID окна. В итоге имеем следующее:
ShowWindow(hwnd,3);
UpdateWindow(hwnd);
До этого момента точка входа не рассматривалась, так как были более важные вещи. Теперь рассмотрим и её:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nCmdShow)
{
return 0;
}
WINAPI - это макрос, который определяет, как функция получает параметры (справа налево) и кто очищает стек (сама функ��ия).
hInstance - это ID приложения.
hPrev - ID предыдущего приложения; в x32 и x64 Windows не используется.
lpCmdLine - аргументы, указанные при запуске в командной строке (аргумент под индексом 0 не содержит названия программы).
int nCmdShow - флаг отображения окна(Список [6]. Прокрутить вниз). Передаётся от программы-инициатора. То есть если программа запускается с скрытым окном, то это поведение сохранится и для нового.
И сразу код:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
MSG - cодержит информацию о сообщении из очереди сообщений (описание [7]).
GetMessage - получение сообщения. Первый аргумент - ссылка на структуру; второй - ID окна (null - все окна текущего потока); третий аргумент - первое сообщение для фильтрации; четвёртый аргумент - последнее сообщение для фильтрации (0 - нет фильтра).
TranslateMessage - преобразует сообщения от клавиатуры в специальные сообщения; если сообщение не от клавиатуры - пропускает. Принимает ссылку на структуру сообщения.
DispatchMessage - передаёт структуру MSG оконной процедуре. Принимает ссылку на структуру сообщения.
Имея все пункты, соединяем воедино:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0); //Отправляет сообщение WM_QUIT
return 0;
case WM_PAINT:
{
return 0;
}
// Сообщения, которые мы не обработали, передаем на обработку по умолчанию
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
const wchar_t* CLASS_NAME = L"MyWindowClass";
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nCmdShow)
{
// 1. Заполнение структуры класса окна
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX); // Обязательно: размер структуры
wc.lpfnWndProc = WindowProc; // Указатель на оконную процедуру
wc.hInstance = hInstance; // Экземпляр приложения
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Курсор - стрелка
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Фон - цвет окна
wc.lpszClassName = CLASS_NAME; // Уникальное имя класса
// 2. Регистрация класса окна
RegisterClassEx(&wc));
// 3. Создание окна
HWND hwnd = CreateWindowEx(
0, // Расширенные стили
CLASS_NAME, // Имя класса окна
L"Test Window", // Заголовок окна
WS_OVERLAPPEDWINDOW, // Стиль окна
100, 100, // Позиция (x, y)
400, 300, // Размер (width, height)
NULL, // Родительское окно
NULL, // Меню
hInstance, // Экземпляр приложения
NULL // Дополнительные параметры
);
// 4. Показать и обновить окно
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 5. Цикл обработки сообщений
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0; // Код завершения из WM_QUIT
}
На этом, по сути, и всё. Дерзайте! Далее будет рассказ о рисовании, отображении различного контента и т.п.
Автор: Johnny_Depp
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/getting-started/435709
Ссылки в тексте:
[1] Обратившись к MSDN: https://learn.microsoft.com/ru-ru/windows/win32/api/winuser/ns-winuser-wndclassexa
[2] Ссылка подробней: https://learn.microsoft.com/ru-ru/windows/win32/winmsg/about-messages-and-message-queues
[3] Список стилей: https://learn.microsoft.com/ru-ru/windows/win32/winmsg/window-styles
[4] Список: https://learn.microsoft.com/ru-ru/windows/win32/winmsg/extended-window-styles
[5] Таблица: https://learn.microsoft.com/ru-ru/windows/win32/api/winuser/nf-winuser-createwindowexa
[6] Список: https://learn.microsoft.com/ru-ru/windows/win32/api/winuser/nf-winuser-showwindow
[7] описание: https://learn.microsoft.com/ru-ru/windows/win32/api/winuser/ns-winuser-msg
[8] Источник: https://habr.com/ru/articles/963446/?utm_source=habrahabr&utm_medium=rss&utm_campaign=963446
Нажмите здесь для печати.