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