ВГТУ
2025-02-24
Пользовательский интерфейс (UI) — это важная часть взаимодействия человека с компьютером. Он определяет, как пользователь воспринимает и использует программное обеспечение. В истории развития компьютеров сформировались две основные парадигмы интерфейсов: консольный интерфейс (CUI, Command Line Interface) и графический интерфейс (GUI, Graphical User Interface). Каждая из этих парадигм имеет свои особенности, преимущества и недостатки.
Консольный интерфейс — это текстовый способ взаимодействия пользователя с компьютером. Пользователь вводит команды с клавиатуры, а система выводит результаты в текстовом виде. Этот тип интерфейса был основным в ранних компьютерах (например, в системах с операционными системами Unix, MS-DOS).
Графический интерфейс — это визуальный способ взаимодействия пользователя с компьютером. Он использует графические элементы, такие как окна, кнопки, иконки и меню. GUI стал популярным с появлением операционных систем, таких как Windows, macOS и графических оболочек для Linux (например, GNOME, KDE).
Характеристика | CUI (Консольный интерфейс) | GUI (Графический интерфейс) |
---|---|---|
Удобство для новичков | Низкое | Высокое |
Ресурсоемкость | Низкая | Высокая |
Гибкость | Высокая | Ограниченная |
Автоматизация | Легко автоматизируется | Сложнее автоматизировать |
Визуализация | Текстовая | Графическая |
Современные системы часто сочетают в себе элементы CUI и GUI. Например, в операционных системах, таких как Windows и macOS, можно использовать командную строку (PowerShell, Terminal) вместе с графическим интерфейсом.
С развитием веб-технологий появились интерфейсы, которые работают в браузере. Они сочетают в себе удобство GUI и возможности удаленного доступа.
С развитием искусственного интеллекта и технологий распознавания речи появляются новые парадигмы взаимодействия, такие как голосовые помощники (Siri, Alexa).
Консольный и графический интерфейсы — это две основные парадигмы взаимодействия пользователя с компьютером. Каждая из них имеет свои сильные и слабые стороны, и выбор между ними зависит от конкретных задач и предпочтений пользователя. В современном мире эти парадигмы часто дополняют друг друга, обеспечивая гибкость и удобство в использовании технологий.
В операционных системах (ОС), таких как Windows, окна являются основными элементами графического интерфейса пользователя (GUI). Каждое окно принадлежит определенному классу, который определяет его поведение, внешний вид и функциональность. Класс окна — это шаблон, на основе которого создаются экземпляры окон.
Класс окна — это структура данных, которая содержит информацию о: - Процедуре окна (Window Procedure) — функция, обрабатывающая сообщения, отправляемые окну. - Стилях окна (Window Styles) — параметры, определяющие внешний вид и поведение окна (например, наличие рамки, заголовка, кнопок управления). - Иконке и курсоре — ресурсы, используемые окном. - Фоне окна — цвет или кисть, используемая для заливки фона. - Меню — меню по умолчанию, связанное с окном.
Класс окна регистрируется в системе перед созданием окна.
Операционные системы предоставляют набор предопределенных классов, которые можно использовать для создания стандартных окон. Примеры предопределенных классов в Windows: - BUTTON — класс для создания кнопок. - EDIT — класс для создания текстовых полей. - LISTBOX — класс для создания списков. - COMBOBOX — класс для создания комбинированных списков. - STATIC — класс для создания статических текстовых элементов.
Эти классы уже зарегистрированы в системе, и их можно использовать без дополнительной регистрации.
Если предопределенные классы не подходят, можно зарегистрировать собственный класс окна. Для этого используется функция RegisterClass
или RegisterClassEx
(в Windows). Пример:
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc; // Указатель на процедуру окна
wc.hInstance = hInstance; // Дескриптор экземпляра приложения
wc.lpszClassName = L"MyWindowClass"; // Имя класса
// Регистрация класса
RegisterClass(&wc);
После регистрации класса можно создавать окна на его основе.
Для получения информации об окне и его классе используются следующие функции: - GetWindowInfo — возвращает информацию об окне. - GetClassInfo — возвращает информацию о классе окна. - GetClassName — возвращает имя класса, к которому принадлежит окно.
Пример:
Некоторые параметры окна и класса можно изменить динамически: - SetWindowLong — изменяет атрибуты окна (например, стили). - SetClassLong — изменяет атрибуты класса (например, иконку или курсор).
Пример:
Рассмотрим пример создания окна с пользовательским классом:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MyWindowClass";
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, L"MyWindowClass", L"Мое окно", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 300, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Класс окна — это важная концепция в разработке графических интерфейсов. Понимание работы с классами окон позволяет создавать гибкие и функциональные приложения. Предопределенные классы упрощают разработку, а возможность регистрации пользовательских классов открывает широкие возможности для кастомизации.
Событийное управление (Event-Driven Programming) — это парадигма, при которой выполнение программы определяется событиями (например, клик мыши, нажатие клавиши, закрытие окна).
Основные понятия:
- Событие (Event):
- Это действие или изменение состояния, которое обрабатывается программой (например, нажатие кнопки).
- Обработчик событий (Event Handler):
- Это функция, которая вызывается при возникновении определенного события.
Цикл обработки событий:
1. Программа ожидает события (например, ввод пользователя).
2. Когда событие происходит, система отправляет его в очередь событий.
3. Программа извлекает событие из очереди и вызывает соответствующий обработчик.
4. Обработчик выполняет необходимые действия.
Примеры событий в графическом интерфейсе:
- Click
— клик мыши.
- KeyPress
— нажатие клавиши.
- Resize
— изменение размера окна.
- Close
— закрытие окна.
Преимущества событийного управления:
- Удобство для создания интерактивных приложений.
- Эффективное использование ресурсов (программа не занимает процессорное время в ожидании).
- Поддержка многозадачности.
#include <windows.h>
// Обработчик событий (Window Procedure)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0); // Завершение программы при закрытии окна
return 0;
case WM_PAINT:
// Отрисовка содержимого окна
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 50, 50, L"Hello, World!", 13);
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Регистрация класса окна
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MainWindowClass";
RegisterClass(&wc);
// Создание окна
HWND hwnd = CreateWindowEx(0, L"MainWindowClass", L"Hello, World!", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
// Цикл обработки событий
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Пример простого приложения на Win32, которое демонстрирует возможности библиотеки gdi32
, может включать в себя создание окна и рисование различных фигур и текста с использованием функций GDI (Graphics Device Interface). Вот пример такого приложения:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("GDIExample");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("GDI Example"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Рисуем прямоугольник
Rectangle(hdc, 50, 50, 200, 200);
// Рисуем эллипс
Ellipse(hdc, 250, 50, 400, 200);
// Рисуем линию
MoveToEx(hdc, 50, 250, NULL);
LineTo(hdc, 400, 250);
// Выводим текст
SetTextAlign(hdc, TA_CENTER);
TextOut(hdc, 225, 300, TEXT("Hello, GDI!"), 11);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
WM_PAINT
, которое отправляется, когда окно нужно перерисовать.Rectangle
рисует прямоугольник.Ellipse
рисует эллипс.MoveToEx
и LineTo
рисуют линию.TextOut
выводит текст на экран.Для компиляции этого кода вам понадобится компилятор, поддерживающий WinAPI, например, MinGW или Visual Studio. В Visual Studio можно создать новый проект Win32 Application и вставить этот код в файл main.cpp
.
После компиляции и запуска приложения вы увидите окно с прямоугольником, эллипсом, линией и текстом, нарисованными с использованием функций GDI.
Добавим в пример использование функций для работы с битмапами и различными режимами наложения (ROP2). Мы загрузим битмап из файла, отобразим его на экране и продемонстрируем различные режимы наложения с помощью функции SetROP2
.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("GDIExample");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("GDI Example with Bitmaps and ROP2"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
HBITMAP hBitmap;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
switch (message) {
case WM_CREATE:
// Загружаем битмап из файла
hBitmap = (HBITMAP)LoadImage(NULL, TEXT("example.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap == NULL) {
MessageBox(hwnd, TEXT("Не удалось загрузить битмап!"), TEXT("Ошибка"), MB_ICONERROR);
}
// Сохраняем битмап в глобальной переменной
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)hBitmap);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Получаем битмап из глобальной переменной
hBitmap = (HBITMAP)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (hBitmap != NULL) {
// Создаем контекст устройства в памяти
hdcMem = CreateCompatibleDC(hdc);
oldBitmap = SelectObject(hdcMem, hBitmap);
// Получаем информацию о битмапе
GetObject(hBitmap, sizeof(bitmap), &bitmap);
// Отображаем битмап на экране
BitBlt(hdc, 50, 50, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
// Демонстрация режимов наложения (ROP2)
SetROP2(hdc, R2_NOT); // Инвертирование цвета
Rectangle(hdc, 100, 100, 300, 200);
SetROP2(hdc, R2_XORPEN); // Исключающее ИЛИ
Ellipse(hdc, 150, 150, 350, 250);
SetROP2(hdc, R2_MERGEPEN); // Объединение с цветом пера
MoveToEx(hdc, 50, 300, NULL);
LineTo(hdc, 400, 300);
// Восстанавливаем контекст устройства
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
}
WM_CREATE
загружается битмап из файла example.bmp
с помощью функции LoadImage
.SetWindowLongPtr
.WM_PAINT
создается контекст устройства в памяти (CreateCompatibleDC
), и битмап отображается на экране с помощью BitBlt
.SetROP2
для изменения режима наложения:
R2_NOT
: Инвертирование цвета.R2_XORPEN
: Исключающее ИЛИ.R2_MERGEPEN
: Объединение с цветом пера.WM_DESTROY
битмап удаляется с помощью DeleteObject
.example.bmp
в той же директории, где находится исполняемый файл.example.bmp
отсутствует, программа выведет сообщение об ошибке.example.bmp
на любой другой битмап, чтобы увидеть, как он отображается.Для создания анимации движения объекта в окне с использованием двойной буферизации (double buffering) в Win32 API, мы будем использовать следующие техники: 1. Двойная буферизация: Рисование происходит в памяти (в буфере), а затем буфер копируется на экран. Это предотвращает мерцание. 2. Таймер: Для обновления позиции объекта и перерисовки окна через определенные интервалы времени.
В этом примере мы создадим анимацию движения круга по экрану.
#include <windows.h>
#include <math.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("AnimationExample");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Animation with Double Buffering"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static int x = 100, y = 100; // Начальные координаты круга
static int dx = 5, dy = 5; // Скорость движения по осям X и Y
static int radius = 50; // Радиус круга
switch (message) {
case WM_CREATE:
// Устанавливаем таймер для обновления анимации каждые 16 мс (~60 FPS)
SetTimer(hwnd, 1, 16, NULL);
return 0;
case WM_TIMER:
// Обновляем позицию круга
x += dx;
y += dy;
// Проверяем столкновение с границами окна
RECT rect;
GetClientRect(hwnd, &rect);
if (x - radius < 0 || x + radius > rect.right) {
dx = -dx; // Меняем направление по оси X
}
if (y - radius < 0 || y + radius > rect.bottom) {
dy = -dy; // Меняем направление по оси Y
}
// Перерисовываем окно
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Создаем контекст устройства в памяти для двойной буферизации
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
SelectObject(hdcMem, hBitmap);
// Очищаем фон
HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(hdcMem, &rect, hBrush);
// Рисуем круг
HBRUSH hCircleBrush = CreateSolidBrush(RGB(255, 0, 0)); // Красный круг
SelectObject(hdcMem, hCircleBrush);
Ellipse(hdcMem, x - radius, y - radius, x + radius, y + radius);
DeleteObject(hCircleBrush);
// Копируем буфер на экран
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
// Освобождаем ресурсы
DeleteObject(hBitmap);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
return 0;
}
WM_PAINT
создается контекст устройства в памяти (hdcMem
), в котором рисуется весь кадр.BitBlt
.WM_CREATE
устанавливается таймер, который срабатывает каждые 16 мс (примерно 60 кадров в секунду).WM_TIMER
обновляется позиция круга и вызывается перерисовка окна с помощью InvalidateRect
.WM_TIMER
.WM_PAINT
используется двойная буферизация для предотвращения мерцания.Для демонстрации наложения изображений с использованием маски в Win32 API, мы создадим пример, где два изображения накладываются друг на друга с использованием маски. Маска позволяет определить, какие части изображения будут прозрачными, а какие — непрозрачными. Это часто используется для создания спрайтов с прозрачным фоном.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("MaskedImageExample");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Masked Image Example"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static HBITMAP hBitmapImage = NULL; // Основное изображение
static HBITMAP hBitmapMask = NULL; // Маска
static HBITMAP hBitmapBackground = NULL; // Фоновое изображение
switch (message) {
case WM_CREATE:
// Загружаем изображения
hBitmapImage = (HBITMAP)LoadImage(NULL, TEXT("image.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hBitmapMask = (HBITMAP)LoadImage(NULL, TEXT("mask.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hBitmapBackground = (HBITMAP)LoadImage(NULL, TEXT("background.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!hBitmapImage || !hBitmapMask || !hBitmapBackground) {
MessageBox(hwnd, TEXT("Не удалось загрузить изображения!"), TEXT("Ошибка"), MB_ICONERROR);
}
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Получаем размеры клиентской области
RECT rect;
GetClientRect(hwnd, &rect);
// Создаем контекст устройства в памяти для двойной буферизации
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hBitmapBuffer = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
SelectObject(hdcMem, hBitmapBuffer);
// Рисуем фоновое изображение
HDC hdcBackground = CreateCompatibleDC(hdc);
SelectObject(hdcBackground, hBitmapBackground);
BitBlt(hdcMem, 0, 0, rect.right, rect.bottom, hdcBackground, 0, 0, SRCCOPY);
DeleteDC(hdcBackground);
// Рисуем изображение с маской
if (hBitmapImage && hBitmapMask) {
HDC hdcImage = CreateCompatibleDC(hdc);
HDC hdcMask = CreateCompatibleDC(hdc);
// Выбираем изображение и маску в контексты устройств
SelectObject(hdcImage, hBitmapImage);
SelectObject(hdcMask, hBitmapMask);
// Шаг 1: Наложение маски на фоновое изображение (AND-операция)
BitBlt(hdcMem, 100, 100, rect.right, rect.bottom, hdcMask, 0, 0, SRCAND);
// Шаг 2: Наложение изображения на результат (OR-операция)
BitBlt(hdcMem, 100, 100, rect.right, rect.bottom, hdcImage, 0, 0, SRCPAINT);
// Освобождаем контексты устройств
DeleteDC(hdcImage);
DeleteDC(hdcMask);
}
// Копируем буфер на экран
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
// Освобождаем ресурсы
DeleteObject(hBitmapBuffer);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
return 0;
}
WM_CREATE
загружаются три изображения:
image.bmp
).mask.bmp
).background.bmp
).WM_PAINT
используется двойная буферизация для предотвращения мерцания.SRCAND
(AND-операция). Это делает прозрачные области черными.SRCPAINT
(OR-операция). Это заполняет прозрачные области цветами из изображения.WM_DESTROY
удаляются загруженные изображения.image.bmp
, mask.bmp
и background.bmp
.mask.bmp
) должна быть черно-белой. Черный цвет (0x000000) — прозрачные области, белый цвет (0xFFFFFF) — непрозрачные.image.bmp
: Основное изображение (например, спрайт).mask.bmp
: Черно-белая маска, соответствующая основному изображению.background.bmp
: Фоновое изображение.image.bmp
, mask.bmp
и background.bmp
находятся в той же директории, что и исполняемый файл.Ресурсы в Windows-приложениях — это данные, которые хранятся внутри исполняемых файлов (EXE, DLL) или в отдельных файлах и используются программой во время выполнения. Ресурсы позволяют включать в приложение такие элементы, как иконки, строки, диалоговые окна, меню, курсоры, изображения и другие данные, не загромождая основной код программы.
Ресурсы в Windows делятся на несколько типов, каждый из которых имеет своё назначение:
Разработчик может создавать собственные типы ресурсов для хранения любых данных, например, конфигурационных файлов, XML-документов или бинарных данных.
Ресурсы в Windows создаются с помощью файлов ресурсов (.rc
), которые компилируются в бинарный формат и включаются в исполняемый файл.
.rc
)Файл ресурсов — это текстовый файл, который описывает все ресурсы, используемые в приложении. Пример простого .rc
файла:
// Иконка приложения
IDI_ICON1 ICON "icon.ico"
// Курсор
IDC_CURSOR1 CURSOR "cursor.cur"
// Строковая таблица
STRINGTABLE
BEGIN
101 "Привет, мир!"
102 "Это пример строки."
END
// Диалоговое окно
IDD_DIALOG1 DIALOGEX 0, 0, 200, 100
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
CAPTION "Пример диалога"
BEGIN
DEFPUSHBUTTON "OK", IDOK, 10, 70, 50, 14
PUSHBUTTON "Отмена", IDCANCEL, 70, 70, 50, 14
END
Файл ресурсов компилируется с помощью компилятора ресурсов (rc.exe
), который создает бинарный файл (.res
). Этот файл затем включается в исполняемый файл на этапе линковки.
Пример команды для компиляции:
Ресурсы загружаются и используются в коде программы с помощью API Windows.
Пример загрузки иконки:
Пример загрузки строки из строковой таблицы:
Пример создания диалогового окна:
Пример загрузки изображения:
Ресурсы в Windows могут быть как встроенными (внутри EXE или DLL), так и внешними (отдельные файлы). Встроенные ресурсы защищены от изменений, но их сложнее обновлять. Внешние ресурсы проще обновлять, но они менее безопасны.
Хранятся внутри исполняемого файла.
Загружаются с помощью функций API, таких как LoadIcon
, LoadString
, LoadBitmap
.
Пример:
Хранятся в отдельных файлах.
Загружаются с помощью функций, таких как LoadImage
, LoadLibrary
(для DLL).
Пример:
Локализация — это процесс адаптации приложения для разных языков и регионов. В Windows локализация ресурсов достигается за счет создания отдельных файлов ресурсов для каждого языка.
Создайте отдельный .rc
файл для каждого языка.
Используйте директиву LANGUAGE
для указания языка и кодовой страницы.
Пример:
Используйте функции API, такие как LoadString
, для загрузки строк на нужном языке.
Пример:
Рассмотрим пример простого Windows-приложения, которое использует ресурсы:
app.rc
)main.c
)#include <windows.h>
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hwndDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
return 0;
}
Меню — это один из основных элементов пользовательского интерфейса в Windows-приложениях. Оно предоставляет пользователю доступ к функциям программы через иерархическую структуру команд. Меню может быть главным (располагаться в верхней части окна), контекстным (появляться при щелчке правой кнопкой мыши) или всплывающим (popup).
Главное меню располагается в верхней части окна под заголовком. Оно содержит основные команды приложения, такие как “Файл”, “Правка”, “Вид” и т.д.
Контекстное меню появляется при щелчке правой кнопкой мыши на определённом элементе интерфейса. Оно содержит команды, актуальные для текущего контекста.
Всплывающее меню — это меню, которое появляется в определённом месте экрана, например, при нажатии на кнопку.
Меню в Windows-приложениях создается с помощью файла ресурсов (.rc
) или программно в коде.
Пример создания главного меню в файле ресурсов:
// Файл ресурсов (app.rc)
IDR_MAINMENU MENU
BEGIN
POPUP "&Файл"
BEGIN
MENUITEM "&Открыть", ID_FILE_OPEN
MENUITEM "&Сохранить", ID_FILE_SAVE
MENUITEM SEPARATOR
MENUITEM "В&ыход", ID_FILE_EXIT
END
POPUP "&Правка"
BEGIN
MENUITEM "&Вырезать", ID_EDIT_CUT
MENUITEM "&Копировать", ID_EDIT_COPY
MENUITEM "В&ставить", ID_EDIT_PASTE
END
POPUP "&Справка"
BEGIN
MENUITEM "&О программе", ID_HELP_ABOUT
END
END
Пример создания меню в коде:
HMENU hMenu = CreateMenu();
HMENU hFileMenu = CreatePopupMenu();
HMENU hEditMenu = CreatePopupMenu();
AppendMenu(hFileMenu, MF_STRING, ID_FILE_OPEN, "&Открыть");
AppendMenu(hFileMenu, MF_STRING, ID_FILE_SAVE, "&Сохранить");
AppendMenu(hFileMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hFileMenu, MF_STRING, ID_FILE_EXIT, "В&ыход");
AppendMenu(hEditMenu, MF_STRING, ID_EDIT_CUT, "&Вырезать");
AppendMenu(hEditMenu, MF_STRING, ID_EDIT_COPY, "&Копировать");
AppendMenu(hEditMenu, MF_STRING, ID_EDIT_PASTE, "В&ставить");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, "&Файл");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hEditMenu, "&Правка");
SetMenu(hwnd, hMenu);
Команды меню обрабатываются в оконной процедуре (WndProc
) с помощью сообщения WM_COMMAND
.
Пример обработки команд:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_OPEN:
MessageBox(hwnd, "Команда: Открыть", "Меню", MB_OK);
break;
case ID_FILE_SAVE:
MessageBox(hwnd, "Команда: Сохранить", "Меню", MB_OK);
break;
case ID_FILE_EXIT:
PostQuitMessage(0);
break;
case ID_EDIT_CUT:
MessageBox(hwnd, "Команда: Вырезать", "Меню", MB_OK);
break;
case ID_EDIT_COPY:
MessageBox(hwnd, "Команда: Копировать", "Меню", MB_OK);
break;
case ID_EDIT_PASTE:
MessageBox(hwnd, "Команда: Вставить", "Меню", MB_OK);
break;
case ID_HELP_ABOUT:
MessageBox(hwnd, "О программе", "Меню", MB_OK);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Контекстное меню создается аналогично главному меню, но отображается при щелчке правой кнопкой мыши.
Пример создания и отображения контекстного меню:
Состояние пунктов меню (активен/неактивен, отмечен/не отмечен) можно изменять динамически.
Пример:
EnableMenuItem(hMenu, ID_FILE_SAVE, MF_GRAYED); // Деактивировать пункт
EnableMenuItem(hMenu, ID_FILE_SAVE, MF_ENABLED); // Активировать пункт
Пример:
Рассмотрим пример простого Windows-приложения с главным меню.
app.rc
)IDR_MAINMENU MENU
BEGIN
POPUP "&Файл"
BEGIN
MENUITEM "&Открыть", ID_FILE_OPEN
MENUITEM "&Сохранить", ID_FILE_SAVE
MENUITEM SEPARATOR
MENUITEM "В&ыход", ID_FILE_EXIT
END
POPUP "&Правка"
BEGIN
MENUITEM "&Вырезать", ID_EDIT_CUT
MENUITEM "&Копировать", ID_EDIT_COPY
MENUITEM "В&ставить", ID_EDIT_PASTE
END
END
main.c
)#include <windows.h>
#define ID_FILE_OPEN 101
#define ID_FILE_SAVE 102
#define ID_FILE_EXIT 103
#define ID_EDIT_CUT 201
#define ID_EDIT_COPY 202
#define ID_EDIT_PASTE 203
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_OPEN:
MessageBox(hwnd, "Открыть файл", "Меню", MB_OK);
break;
case ID_FILE_SAVE:
MessageBox(hwnd, "Сохранить файл", "Меню", MB_OK);
break;
case ID_FILE_EXIT:
PostQuitMessage(0);
break;
case ID_EDIT_CUT:
MessageBox(hwnd, "Вырезать", "Меню", MB_OK);
break;
case ID_EDIT_COPY:
MessageBox(hwnd, "Копировать", "Меню", MB_OK);
break;
case ID_EDIT_PASTE:
MessageBox(hwnd, "Вставить", "Меню", MB_OK);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "MyWindowClass";
RegisterClass(&wc);
HWND hwnd = CreateWindow("MyWindowClass", "Окно с меню", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Диалоговые окна — это специальные окна, которые используются для взаимодействия с пользователем. Они могут отображать информацию, запрашивать данные или предоставлять выбор опций. Диалоговые окна бывают двух типов: модальные и немодальные.
Диалоговые окна создаются с помощью файла ресурсов (.rc
) или программно в коде.
Пример создания диалогового окна в файле ресурсов:
// Файл ресурсов (app.rc)
IDD_DIALOG1 DIALOGEX 0, 0, 200, 100
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Пример диалога"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK", IDOK, 10, 70, 50, 14
PUSHBUTTON "Отмена", IDCANCEL, 70, 70, 50, 14
LTEXT "Введите текст:", IDC_STATIC, 10, 10, 80, 10
EDITTEXT IDC_EDIT1, 10, 25, 180, 14
END
Пример создания диалога в коде:
Обработка диалогового окна выполняется в специальной процедуре диалога (DialogProc
). Эта процедура обрабатывает сообщения, такие как нажатие кнопок или ввод текста.
Пример процедуры диалога:
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG:
// Инициализация диалога
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK: {
char text[100];
GetDlgItemText(hwndDlg, IDC_EDIT1, text, sizeof(text));
MessageBox(hwndDlg, text, "Введенный текст", MB_OK);
EndDialog(hwndDlg, IDOK);
return TRUE;
}
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
Модальные диалоги блокируют взаимодействие с родительским окном до закрытия. Для отображения модального диалога используется функция DialogBox
.
Пример:
Немодальные диалоги позволяют пользователю взаимодействовать с родительским окном. Для отображения немодального диалога используется функция CreateDialog
.
Пример:
Диалоговые окна содержат элементы управления, такие как кнопки, текстовые поля, списки и т.д. Для работы с этими элементами используются функции API, такие как GetDlgItemText
, SetDlgItemText
, SendDlgItemMessage
.
Пример:
Пример:
Пример обработки нажатия кнопки:
Рассмотрим пример программы, которая отображает модальное диалоговое окно с текстовым полем и кнопками.
app.rc
)IDD_DIALOG1 DIALOGEX 0, 0, 200, 100
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Пример диалога"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK", IDOK, 10, 70, 50, 14
PUSHBUTTON "Отмена", IDCANCEL, 70, 70, 50, 14
LTEXT "Введите текст:", IDC_STATIC, 10, 10, 80, 10
EDITTEXT IDC_EDIT1, 10, 25, 180, 14
END
main.c
)#include <windows.h>
#define IDC_EDIT1 101
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK: {
char text[100];
GetDlgItemText(hwndDlg, IDC_EDIT1, text, sizeof(text));
MessageBox(hwndDlg, text, "Введенный текст", MB_OK);
EndDialog(hwndDlg, IDOK);
return TRUE;
}
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
return 0;
}
Обработка пользовательского ввода — это одна из ключевых задач при разработке приложений. Пользовательский ввод может поступать от различных устройств, таких как клавиатура, мышь, сенсорный экран, и даже от голосовых команд. В Windows для обработки пользовательского ввода используется API, который предоставляет функции и сообщения для работы с этими устройствами.
Клавиатурный ввод обрабатывается с помощью сообщений WM_KEYDOWN
, WM_KEYUP
, WM_CHAR
и других.
WM_KEYDOWN
: генерируется при нажатии клавиши.
WM_KEYUP
: генерируется при отпускании клавиши.
WM_CHAR
: генерируется при вводе символа (с учетом раскладки клавиатуры).Пример обработки клавиатурного ввода:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN:
MessageBox(hwnd, "Клавиша нажата", "Клавиатура", MB_OK);
break;
case WM_CHAR:
if (wParam == 'A') {
MessageBox(hwnd, "Нажата клавиша 'A'", "Клавиатура", MB_OK);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
WPARAM
: виртуальный код клавиши (Virtual Key Code). Например, VK_SPACE
для пробела, VK_RETURN
для Enter.LPARAM
: содержит дополнительную информацию, такую как:
Пример обработки WM_KEYDOWN
:
GetAsyncKeyState
: проверяет состояние клавиши (нажата/не нажата).GetKeyboardState
: получает состояние всех клавиш клавиатуры.Пример использования GetAsyncKeyState
:
Мышиный ввод обрабатывается с помощью сообщений, таких как WM_MOUSEMOVE
, WM_LBUTTONDOWN
, WM_RBUTTONDOWN
и других.
WM_MOUSEMOVE
: генерируется при перемещении мыши.WM_LBUTTONDOWN
: генерируется при нажатии левой кнопки мыши.WM_RBUTTONDOWN
: генерируется при нажатии правой кнопки мыши.WM_MOUSEWHEEL
: генерируется при прокрутке колеса мыши.Пример обработки мышиного ввода:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_LBUTTONDOWN:
MessageBox(hwnd, "Левая кнопка мыши нажата", "Мышь", MB_OK);
break;
case WM_RBUTTONDOWN:
MessageBox(hwnd, "Правая кнопка мыши нажата", "Мышь", MB_OK);
break;
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
char buffer[50];
sprintf(buffer, "Мышь: X=%d, Y=%d", x, y);
SetWindowText(hwnd, buffer);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
WPARAM
: состояние клавиш-модификаторов (Ctrl, Shift, Alt) и кнопок мыши.
MK_LBUTTON
: нажата левая кнопка мыши.MK_RBUTTON
: нажата правая кнопка мыши.MK_MBUTTON
: нажата средняя кнопка мыши.MK_CONTROL
: нажата клавиша Ctrl.MK_SHIFT
: нажата клавиша Shift.LPARAM
: координаты курсора (X и Y) относительно верхнего левого угла клиентской области окна.
LOWORD(lParam)
: координата X.
HIWORD(lParam)
: координата Y.Пример обработки WM_MOUSEMOVE
:
WM_MOUSEWHEEL
WPARAM
:
HIWORD(wParam)
: значение прокрутки (положительное — вверх, отрицательное — вниз).LOWORD(wParam)
: состояние клавиш-модификаторов.LPARAM
: координаты курсора (X и Y) в экранных координатах.Пример обработки WM_MOUSEWHEEL
:
GetCursorPos
: получает текущие координаты курсора.SetCursorPos
: устанавливает позицию курсора.Пример использования GetCursorPos
:
Сенсорный ввод обрабатывается с помощью сообщений, таких как WM_TOUCH
, и функций, таких как GetTouchInputInfo
.
WM_TOUCH
: генерируется при касании экрана.Параметры сообщения WM_TOUCH
: - WPARAM
: количество точек касания. - LPARAM
: дескриптор структуры HTOUCHINPUT
, содержащей информацию о касаниях.
Пример обработки сенсорного ввода:
case WM_TOUCH: {
UINT inputCount = LOWORD(wParam);
HTOUCHINPUT hTouchInput = (HTOUCHINPUT)lParam;
TOUCHINPUT* pTouchInputs = new TOUCHINPUT[inputCount];
if (GetTouchInputInfo(hTouchInput, inputCount, pTouchInputs, sizeof(TOUCHINPUT))) {
for (UINT i = 0; i < inputCount; i++) {
int x = pTouchInputs[i].x / 100;
int y = pTouchInputs[i].y / 100;
char buffer[50];
sprintf(buffer, "Касание: X=%d, Y=%d", x, y);
MessageBox(hwnd, buffer, "Сенсорный ввод", MB_OK);
}
}
delete[] pTouchInputs;
CloseTouchInputHandle(hTouchInput);
break;
}
Голосовой ввод может быть обработан с помощью API распознавания речи, такого как Windows Speech Recognition или Microsoft Speech API (SAPI).
Пример инициализации распознавания речи:
#include <sapi.h>
ISpRecognizer* pRecognizer = NULL;
CoInitialize(NULL);
CoCreateInstance(CLSID_SpSharedRecognizer, NULL, CLSCTX_ALL, IID_ISpRecognizer, (void**)&pRecognizer);
ISpRecoContext* pRecoContext = NULL;
pRecognizer->CreateRecoContext(&pRecoContext);
pRecoContext->SetNotifyWindowMessage(hwnd, WM_USER, 0, 0);
pRecoContext->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));
ISpRecoGrammar* pGrammar = NULL;
pRecoContext->CreateGrammar(0, &pGrammar);
pGrammar->LoadDictation(NULL, SPLO_STATIC);
pGrammar->SetDictationState(SPRS_ACTIVE);
Рассмотрим пример программы, которая обрабатывает клавиатурный и мышиный ввод.
main.c
)#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN:
MessageBox(hwnd, "Клавиша нажата", "Клавиатура", MB_OK);
break;
case WM_CHAR:
if (wParam == 'A') {
MessageBox(hwnd, "Нажата клавиша 'A'", "Клавиатура", MB_OK);
}
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd, "Левая кнопка мыши нажата", "Мышь", MB_OK);
break;
case WM_RBUTTONDOWN:
MessageBox(hwnd, "Правая кнопка мыши нажата", "Мышь", MB_OK);
break;
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
char buffer[50];
sprintf(buffer, "Мышь: X=%d, Y=%d", x, y);
SetWindowText(hwnd, buffer);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "MyWindowClass";
RegisterClass(&wc);
HWND hwnd = CreateWindow("MyWindowClass", "Обработка ввода", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}