Графический интерфейс 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):\
  • Это функция, которая вызывается при возникновении определенного события.

Цикл обработки событий:\

  1. Программа ожидает события (например, ввод пользователя).\
  2. Когда событие происходит, система отправляет его в очередь событий.\
  3. Программа извлекает событие из очереди и вызывает соответствующий обработчик.\
  4. Обработчик выполняет необходимые действия.

Примеры событий в графическом интерфейсе:\

  • 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}

Описание кода:

  1. WinMain: Это точка входа приложения. Здесь регистрируется класс окна, создается окно и запускается цикл обработки сообщений.
  2. WndProc: Это процедура окна, которая обрабатывает сообщения, отправляемые окну. В данном примере она обрабатывает сообщение WM_PAINT, которое отправляется, когда окно нужно перерисовать.
  3. 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}

Описание:

  1. Загрузка битмапа:
    • В обработчике WM_CREATE загружается битмап из файла example.bmp с помощью функции LoadImage.
    • Битмап сохраняется в глобальной переменной, связанной с окном, с использованием SetWindowLongPtr.
  2. Отображение битмапа:
    • В обработчике WM_PAINT создается контекст устройства в памяти (CreateCompatibleDC), и битмап отображается на экране с помощью BitBlt.

  1. Режимы наложения (ROP2):
    • Используется функция SetROP2 для изменения режима наложения:
      • R2_NOT: Инвертирование цвета.
      • R2_XORPEN: Исключающее ИЛИ.
      • R2_MERGEPEN: Объединение с цветом пера.
    • Эти режимы применяются к прямоугольнику, эллипсу и линии.
  2. Удаление битмапа:
    • В обработчике WM_DESTROY битмап удаляется с помощью DeleteObject.

Как использовать:

  1. Создайте файл example.bmp в той же директории, где находится исполняемый файл.
  2. Скомпилируйте и запустите программу.
  3. Вы увидите:
    • Загруженный битмап.
    • Прямоугольник, эллипс и линию, нарисованные с разными режимами наложения.

Примечания:

  • Если файл 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}

Описание кода:

  1. Двойная буферизация:
    • В обработчике WM_PAINT создается контекст устройства в памяти (hdcMem), в котором рисуется весь кадр.
    • После завершения рисования буфер копируется на экран с помощью BitBlt.
  2. Таймер:
    • В обработчике WM_CREATE устанавливается таймер, который срабатывает каждые 16 мс (примерно 60 кадров в секунду).
    • В обработчике WM_TIMER обновляется позиция круга и вызывается перерисовка окна с помощью InvalidateRect.

  1. Анимация:
    • Круг движется по экрану, меняя направление при столкновении с границами окна.
    • Позиция круга обновляется в обработчике WM_TIMER.
  2. Столкновение с границами:
    • Если круг достигает границы окна, его скорость по соответствующей оси инвертируется.

Как это работает:

  • Таймер обновляет позицию круга и вызывает перерисовку окна.
  • В 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}

Описание кода:

  1. Загрузка изображений:
    • В обработчике WM_CREATE загружаются три изображения:
      • Основное изображение (image.bmp).
      • Маска (mask.bmp).
      • Фоновое изображение (background.bmp).
    • Маска должна быть черно-белой: черный цвет (0x000000) обозначает прозрачные области, белый цвет (0xFFFFFF) — непрозрачные.

  1. Наложение изображений:
    • В обработчике WM_PAINT используется двойная буферизация для предотвращения мерцания.
    • Фоновое изображение рисуется на буфере.
    • Основное изображение накладывается на фон с использованием маски:
      • Сначала маска применяется к фону с помощью операции SRCAND (AND-операция). Это делает прозрачные области черными.
      • Затем основное изображение накладывается с помощью операции SRCPAINT (OR-операция). Это заполняет прозрачные области цветами из изображения.
  2. Освобождение ресурсов:
    • В обработчике WM_DESTROY удаляются загруженные изображения.

Как это работает:

  • Маска определяет, какие части изображения будут прозрачными.
  • Основное изображение накладывается на фон только в тех областях, где маска белая.
  • Черные области маски делают соответствующие части фона прозрачными.

Результат:

  • На фоновом изображении отображается основное изображение с прозрачными областями, заданными маской.

Примечания:

  1. Файлы изображений:
    • Создайте три файла: image.bmp, mask.bmp и background.bmp.
    • Маска (mask.bmp) должна быть черно-белой. Черный цвет (0x000000) — прозрачные области, белый цвет (0xFFFFFF) — непрозрачные.
  2. Пример изображений:
    • image.bmp: Основное изображение (например, спрайт).
    • mask.bmp: Черно-белая маска, соответствующая основному изображению.
    • background.bmp: Фоновое изображение.

  1. Компиляция:
    • Скомпилируйте код с помощью компилятора, поддерживающего WinAPI (например, MinGW или Visual Studio).
  2. Запуск:
    • Убедитесь, что файлы 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}
← Управление процессами в Windows ОС Unix →