@20 – Объектно-ориентированное программирование
Так вот, то что сейчас будет происходить даже отдаленно не будет напоминать принципы ООП. Готовьтесь к функциональному программированию, в котором вы постоянно первым элементом будете передавать так называемый “объект” (на самом деле дескриптор окна).
У меня конечно не высший бал по IELTS/TOEFL, но переводить одно английское слово (handle) другим английским словом (descriptor), просто написав его кириллицей…
Не будем вводить и иы свою номенклатуру, поэтому оставим “дескриптор”
Все что начинается с h / H – handle, он же дескриптор.
Скоро вы узнаете насколько тщательно она это делает
Далее нам рассказывают про “архитектуру, управляемую событиями – @21” и “оконную процедуру – @22”
Из этих 2 тем необходимо понять следующее:
Оконная процедура – это функция, которая обрабатывает заранее определенные (заданные через #define) события.
Эта функция является callback (если в вашем бэграунде есть javascript или java, вам будет легче понять). Эту фунцию мы сами никогда не вызываем. Управление передается ей только при обработки очередного события в”очереди событий“. А эта очередь собирается из системных событий и событий, которые формирует пользователь, нажимая на всякие кнопочки.
WM_PAINT
WM_COMMAND
@23 – Оконные классы
Короче, чтобы создать окно мы сначала должны завести для него класс (прям ООП вырисовывается). В нем мы указываем название, title, расположение, размеры, видимость, наличие/отсутствие меню, оконную процедуру(это будет функция, которая будет обрабатывать сообщения для окон, созданных по этому классу)…
Есть несколько уже предопределенных классов (например BUTTON, EDIT, …
@24 – Цикл обработки сообщений
Этот цикл является стандартом и всегда находится в точке входа в программу
Грубо говоря, после инициализации всех окон запускается бесконечный цикл обработки сообщений приложением. Единственный способ завершить этот цикл и закрыть программу является передача сообщения WM_QUIT
@24 – Первая программа
1 2 3 4 5 6 |
#include <windows.h> int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox(NULL, "Hello, Win32 world", "Hello from Message Box", MB_OK); return 0; } |
В этой программе нету ни создания окна, ни цикла обработки сообщения. Вся фишка в функции MessageBox:
- Она создает предопределенное диалоговое окно
- Предопределенный класс содержит свою оконную функцию
- Для этой функции Windows создает невидимый цикл обработки сообщений
Я этот раздел не читал. То что Вам вначале нужно знать это то, что начало координат находится в верхнем левом углу, ось X направленная вправо, а ось Y вниз
@31 – Hello World
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
WinMain (стандартные аргументы) { Подготовить, зарегистрировать классы окон Создать экземпляр окна Запустить бесконечный цикл обработки очереди сообщений Выход из программы } WndProc (стандартный список аргументов) { Записываем гигантский switch..case, в котом обрабатываются все сообщения данного окна } |
@32 – Регистрация класса окна
В книге обязательно прочитайте, что означает каждое из них. Вообще советую вам сначала читать главу из книги, а мой текст воспринимать как краткий конспект.
@ 35 – Создание окна
Окно создается непосредственно при вызове функции CreateWindow. Этой функцией мы будем пользоваться довольно часто. Как минимум мы вызовем ее для создания нашего главное окна. А потом она мы будем использовать ее для создания предопределенных окон (например BUTTON, EDIT, STATIC… )
Основные поля
- lpClassName – имя зарегистрированного класса (при создание главного окна совпадает с тем, что мы ввели в wn.lpszClassName)
- x, y, nWidth, nHeight – размеры и координаты окна (no comments)
- hWndParent – для основного окна NULL, для дочерних придется присваивать descriptor основного
- hMenu – будем использовать исключительно с основным окном (а меню создадим при помощи ресурсов)
@39 – Отображение окна на экране
Вы думали, что создав окно оно сразу появиться на экране? Пффф… Вы плохо знаете мелко-мягких. Неее, вещь нужная. Я ее использовал в своем курсаче. Потом поймете зачем. Она очень простая, поэтому акцентироваться на ней не буду. Скажу только об одном интересном моменте: если мы зарегистрировали дочерние элементы в главном окне, а потом вызвали функцию ShowWindow для главного, то дочерние элементы тоже отобразятся.
@40 – Обработка сообщений
Стандартный цикл обработки сообщений. В моей программе он написан символ в символ. Прочитайте что он делает, чтобы знать для чего он и перейдем к самому интересному.
@42 – Оконная процедура
Вот мы и добрались до оконной процедуры, где вы будете проводить большую часть своей жизни много времени.
В этот раз для нас важны все параметры
- uMsg (у меня в проекте это message) – мы будем использовать, для того, чтобы определить какое именно сообщение мы обрабатываем: WM_PAINT или WM_COMMAND или еще что-то, что нам не интересно. Обязательна дефолтная обработка сообщений:
1 2 |
default: return DefWindowProc(hWnd, uMsg, wParam, lParam); |
Всего зарезервированы 1024 сообщения.
- wParam и lParam – используются для передачи дополнительной информации в сообщении. Для каждого сообщения придется читать документацию, чтобы знать что искать в этих параметрах. Чаще всего мы будем пользоваться первым wParam
Если сообщение обрабатывается, то оконная процедура обязана вернуть нулевое значение.
Для этого используется return 0;
@42 – Обработка сообщения WM_PAINT
При обработке этого сообщения мы будем перерисовывать недействительную часть окна. Это понятие очень важно. Немного расскажу о принципе:
- Рекомендую разделять логику и рисование в программе. Так вам будет легче создавать и, упаси Господь, сопровождать программу.
- Предположим в ответ на наше действие изменился текст(цвет, размер, координаты) какого-либо элемента (сейчас нам это не важно). По первому правилу, мы изменили текст (цвет…), а в WM_PAINT написали код для вывода нашего элемента. Так вот, пока мы не объявим, что какая-либо часть клиентского окна недействительна, WM_PAINT никогда не вызовется (есть несколько сообщений, которые по умолчанию генерируют сообщение WM_PAINT, например перемещение WM_MOVE, изменение размера окна WM_SIZE …).
- Забегая вперед скажу, что для того, чтобы объявить, что вся клиентская область недействительно (и как следствие перерисовать ее) это вызывать функцию InvalidateRect(hWnd, NULL, TRUE); после кода обработки сообщения, в котором мы поменяли текст (цвет…)
Стандартная структура обработки сообщения WM_PAINT:
1 2 3 4 5 6 7 |
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Код для отображения каких-либо элементов EndPaint(hWnd, &ps); break; |
@46 – Программа “Hello, World!” – второй вариант
Но так как мы пишем(задание у меня такое было) исключительно на C, то я предложу вам свой вариант структуры приложения
- WinMain – функция, с которой стартует программа
- Init – инициализация всех окон программы (кнопок, edit полей,…)
- Proc – функция(ии) для обработки сообщений от окна(окон)
- Дальше старайтесь раскидать функциональность по файлам