Здравствуйте. Эта статья посвящена самому началу работы с Direct2D(Первая в серии, дальше будут продолжения), поскольку в документации от Microsoft мало что сказано о том, что вообще нужно сделать для создания окна и прочих вещей, а также слишком много «воды». Таким образом, это альтернатива началу документации от Microsoft.
Структура будет построена как ряд в��просов и ответов на них. Так как всё сводится к понятию окно, первым делом будет дано объяснение, что это такое в Windows.
Что такое окно в Windows?
Если просто, - как и всё остальное, - это N структура. Правильно заполнив, зарегистрировав и вызвав соответствующие функции, вы получите на экране белый прямоугольник определённого стиля.
Но прямого доступа к структуре окна мы не имеем, лишь к её части - структуре класса окна WNDCLASSEXW. Обратившись к MSDN, получим следующее:
typedef struct WNDCLASSEXW {
UINT cbSize;
UINT style;
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);
}
}Заполнение WNDCLASSEXW
Зная, что для регистрации нужна заполненная структура, произведём необходимое:
1.Размер, достаточно указать в значение результат sizeof(WNDCLASSEXW)
1.Стиль окна определяет как визуальное отображение, так и его поведение. Список стилей.
2.Ссылка на оконную процедуру (описана выше).
3. и 4. Название говорит само за себя, но четвёртый параметр - для каждого отдельного дочернего окна.
5. HInstance - это базовый адрес в памяти, по которому загружен исполняемый модуль (EXE или DLL); его значение передаётся через точку входа.
6. и 7. и 8. Названия говорят сами за себя.
9. То самое меню, которое отображается у программ.
10. Название класса окна в Unicode.
11. Иконка окна.
Минимальный пример заполнения:
WNDCLASSEXW wcew = { 0 };
wcew.cbSize = sizeof(WNDCLASSEXW); // Обязательно: размер структуры
wcew.lpfnWndProc = WindowProc; // Указатель на оконную процедуру
wcew.hInstance = hInstance; // Экземпляр приложения
wcew.hCursor = LoadCursor(NULL, IDC_ARROW); // Курсор - стрелка
wcew.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Фон - цвет окна
wcew.lpszClassName = L"MyWindowClass"; // Уникальное имя классаWindowProc и hInstance указаны как бы вне; полноценный пример будет позже.
Регистрация WNDCLASSEXW. Создание окна
Для регистрации достаточно вызвать RegisterClassExW, передав в аргументы ссылку на структуру WNDCLASSEXW, то есть: RegisterClassExW(&wcew).
Теперь создание окна происходит при помощи вызова функции CreateWindowExW, результатом которого является ID окна - HWND. Аргументы функции:
dwExStyle - дополнительный стиль окна, изменяющий поведение. Список;
lpClassName - тип создаваемого окна или элемента управления;
lpWindowName - название окна;
dwStyle - определяет внешний вид и поведение окна. Таблица(прокрутить вниз);
X - позиция окна по оси X;
Y - позиция окна по оси Y;
nWidth - ширина окна;
nHeight - высота окна;
hWndParent - родительское окно;
hMenu - меню или ID контрола;
hInstance - экземпляр приложения;
lpParam - дополнительные параметры (указатель на любые данные).
Зная все нужные параметры, можно просто написать:
HWND hwnd = CreateWindowExW(0, wcew.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 lpCmdLine, 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 lpCmdLine, 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;
}
На этом, по сути, и всё. Дерзайте! Далее будет рассказ о рисовании, отображении различного контента и т.п.