trailer_12511368665890

Доброго времени суток, дорогие читатели. Поздравляю всех православных с Рождеством. Для кого-то это праздник, а для меня это немного свободного времени, чтобы разгрести то, что я хотел написать.
А сегодня я буду писать об умирающей (я оптимист) технологии, а именно об Win32 Api. “Зачем я это делаю?” – спросите Вы. А ответ очень прост. Дело в том что совсем недавно я поступил в местный университет на специальность “инженер-программист” и сразу же столкнулся с этой технологией. Мне с моим хоть каким-то, но опытом, было довольно сложно разобраться в этой теме. Честно, я даже не представляю как, кто-нибудь, абсолютно не знакомый с языками программирования, сможет быстренько разобраться в этой теме, особенно, если этот “кто-то” – заочник. Вот для этих страдальцев я и пишу этот пост. Сразу скажу, что я изучал эту технологию ровно до того уровня, чтобы сделать курсовой проект. Не ожидайте здесь “лучших практик”, идеального кода, применения ООП. Готовьтесь к быдло-коду. Единственное, что у меня получилось, так это разбить свой проект на файлы. Да, все мое повествование будет приближено к курсовой работе и практически не будет отходить от нее. Для тех у кого будет совершенно другое задание примите мои самые искренние соболезнования, а этот пост можете почитать для входа в эту тему… Поехали

А поможет нам в нашем нелегком труде одна (единственная) книга Win32 Api. Разработка приложений для Window авторства Щупака Юрия Абрамовича. Есть две редакции данной книги: 2007 и 2008 года. Разница между ними по большому счету только в обложке (ну и еще 20 страниц). Так как я буду рассматривать только первые главы этой книги, то они абсолютно идентичны.
Номера страниц будут приводить по новой книге, так что ищите…

@20 – Объектно-ориентированное программирование

“Хотя формально операционная система Windows …”
Так вот, то что сейчас будет происходить даже отдаленно не будет напоминать принципы ООП. Готовьтесь к функциональному программированию, в котором вы постоянно первым элементом будете передавать так называемый “объект” (на самом деле дескриптор окна).
“Независимо от типа объекта .. ”
У меня конечно не высший бал по IELTS/TOEFL, но переводить одно английское слово (handle) другим английским словом (descriptor), просто написав его кириллицей…
Не будем вводить и иы свою номенклатуру, поэтому оставим “дескриптор”
Все что начинается с h / H – handle, он же дескриптор.
“Система Windows тщательно скрывает свои внутренние секреты…”
Скоро вы узнаете насколько тщательно она это делает

Далее нам рассказывают про “архитектуру, управляемую событиями – @21” и “оконную процедуру – @22
Из этих 2 тем необходимо понять следующее:
Оконная процедура – это функция, которая обрабатывает заранее определенные (заданные через #define) события.
Эта функция является callback (если в вашем бэграунде есть javascript или java, вам будет легче понять). Эту фунцию мы сами никогда не вызываем. Управление передается ей только при обработки очередного события в”очереди событий“. А эта очередь собирается из системных событий и событий, которые формирует пользователь, нажимая на всякие кнопочки.

Основные сообщения, которые мы будем обрабатывать:
WM_PAINT
WM_COMMAND

@23 – Оконные классы

Эта структура, определяющая основные характеристики окна.
Короче, чтобы создать окно мы сначала должны завести для него класс (прям ООП вырисовывается). В нем мы указываем название, title, расположение, размеры, видимость, наличие/отсутствие меню, оконную процедуру(это будет функция, которая будет обрабатывать сообщения для окон, созданных по этому классу)…
Есть несколько уже предопределенных классов (например BUTTON, EDIT, …
Для непосредственного создания окна, мы должны передать сформировавшийся класс функции createWindow

@24 – Цикл обработки сообщений

“Непременным компонентом всех Windows-приложений является цикл обработки сообщений…”
Этот цикл является стандартом и всегда находится в точке входа в программу
Грубо говоря, после инициализации всех окон запускается бесконечный цикл обработки сообщений приложением. Единственный способ завершить этот цикл и закрыть программу является передача сообщения WM_QUIT
Теории пока хватит, надо быстрее переходить к практике

@24 – Первая программа

WinMain – эквивалент main в консольных программах, написанных на C – так называемая “точка входа“. С нее начинается и в ней закончиться программа.
В этой программе нету ни создания окна, ни цикла обработки сообщения. Вся фишка в функции MessageBox:
  1. Она создает предопределенное диалоговое окно
  2. Предопределенный класс содержит свою оконную функцию
  3. Для этой функции Windows создает невидимый цикл обработки сообщений
Далее идет рассказ о координатах – @28
Я этот раздел не читал. То что Вам вначале нужно знать это то, что начало координат находится в верхнем левом углу, ось X направленная вправо, а ось Y вниз

@31 – Hello World

Мы готовы перейти к созданию полноценной программы со своими окнами и оконными процедурами. Основная идея:

Бессмысленно переписывать всю программу. Поэтому набирайте… разрабатываем пальчики. Кстати, насчет IDE. Я писал в Code::Blocks. Есть конечно CLion от JetBrains, но Вы замущаетесь с настройкой CMake. Вам хватит проблем и с Code::Blocks.
Пройдемся подробнее по этой программе, это поможет понять нам основные концепции работы с win api:

@32 – Регистрация класса окна

Начинается все с регистрации класса окна (заполняем структуру типа WNDCLASSEX)
В книге обязательно прочитайте, что означает каждое из них. Вообще советую вам сначала читать главу из книги, а мой текст воспринимать как краткий конспект.
Основное поле lpfnWndProc – указатель на функцию окна (сюда пишем название функции, которая будет обрабатывать сообщения для этого окна, в нашем примере это WndProc)

@ 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 или еще что-то, что нам не интересно. Обязательна дефолтная обработка сообщений:


Всего зарезервированы 1024 сообщения.

  • wParam и lParam – используются для передачи дополнительной информации в сообщении. Для каждого сообщения придется читать документацию, чтобы знать что искать в этих параметрах. Чаще всего мы будем пользоваться первым wParam

Если сообщение обрабатывается, то оконная процедура обязана вернуть нулевое значение.

Для этого используется return 0;

@42 – Обработка сообщения WM_PAINT

При обработке этого сообщения мы будем перерисовывать недействительную часть окна. Это понятие очень важно. Немного расскажу о принципе:

  1. Рекомендую разделять логику и рисование в программе. Так вам будет легче создавать и, упаси Господь, сопровождать программу.
  2. Предположим в ответ на наше действие изменился текст(цвет, размер, координаты) какого-либо элемента (сейчас нам это не важно). По первому правилу, мы изменили текст (цвет…), а в WM_PAINT написали код для вывода нашего элемента. Так вот, пока мы не объявим, что какая-либо часть клиентского окна недействительна, WM_PAINT никогда не вызовется (есть несколько сообщений, которые по умолчанию генерируют сообщение WM_PAINT, например перемещение WM_MOVE, изменение размера окна WM_SIZE …).
  3. Забегая вперед скажу, что для того, чтобы объявить, что вся клиентская область недействительно (и как следствие перерисовать ее) это вызывать функцию InvalidateRect(hWnd, NULL, TRUE); после кода обработки сообщения, в котором мы поменяли текст (цвет…)

Стандартная структура обработки сообщения WM_PAINT:

 

@46 – Программа “Hello, World!” – второй вариант

Дальше по книжке на предлагается воспользоваться преимуществом языка C++
Но так как мы пишем(задание у меня такое было) исключительно на C, то я предложу вам свой вариант структуры приложения
  1. WinMain – функция, с которой стартует программа
  2. Init – инициализация всех окон программы (кнопок, edit полей,…)
  3. Proc – функция(ии) для обработки сообщений от окна(окон)
  4. Дальше старайтесь раскидать функциональность по файлам