Здравствуйте. Эта статья посвящена самому началу работы с Direct2D(Первая в серии, дальше будут продолжения), поскольку в документации от Microsoft мало что сказано о том, что вообще нужно сделать для создания окна и прочих вещей, а также слишком много «воды». Таким образом, это альтернатива началу документации от Microsoft.
Структура будет построена как ряд вопросов и ответов на них. Так как всё сводится к понятию окно, первым делом будет дано объяснение, что это такое в Windows.
Что такое окно в Windows?
Если просто, - как и всё остальное, - это N структура. Правильно заполнив, зарегистрировав и вызвав соответствующие функции, вы получите на экране белый прямоугольник определённого стиля.
Но прямого доступа к структуре окна мы не имеем, лишь к её части - структуре класса окна _WNDCLASS. Обратившись к MSDN, получим следующее:
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). Ссылка подробней; в 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);
}
}
Заполнение _WNDCLASS
Зная, что для регистрации нужна заполненная структура, произведём необходимое:
1.Стиль окна определяет как визуальное отображение, так и его поведение. Список стилей.
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 указаны как бы вне; полноценный пример будет позже.
Регистрация _WNDCLASS. Создание окна
Для регистрации достаточно вызвать RegisterClassEx, передав в аргументы ссылку на структуру _WNDCLASS, то есть: RegisterClassEx(&wcex).
Теперь создание окна происходит при помощи вызова функции CreateWindowEx, результатом которого является ID окна - HWND. Аргументы функции:
-
dwExStyle - дополнительный стиль окна, изменяющий поведение. Список;
-
lpClassName - тип создаваемого окна или элемента управления;
-
lpWindowName - название окна;
-
dwStyle - определяет внешний вид и поведение окна. Таблица(прокрутить вниз);
-
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 - флаг отображения окна(Список. Прокрутить вниз). Передаётся от программы-инициатора. То есть если программа запускается с скрытым окном, то это поведение сохранится и для нового.
Приём и обработка сообщений, передача оконной процедуре. Финал
И сразу код:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
-
MSG - cодержит информацию о сообщении из очереди сообщений (описание).
-
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
