Написать простое приложение для windows

Как создать программу на ПК: пошаговое руководство

Пройдите тест, узнайте какой профессии подходите

Работать самостоятельно и не зависеть от других

Работать в команде и рассчитывать на помощь коллег

Организовывать и контролировать процесс работы

Введение в программирование

Программирование — это процесс создания программ, которые выполняют определенные задачи на компьютере. Программы могут быть самыми разными: от простых калькуляторов до сложных систем управления базами данных. Если вы новичок и хотите научиться создавать программы на ПК, то вы попали по адресу. В этой статье мы рассмотрим основные шаги, которые помогут вам начать путь в программировании.

Программирование открывает перед вами множество возможностей. Вы сможете автоматизировать рутинные задачи, создавать собственные приложения и даже разрабатывать игры. Важно понимать, что программирование — это не только написание кода, но и решение задач, логическое мышление и постоянное обучение. Начать может быть сложно, но с правильным подходом и упорством вы сможете достичь успеха.

Кинга Идем в IT: пошаговый план для смены профессии

Выбор языка программирования

Первый шаг в создании программы — это выбор языка программирования. Существует множество языков, каждый из которых имеет свои особенности и области применения. Вот несколько популярных языков для начинающих:

  • Python: Отличный выбор для новичков благодаря простому синтаксису и широкому сообществу. Подходит для веб-разработки, анализа данных и автоматизации задач.
  • JavaScript: Основной язык для веб-разработки. Позволяет создавать интерактивные веб-страницы и приложения.
  • Java: Популярен в корпоративной среде и для разработки мобильных приложений на Android.
  • C#: Используется для разработки приложений на платформе .NET, включая игры на Unity.

Как выбрать язык программирования?

Выбор языка зависит от ваших целей и интересов. Если вы хотите быстро увидеть результаты своей работы, начните с Python или JavaScript. Если вас интересует разработка мобильных приложений, обратите внимание на Java или Kotlin. Для создания игр подойдет C#.

Важно также учитывать, что каждый язык программирования имеет свое сообщество и ресурсы для обучения. Например, для Python существует множество бесплатных курсов, книг и форумов, где вы можете получить помощь. JavaScript активно используется в веб-разработке, и вы найдете множество онлайн-ресурсов и уроков по созданию веб-приложений. Java и C# также имеют обширные сообщества и множество учебных материалов.

Установка и настройка среды разработки

После выбора языка программирования необходимо установить среду разработки (IDE). IDE — это программное обеспечение, которое облегчает написание, отладку и тестирование кода. Вот несколько популярных IDE для разных языков:

  • Python: PyCharm, Visual Studio Code
  • JavaScript: Visual Studio Code, WebStorm
  • Java: IntelliJ IDEA, Eclipse
  • C#: Visual Studio

Установка Visual Studio Code

Visual Studio Code (VS Code) — это бесплатная и мощная IDE, которая поддерживает множество языков программирования. Вот как установить VS Code:

  1. Перейдите на официальный сайт Visual Studio Code.
  2. Скачайте установочный файл для вашей операционной системы (Windows, macOS, Linux).
  3. Запустите установочный файл и следуйте инструкциям на экране.

После установки VS Code, вы можете установить расширения для поддержки выбранного языка программирования. Например, для Python установите расширение «Python», для JavaScript — «JavaScript (ES6) code snippets».

Кроме того, VS Code предлагает множество других полезных расширений, таких как линтеры для проверки синтаксиса, автодополнение кода и интеграция с системами контроля версий, такими как Git. Все это делает работу с кодом более удобной и продуктивной.

Создание первой программы

Теперь, когда у вас установлена среда разработки, можно приступить к созданию первой программы. Давайте начнем с простого примера на Python — программы, которая выводит «Hello, World!».

Пример программы на Python

  1. Откройте Visual Studio Code.
  2. Создайте новый файл и сохраните его с расширением .py (например, hello.py).
  3. Введите следующий код:

  4. Сохраните файл и запустите его, нажав правой кнопкой мыши на файле и выбрав «Run Python File in Terminal».

Вы должны увидеть вывод «Hello, World!» в терминале. Поздравляем, вы только что создали свою первую программу! 🎉

Пример программы на JavaScript

Если вы выбрали JavaScript, вот аналогичный пример:

  1. Создайте новый файл и сохраните его с расширением .js (например, hello.js).
  2. Введите следующий код:

  3. Сохраните файл и запустите его в терминале, введя команду node hello.js.

Вы увидите тот же результат — «Hello, World!» в терминале.

Создание первой программы — это важный шаг, который показывает, что вы можете написать код, который выполняет определенные действия. Это также дает вам уверенность и мотивацию продолжать изучение программирования.

Отладка и тестирование

Создание программы — это только начало. Важно также уметь отлаживать и тестировать свой код, чтобы убедиться, что он работает правильно.

Отладка кода

Отладка — это процесс поиска и исправления ошибок в коде. В большинстве IDE есть встроенные инструменты для отладки. Например, в Visual Studio Code вы можете установить точки останова (breakpoints) и пошагово выполнять код, чтобы понять, где возникают ошибки.

Отладка помогает вам понять, как работает ваш код и где могут возникать проблемы. Вы можете использовать точки останова, чтобы остановить выполнение программы в определенных местах и проверить значения переменных. Это особенно полезно, когда вы работаете с более сложными программами и алгоритмами.

Тестирование

Тестирование помогает убедиться, что ваша программа работает правильно в различных сценариях. Существует несколько видов тестирования:

  • Модульное тестирование: Проверка отдельных частей кода (функций, методов).
  • Интеграционное тестирование: Проверка взаимодействия между различными частями программы.
  • Системное тестирование: Проверка всей программы в целом.

Для автоматизации тестирования можно использовать специальные библиотеки и фреймворки. Например, для Python это unittest или pytest, для JavaScript — Jest или Mocha.

Тестирование помогает выявить ошибки и проблемы на ранних стадиях разработки, что позволяет избежать серьезных проблем в будущем. Автоматизация тестирования делает этот процесс более эффективным и позволяет быстро проверять изменения в коде.

Заключение

Создание программы на ПК — это увлекательный и полезный навык, который открывает множество возможностей. В этой статье мы рассмотрели основные шаги, которые помогут вам начать путь в программировании: выбор языка, установка среды разработки, создание первой программы, отладка и тестирование.

Программирование — это не только технический навык, но и творческий процесс. Вы можете создавать что-то новое, решать интересные задачи и делиться своими проектами с миром. Начните с малого, постепенно усложняйте свои проекты и не бойтесь экспериментировать.

Надеемся, что эти советы помогут вам сделать первые шаги в мире программирования. Удачи! 🚀

Читайте также

Все способы:

  • Создаем собственное программное обеспечение для Windows
  • Способ 1: Программы для написания программ
  • Способ 2: Язык программирования и среда разработки
  • Вопросы и ответы: 16

Ежедневно каждый активный пользователь компьютера сталкивается с работой в разных программах. Они призваны облегчить работу за ПК и выполняют определенный ряд функций. Например, калькулятор подсчитывает заданные примеры, в текстовом редакторе вы создаете документы любой сложности, а через плеер просматриваете любимые фильмы или слушаете музыку. Весь этот софт был создан с помощью языков программирования, начиная от основных элементов управления, и заканчивая графическим интерфейсом. Сегодня мы бы хотели обсудить два метода собственноручного написания простых приложений для операционной системы Windows.

Создаем собственное программное обеспечение для Windows

Сейчас разработать свою программу можно и без знания языков программирования, однако для этого существует совсем мало подходящих средств, позволяющих в полной мере реализовать задуманное. К тому же сейчас на просторах интернета бесплатно доступно множество курсов по ЯП, описывающих примеры написания софта с предоставлением исходного кода. Поэтому поставленная задача вполне реализуема, нужно лишь выбрать метод, что мы и предлагаем сделать далее.

Способ 1: Программы для написания программ

Если вы интересовались созданием игр, то знаете о специальных инструментах, куда уже встроено множество компонентов и записаны основные скрипты. Юзеру остается лишь создать из этого цельную картину, систематизируя имеющиеся данные. Примерно по такому же принципу работает и ПО, позволяющее создавать собственные приложения без знания языков программирования. За пример мы взяли HiAsm, поскольку это единственное оптимальное решение с полной локализацией на русский язык.

Скачать HiAsm Studio с официального сайта

  1. Сразу приступим к рассмотрению простой инструкции по созданию примитивной программы в HiAsm. Для начала перейдите по указанной выше ссылке, чтобы скачать и установить используемый сегодня инструмент.
  2. После запуска ознакомьтесь с представленной информацией по использованию и решению частых проблем. Сразу хотим отметить, что некоторые антивирусы распознают HiAsm как вредоносный код, блокируя запускающиеся файлы. Поэтому при возникновении неполадок рекомендуем добавить инструмент в исключения или на время выключать защиту операционной системы.
  3. Инструкция по использованию программного обеспечения HiAsm Studio

  4. Через меню «Файл» создайте новый проект.
  5. Переход к созданию нового проекта в программе HiAsm Studio

  6. Появится новое окно с выбором различных типов приложений. Сегодня мы хотим сконцентрироваться на стандартной программе для Windows с графическим интерфейсом.
  7. Выбор типа приложения для написания в программе HiAsm Studio

  8. За пример возьмем простое электронное меню с выбором блюд через всплывающий список, а также с возможностью указания количества необходимых порций. Данный выбор был сделан лишь для того, чтобы продемонстрировать работу основных элементов HiAsm. Сначала перейдем к добавлению нового элемента в главное окно, нажав на соответствующую кнопку.
  9. Открытие панели элементов для добавления в программу HiAsm Studio

  10. В открывшемся окне вы увидите, что все объекты распределены по группам, чтобы было удобно выбирать требуемое. Создадим всплывающий список, нажав по нему.
  11. Выбор всплывающего списка для добавления в программу HiAsm Studio

  12. Переместите элемент на рабочую область, а затем соедините с главным окном.
  13. Соединение всплывающего списка с главным меню в программе HiAsm Studio

  14. Дважды щелкните по списку, чтобы заполнить строки. Каждую новую позицию пишите с новой строки.
  15. Редактирование пунктов добавленного списка в HiAsm Studio

  16. Подтвердите изменения, щелкнув на зеленую галочку.
  17. Сохранение изменений в добавленном списке HiAsm Studio

  18. Теперь давайте добавим обычный текст, который будет свидетельствовать о названии всплывающего меню.
  19. Переход к добавлению надписи в программу HiAsm Studio

  20. Откройте объект и заполните его содержимым.
  21. Набор текста для надписи в главном окне HiAsm Studio

  22. Обозначим надпись дополнительной картинкой, выбрав соответствующий элемент из списка.
  23. Переход к добавлению изображения в программу HiAsm Studio

  24. Все это тоже нужно будет связать с главным окном.
  25. Привязка изображения к главному окну создаваемой программы в HiAsm Studio

  26. HiAsm поддерживает изображения разных размеров и форматов, добавляется оно точно так же, как в случае с текстом.
  27. Добавление изображения в объект HiAsm Studio

  28. Дополнительно присутствует встроенный редактор, позволяющий изменить определенные части картинки.
  29. Редактирование добавленного изображения в HiAsm Studio

  30. Далее через «Вид» вы можете запустить «Редактор формы».
  31. Переход в режим редактирования вида HiAsm Studio

  32. Он позволит расположить все компоненты в необходимом месте на окне путем перемещения и масштабирования.
  33. Редактирование расположения объектов в программе HiAsm Studio

  34. Каждый объект или меню редактируется через окно «Свойства элемента». Запустите его, чтобы увидеть основные параметры, предварительно выбрав одно из меню или окон.
  35. Открытие окна свойств элемента программы в HiAsm Studio

  36. Здесь вы можете менять основной фон, устанавливать размеры, расположение курсора, положение относительно основного окна и добавить одну из множества точек.
  37. Настройка свойств главного окна в программе HiAsm Studio

  38. Окно свойств по умолчанию находится справа. Давайте обратим внимание на редактирование текста. Выберите шрифт, цвет и размер. В разделе «Style» активируется курсив, подчеркивание или выделение жирным.
  39. Настройка текста через вкладку Свойства в программе HiAsm Studio

  40. Добавим перемещаемый ползунок, чтобы регулировать количество порций.
  41. Добавление ползунка определения количества в программе HiAsm Studio

  42. В меню «Свойства» потребуется настроить минимальное и максимальное значение отметок, например, от 1 до 6.
  43. Настройка количества сечений для ползунка в программе HiAsm Studio

  44. После каждого изменения можете запускать программу, чтобы ознакомиться с результатами и убедиться в отсутствии ошибок.
  45. Проверка внешнего вида программы в HiAsm Studio

  46. По завершении мы предлагаем добавить кнопку «ОК», подтверждающую готовность заказа. Она находится в разделе «Rush-Контролы».
  47. Добавление кнопки подтверждения в программу HiAsm Studio

  48. Задайте кнопке название, например «ОК» или «Подтвердить заказ».
  49. Настройка кнопки подтверждения в программе HiAsm Studio

  50. После завершения добавления двух позиций у нас получилась программа, которую вы видите на скриншоте ниже. Конечно, здесь еще нужно работать с оформлением и другими недостатками функциональности, внешнего вида. Однако этот пример был создан только ради того, чтобы продемонстрировать принцип действия HiAsm.
  51. Предварительный просмотр программы перед сохранением в HiAsm Studio

  52. Если хотите сделать перерыв или сохранить готовый проект для дальнейшего конвертирования в исполняемый файл, нажмите на кнопку «Сохранить» и выберите место на жестком диске.
  53. Сохранение готового проекта в HiAsm Studio

Возможностей рассмотренного инструмента хватит не только для того, чтобы создать простое графическое приложение. HiAsm вполне справляется и с гораздо сложными работами, например, созданием проигрывателя или загрузчика файлов из интернета. Конечно, здесь придется приложить намного больше усилий и выучить множество схем и встроенных скриптов. Все это намного проще освоить, если использовать официальные ресурсы, например, форум. Там пользователи не только делятся своими работами, но и объясняют начинающим азы конструирования ПО. Тем более при возникновении вопросов ничего не мешает вам создать отдельную тему, подробно описав сложившуюся трудность.

Перейти на официальный форум HiAsm

Способ 2: Язык программирования и среда разработки

Как уже было сказано ранее, абсолютно все программы пишутся на определенном языке программирования. В некоторых сложных проектах бывает задействовано сразу несколько ЯП. Такой способ написания софта самый сложный, но при освоении одного из языков вы получаете практически безграничные возможности в кодировании программного обеспечения, утилит или отдельных скриптов. Главная задача — определиться с языком программирования. На этот вопрос постарались дать ответ специалисты из известного обучающего сервиса GeekBrains. Всю необходимую информацию вы найдете по указанной ниже ссылке.

5 языков программирования, которые надо учить первыми

Теперь же давайте рассмотрим несколько вариантов обеспечения, написанного при помощи упомянутых в статье ЯП. В первую очередь затронем Python, который некоторые программисты считают самым простым языком. Чтобы на экране появилось простое графическое окно размером на весь экран, придется подключить стандартную библиотеку Tkinter и написать код такого формата:

from tkinter import *


class Paint(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent

def main():
root = Tk()
root.geometry("1920x1080+300+300")
app = Paint(root)
root.mainloop()

if __name__ == "__main__":
main()

Отображение окна созданного на языке программирования Python

Далее добавляется код, который вы видите на скриншоте ниже. Он реализует примерно такие же функции, как стандартная программа Paint.

Открывок кода графического приложения на Python

После успешной компиляции запускается графическое окно с уже добавленными кнопками. Каждая из них отвечает за размер кисти и цвет.

Внешний вид графического приложения на Python

Как видите, разобраться в приложениях с GUI (графическим интерфейсом) не так уж и сложно, однако сначала лучше начать с консольных скриптов и небольших программ. Освоить Python вам помогут свободные материалы, уроки и литература, которой сейчас вполне достаточно, чтобы самостоятельно изучить необходимый материал.

В приведенной статье на GeekBrains отдельное внимание уделено и C#, который называют универсальным языком программирования для тех, кто еще не определился, в какой области хочет применять свои навыки. Разработка ПО для Windows ведется в официальной среде от Microsoft под названием Visual Studio. Код внешне выглядит так, как вы видите ниже:

namespace MyWinApp
{
using System;
using System.Windows.Forms;

public class MainForm : Form
{
// запускаем приложение
public static int Main(string[] args)
{
Application.Run(new MainForm());
return 0;
}
}
}

Как видите, существуют определенные различия с тем же Python. Если скопировать этот код, вставить его в IDE и скомпилировать, на экране появится простейшее графическое окно, куда уже в дальнейшем и будут прикрепляться кнопки, блоки и другие объекты.

Мы упомянули о Visual Studio как о среде разработки. Она понадобится в любом случае, если вы хотите писать собственный софт на ЯП, поскольку стандартный блокнот или текстовый редактор для этого практически не подходит. Ознакомиться с лучшими IDE, поддерживающими разные языки, мы советуем в отдельной нашей статье от другого автора далее.

Подробнее: Выбираем среду программирования

В рамках этой статьи мы постарались максимально детально ознакомить вас с процессом написания программного обеспечения с помощью двух доступных методов. Как видите, дело это не совсем простое, ведь нужно получать специальные знания и учить многие аспекты, чтобы освоиться в этом деле. Приведенный выше материал был нацелен лишь на предоставление общей информации для ознакомления и не является полноценным уроком, освоив который, можно стать уверенным программистом. Если вас заинтересовал какой-либо ЯП или HiAsm, потребуется уделить много времени на изучение соответствующих обучающих материалов.

Наша группа в TelegramПолезные советы и помощь

Вы ищете руководство по разработке приложений для Windows 10 для начинающих? Существует множество причин, по которым вам может понадобиться разработка настольного приложения. Возможно, вы захотите упорядочить данные для своего бизнеса, чтобы все было легко доступно. Возможно, у вас есть идея создать компьютерную программу, игру, утилиту или приложение для коммерческого релиза. Возможно, вы придумали отличный способ помочь благотворительной организации или местной группе по соседству.

В любом случае, при правильном развертывании, приложения могут быть способом получения дохода или быть полезными для людей, автоматизируя задачи или помогая другими способами. Если вы хотите создавать приложения, которые имеют самый простой уровень или технические знания и оказывают наибольшее влияние, то стоит понимать, что, несмотря на популярность мобильных устройств, таких как телефоны и планшеты, целью номер один для разработчика программного обеспечения является настольный компьютер. Для большинства людей, благодаря успеху Microsoft, этот настольный компьютер, вероятно, работает под управлением Windows 10 или, в меньшей степени, его младшего брата Windows 11 .

Поэтому давайте поделимся с вами некоторыми основами разработки приложений для Windows 10.

Оглавление

Подходит ли вам настольное приложение?

Что касается приложений, у вас есть три основных варианта на выбор: настольные приложения, мобильные приложения или веб-приложения.

В то время как настольные приложения обладают мощностью и надежностью для удовлетворения ваших требований, веб-приложения не следует упускать из виду, но помните, что доставка веб-приложений требует некоторых компромиссов и подходящего — и безопасного — места для их размещения. Если вы храните или обрабатываете личные данные в своем приложении, вам следует изучить лучшие способы сделать это безопасно для веб-приложения. Также обратите внимание, что в разных странах и юрисдикциях могут быть свои собственные правила обработки, хранения и передачи любых личных данных, а также множество запутанных неочевидных правил, касающихся таких вещей, как файлы cookie веб-браузера и соображения доступности для пользователей с ограниченными физическими возможностями.

pexels josh sorenson 1714203

Стоит ли мне создавать мобильное приложение?

В то же время мобильные приложения также являются опцией в зависимости от требований проекта. Каждый из них имеет свои преимущества и недостатки, которые вы должны взвесить, чтобы ваше приложение было успешным. Но стоит отметить, что хотя мобильная разработка достаточно проста, особенно с помощью подходящего кроссплатформенного инструмента разработки, может быть много препятствий, которые нужно преодолеть. Например, развертывание мобильного приложения на Android может быть таким же простым, как компиляция приложения, а затем копирование его на устройство пользователя в качестве «специального» приложения. Но обычно это не так, большинству разработчиков приходится бороться с магазином приложений поставщика устройства. Для Apple это Apple App Store. Для Android это обычно Google Play Store или более ограниченный рынок для конкретного поставщика, например Samsung Galaxy Store.

Эти магазины могут быть отличным способом воплотить идею вашего приложения в «реальный мир», и очень приятно видеть, как люди действительно загружают и используют ваше мобильное приложение. Тем не менее, все эти магазины имеют обширный и иногда запутанный список правил, которым должно следовать ваше приложение, иначе оно будет либо вообще не разрешено в магазинах приложений, либо будет снято с продажи в будущем. Правила варьируются от простых вещей, таких как правильные значки, текст и изображения для страницы приложения в магазине приложений, и очевидных вещей, таких как правильная поддержка новейших устройств, до менее очевидных вещей, таких как отсутствие отдельной страницы регистрации для любые услуги подписки, если вы также не разрешаете пользователям регистрироваться по ссылке в приложении, которая проходит через способы оплаты владельца магазина приложений (чтобы гарантировать, что они получат свою долю прибыли).

Если вы знаете, что ваше приложение будет частью повседневной жизни пользователя, пользователям нужно будет копировать и вставлять в буфер обмена, будут отправляться уведомления или приложение будет использоваться без доступа к Интернету, выберите настольное приложение. Когда к чему-то нужно получить быстрый и частый доступ на компьютере пользователя, следует отдать предпочтение настольному приложению.

Есть ли у настольных приложений магазин приложений?

Одним словом, да, у Windows есть один, а также у Apple и некоторых типов Linux, хотя большинство разработчиков программного обеспечения, как правило, не используют магазин приложений и просто позволяют пользователям загружать приложение прямо на свой рабочий стол, обычно по ссылке на веб-странице. . Магазин приложений Windows недавно смягчил ряд своих правил, чтобы разработчикам было проще продавать или распространять из него свои приложения. В Windows 11 также есть возможность поддерживать приложения Android, которые могут работать на настольных компьютерах под управлением Windows 11. Microsoft также представила подсистему Windows для Linux или WSL. WSL позволяет пользователям Windows работать, а вам — разрабатывать приложения Linux на компьютере с Windows 10 или 11 . Это работает аналогично запуску приложений Android в Windows.

Вы рассматриваете возможность разработки кроссплатформенных приложений?

Итак, как только вы установили, что настольное приложение лучше всего соответствует вашим требованиям, вы должны решить, на какую настольную платформу ориентироваться. Вы можете выбирать между платформо-зависимым (собственным) и кросс-платформенным программированием. Приложения для конкретных платформ могут работать только на одной платформе, будь то macOS, Windows или Linux. Тем не менее, для самого широкого охвата Windows 10 и 11 по-прежнему являются наиболее широко используемой настольной операционной системой в мире с очень значительным отрывом.

Кроссплатформенная разработка

Кроссплатформенная разработка, которая позволяет вам создавать код приложения, который не привязан к одной конкретной целевой платформе, но может работать, например, на Windows, macOS и Linux с использованием одного и того же кода. Если вам требуется: быстрая разработка, более низкие накладные расходы на разработку и обслуживание, потенциально меньшее количество ошибок из-за меньшего количества кода и возможность одновременного запуска на нескольких платформах и расширения охвата.

RAD Studio и Delphi поддерживают широкий набор платформ разработки. Для RAD Studio доступны две ключевые среды разработки приложений: библиотека визуальных компонентов (VCL) и FireMonkey FMX. Выбор использования VCL означает, что ваше приложение может быть нацелено только на настольные компьютеры Microsoft Windows (Win32/Win64) или планшеты под управлением Windows, такие как MS Surface.

Приложения, написанные с использованием FMX, представляют собой кроссплатформенные приложения, которые могут поддерживать Android, iOS, macOS, Windows и Linux. Разработчики могут создавать и развертывать свои приложения на основе единого кода и пользовательского интерфейса с помощью FireMonkey FMX на всех этих платформах. Любые различия в «форм-факторе» устройств — например, iPad Pro имеет более высокое разрешение экрана и форму, отличную от iPhone SE или более старого телефона Android, — можно устранить с помощью специализированных макетов экрана, которые скомпилированы в приложение от RAD Studio.

Если вы тщательно спроектируете свои экраны, вам могут не понадобиться специально настроенные экраны, но часто они вам понадобятся; к счастью, это очень легко сделать с помощью RAD Studio.

pexels mateusz dach 450035

Является ли UWP устаревшим?

Очевидно, что мы сосредоточимся на средах разработки VCL и FMX, но как насчет других технологий, таких как UWP от Microsoft? Универсальная платформа Windows (UWP) была одной из многих вычислительных платформ, выпущенных Microsoft для разработчиков, и впервые была представлена ​​в Windows 10 .операционная система. Microsoft задумывала UWP как универсальную платформу, которая должна была помочь в разработке приложений за пределами операционной системы Windows. Еще в 2015 году предполагалось, что UWP позволит разработчикам ориентироваться не только на рабочий стол Windows, но и на другие устройства, такие как консоль Microsoft XBOX и систему смешанной реальности HoloLens, практически без изменений. UWP имел ряд проблем и технических ограничений, которые вызвали некоторые разногласия с известными организациями, такими как Epic Games. После некоторой капитуляции со стороны Microsoft кажется, что UWP устарела . Microsoft активно продвигает новые направления, включая многоплатформенный пользовательский интерфейс приложения .NET или систему .NET MAUI.

UWP был одним из многих выпущенных решений для разработки, которые можно было использовать для создания клиентских приложений для Windows. Однако за последние несколько лет произошли различные сдвиги в платформе Windows от Microsoft, связанные с поддержкой разработки приложений и платформ. Инновации, представленные для обеспечения работы приложений Android в более новых версиях Windows, и превосходный WSL показывают, насколько далеко Microsoft продвинулась в использовании других операционных систем и устройств, отличных от Windows, даже если эти приложения все еще работают на компьютере с Windows.

Как насчет разработки приложений для Microsoft Windows 10 и WinRT?

К тому времени, когда Microsoft выпустила Windows 10, они уже добавили новую систему, основанную на «бинарном интерфейсе приложений» (ABI), среду выполнения Windows, более известную как WinRT, которая была запущена с Windows 8. WinRT был нацелен на обслуживание «управляемых» языков программирования и обеспечивал поддержку C++, Rust, Python, JavaScript/TypeScript и, конечно же, собственных C# и Visual Basic от Microsoft.

Внедрение WinRT кажется консервативным и, возможно, более медленным, чем хотелось бы Microsoft. Разработчикам необходимо было перепроектировать часть своего кода из-за того, что WinRT был написан с использованием C++ и основан на объектно-ориентированной парадигме и модели асинхронного программирования, что для некоторых существующих кодовых баз могло быть препятствием для внедрения.

К счастью, RAD Studio предлагает сопоставления WinRT API и интерфейсы Object Pascal для поддержки таких служб Windows, как уведомления Win 10 и контракты Win 10. Разработчики могут использовать компоненты, которые мы предоставляем для приложений Windows для уведомлений и контрактов как в VCL, так и в FMX C++ и Object Pascal. Базовый механизм включения приложений Windows не беспокоит разработчиков, использующих RAD Studio, поскольку VCL и FireMonkey FMX позаботятся о деталях за нас. Вы пишете свой код на Delphi или C++ и управляете свойствами визуальных компонентов, таких как поля редактирования, метки, списки, деревья и т. д.; и невизуальные компоненты, такие как диалоги открытия/сохранения и уведомления, и пусть компилятор и библиотеки времени выполнения сделают всю тяжелую работу за вас. Зачем стараться больше?

Являются ли Delphi и VCL лучшим вариантом для разработки приложений для Windows 10?

Конечно, у вас есть возможность программировать, используя тот инструмент, который, по вашему мнению, вам подходит, но мы считаем, что для наибольшей производительности лучше использовать C++ и Delphi. Delphi и библиотека визуальных компонентов(VCL) предлагают превосходные возможности для инкапсуляции возможностей операционной системы Windows. Возможно, наиболее важной причиной выбора RAD Studio и Delphi является непревзойденная простота изучения языка Delphi, поскольку разработчики могут очень быстро создавать полностью готовые к использованию программы. «RAD» в RAD Studio — это аббревиатура от «Быстрая разработка приложений», а экран перетаскивания и дизайн формы означают, что вам не нужны никакие инструменты типа каркаса или даже эскизы, чтобы разработать то, как вы хотите, чтобы экраны вашей программы выглядели. вы создаете их в RAD Studio и точно знаете, как они будут выглядеть во время работы .

VCL зарекомендовал себя среди своих пользователей как одна из лучших возможных оболочек поверх собственных Windows API и библиотек пользовательского интерфейса. Только библиотека VCL предлагает перспективную поддержку вашего исходного кода с заслуженной репутацией стабильности и долговечности. Вы можете взять «устаревшее» приложение, созданное много лет назад, и легко поддерживать и обновлять его для Windows 10 и 11, а также обеспечивать поддержку более ранних версий Windows. Приложения, написанные на Delphi, очень устойчивы к изменениям в операционной системе по мере выпуска различных обновлений и исправлений Windows. Это заметно по сравнению с другими типами технологий, такими как Python, которые могут давать сбои, когда необходимая среда выполнения Python либо отсутствует, либо заменена несовместимой версией. Это также верно для приложений, написанных с помощью . NET, которые могут пострадать при замене или обновлении слишком большого количества движущихся частей поддерживающей среды выполнения .NET. Конечно, есть способы обойти это, ничто не может быть слишком ужасным, но приложения, созданные с помощью RAD Studio, действительно очень надежны из-за отсутствия зависимости от среды выполнения и их автономного характера, что способствует высокой стабильности.

Учебник по разработке приложений для Windows 10 для начинающих

Хорошо, мы достаточно поговорили о достоинствах использования RAD Studio и о том, следует ли вам разрабатывать настольную программу для Windows 10 или мобильное кроссплатформенное приложение. Давайте попробуем создать действительно быстрое и простое приложение, чтобы показать вам, насколько это просто! Здесь, на сайте блога, есть еще много подробных руководств и статей, где мы освещаем все виды тем, от начинающих до продвинутых.

Следующие шаги дадут вам представление о том, как легко создать простое VCL-приложение «Hello world» для Windows за пару минут. Простейшим образом мы попытаемся продемонстрировать основные шаги для создания приложения VCL Forms. Приложение, которое вы создадите с помощью этого руководства, будет включать следующее:

  • Мы создадим форму VCL.
  • Мы добавим элемент управления в эту форму.
  • Мы добавим некоторый код, который срабатывает, когда наш пользователь нажимает кнопку.
  • Окончательно. наше приложение «Hello World» будет отображать диалоговое окно, когда пользователь взаимодействует с ним.

Как создать простое приложение «Hello world»

Откройте RAD Studio и щелкните пункт «Приложение Windows VCL — Delphi» (или C++) на странице приветствия.

2022 08 12 08 23 38

Как создать первый экран для вашего приложения Windows

Теперь у вас должна быть пустая форма на экране. Итак, давайте добавим в эту форму элемент управления «Кнопка».

  • Выберите вкладку «Палитра» — если вы не можете ее найти, выберите « Просмотр»> «Окна инструментов»> «Палитра»  или просто нажмите CTRL + ALT + P.
  • Палитра показывает все типы элементов управления, которые вы можете просто добавить в свое приложение. В палитре щелкните запись TButton, которая находится в группе Standard.
2022 08 12 08 34 23

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

Как сделать так, чтобы ваше первое приложение отображало строку «Hello world»

  • Щелкните один раз кнопку Button1 в форме.
  • В  Инспекторе объектов дважды щелкните поле действия OnClick на   вкладке События . Отобразится  редактор кода  с курсором в  блоке  обработчика событий Button1Click .
  1. Для Delphi переместите курсор перед  начальным  зарезервированным словом и нажмите  ENTER . Это создает новую строку над блоком кода. Для C++ поместите курсор после открывающей фигурной скобки ( { ) и нажмите  ENTER .
  2. Для Delphi вставьте курсор в новую созданную строку и введите следующее объявление переменной:

Для C++ введите следующий код:

  1. Для Delphi вставьте курсор в блок кода и введите следующий код:

s:= ‘Hello world!’;

ShowMessage(s);

Для C++ введите следующий код:

s = «Hello world!»;

ShowMessage(s);

2022 08 12 08 48 49

Как запустить свое первое приложение «Hello world»

  • Выберите  « Выполнить» > «Выполнить»  , чтобы построить и запустить приложение. Форма отображается с кнопкой с именем  Button1 .
  • Нажмите кнопку  1 . В диалоговом окне отображается сообщение «Hello World!»
  • Закройте форму VCL, чтобы вернуться в IDE.
2022 08 12 08 50 56

Наше первое приложение запущено!

Готовы приступить к разработке приложений для Windows 10?

Если у вас еще нет копии RAD Studio, почему бы не попробовать RAD Studio с Delphi и не загрузить ее сегодня ? Получите преимущество, которое предоставляет RAD Studio, для максимально эффективной разработки мощных настольных и кроссплатформенных приложений.

Хе хе ну вы поняли «ВОйти ВАйти».

Принято считать, что программирование это сложно, но это миф (все проще, чем кажется), и все что нужно, чтобы стать программистом это немного упорства и изобретательности.

В этой статье мы получим все базовые навыки, которые нужны программисту, по окончании статьи у тебя будут все необходимые навыки для C++ Junior Desktop Developer, а также ты сможешь создавать 90% программ с софт портала, меньше слов ближе к делу.

(РЕМАРКА! Эта статья не будет интересна тем, кто уже знает программирование, поэтому попрошу избавить меня от вашего «экспертного мнения» программиста.

я планировал цикл из 5 статей посвященный всем основным языкам (с++, паскаль, джава, си шарп, веб), но налетевшие эксперты программирования (*ыдлокодеры) , которые даже статьи толком не читали вызвали у меня жесткий бадхерт и дезморал, из за чего желание писать статьи упало на пол шестого, как и пребывание на этом паблике, поэтому сорянчик:(

Чуть-чуть теории и много практики

Программирование построено на 4 основных концептах:

  1. Переменные

  2. Конструкции условий

  3. Конструкции циклов

  4. И конструкции функций

В двух словах мы хитроумно играем с базовым функционалом, предоставляемым на уровне процессора\биоса\системы, комбинируем те базовые функции с какими‑то данными из переменных, добавляем условия и циклы и оборачиваем это все в новые функции.

К примеру:

У нас есть функция на уровне системы (для изменения цвета пикселя на экране, которая выходит от драйвера видеокарты, а та из комбинированной работы процессора и видеокарты).

Мы несколько раз вызываем эту функцию (в каких‑либо координатах), таким образом рисуем линию, оборачиваем это в функцию для рисования линий, 4 раза вызываем эту функцию чтобы нарисовать (к примеру) кнопку, потом оборачиваем это в функцию для рисования кнопок и уже рисуем в зависимости от положения курсора разными цветами, и тд.

Надеюсь, логика понятна, немного по сути:

Переменная — условный «маркер данных», подстановочное значение, ключевое слово, с которым что‑то ассоциировано, хранилище данных.

Конструкция условий — какое либо правило для выполнения одной части кода, либо альтернативной (состоит из заголовка «условия» и блока кода, выделенного {} собаками «тела»).

Конструкция цикла — тоже условие, но вместо альтернативного, кода повторяет код внутри тела (фигурных {} скобочек), до тех пор, пока условие выполняется.

Функция — ключевое слово, которое выполняет какой‑либо блок кода, и возвращает на место своего использования какие‑либо данные (функция может не только возвращать какие‑либо данные, но и принимать в себя данные (аргументы функции), которые вместе с функцией указаны в месте вызова, в коде, функция также состоит из заголовка и тела).

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

Ничего страшного если непонятно разберемся на ходу.

Последнее из теории это GetMessage() и SendMessage(), две ключевых функции, на которых построены все графические приложения, первая получает сообщения от системы, вторая отправляет сообщения в систему.

К примеру: получаем от системы сообщение «координаты мыши изменены», в коде создаем условие, проверяющие пересекают ли новые координаты местоположение кнопки, и, если да, меняем цвет кнопки. Отправляем сообщение примерно также (из программы в систему), и такой цикл происходит все время, пока запущенна программа (система: нажата кнопка, программа: изменен размер окна, система: создан новый файл, программа: окно перерисовано, и тд).

Много практики

Запустим Visual Studio и создадим новый проект, выбираем классическое приложение Windows (для него даже ничего не нужно скачивать, все уже в системе), тыкаем далее\создать. Готово, можем нажать кнопку запустить, и у нас запуститься готовое пустое приложение.

Закроем его (не пугаемся!).

В редакторе понаписано много чего не понятного (ничего страшного что непонятно), и сгенерированные комментарии (вот это //зеленое на русском) не особо помогают.

Вообще для реализации Win32 приложений сгенерированный код избыточен, и не совсем понятен даже для «программистов», я думаю это такая «шуточка» от разработчиков, мол справишься с этим, значит справишься со всем (как не трудно догадаться по малому количеству программ, и в общем информации по данному подходу в сети, примерно 90% программистов — не программисты).

На самом деле из всего этого кода нам хватило бы одной функции и пары команд, ну да ладно, разберем исходный код!

Этап №1 очень сложный код

Ну шо народ погнали на!?

// WindowsProject1.cpp : Определяет точку входа для приложения.
//

// этот кусок можно пропустить (это добавил я)
// директива pragma once подразумевает
// исключение логического бага в программе,
// который может произойти из-за команды для
// подключения библиотек (файлов с заготовками кода)
// include, которая просто вставляет на свое место
// все содержимое файла, который был указан
// (включая и команды include внутри подключаемого
// файла) при вызове, из за чего могу возникать логические
// ошибки и множественные самоподключения (когда
// одна библиотека ссылается на вторую, а вторая на
// первую), в двух словах все исходники собираются
// в одни длинный исходный код (командами include),
// а команда pragma once удаляет все дубликаты,
// оставляя только самую первую include,
// лично я начинаю любой файл с pragma once,
// что бы не беспокоится о зависимостях, но это
// не обязательно (кароче пока не заморачивайтесь)
#pragma once 

// и начать отсюда
// подключаем вспомогательные библиотеки
// наборы готового кода как в этом файле, 
// где уже описанные какие-то функции (заготовки) для
// реализации и запуска нашей программы
#include "framework.h"
#include "WindowsProject2.h"

// определим вспомогательный термин 
// (читай создадим разновидность переменной)
// под названием MAX_LOADSTRING и ассоциируем
// с ней число 999, теперь везде в коде, где 
// будет встречается термин MAX_LOADSTRING
// он будет заменятся на 999
#define MAX_LOADSTRING 999

// Глобальные переменные:
// 
// hInst это глобальная переменная экземпляра нашей программы
// переменная создается следующим образом - сначала пишется ключевое слово,
// которое обозначает тип данных переменной, потом какое-нибудь имя (ключевое
// слово), с которым ассоциированных данные, то есть везде в коде, где будет
// встречается ключевое слово, оно будет автоматически заменятся на данные,
// которые были ассоциированы с этим ключевым словом
HINSTANCE hInst;                                // текущий экземпляр

// сразу создадим свою переменную, тип данных
// будет HWND, название Form1, будем хранить тут уникальный
// идентификатор на наше окно программы (формы)
HWND Form1;

// в общем то создаем пару переменных типа "буквенная многопеременная в одной",
// и сразу задаем его длину в параметре [MAX_LOADSTRING] (иначе говоря набор 
// символов, или текстовые переменные)
WCHAR szTitle[MAX_LOADSTRING];                  // Текст строки заголовка
WCHAR szWindowClass[MAX_LOADSTRING];            // имя класса главного окна

// Отправить объявления функций, включенных в этот модуль кода:
// 
// эти вспомогательные функции сейчас не важны, по сути, они просто "нормализуют"
// нашу программу для системы, создают новый экземпляр окна (из заготовок в системе) 
// и добавляют мониторинг действий (нет смысла вдаваться в подробности, и забивать голову)
// функции начинаются с заголовков, сначала идет тип данных, которые должна вернуть функция,
// потом ключевое слово, любое (название этой функции), и наконец в круглых () скобках
// "заготовки" переменных всех переменных, которые должы быть переданы функцие, в самой же
// (внутри), эти данные доступны под названиями "заготовк переменных"
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

// создаем функцию wWinMain
// эта функция принимает в себя несколько параметров,
// это "точка входа", когда программа запускается, она 
// автоматически начинает выполнять эту функцию
//
// эта функция будет обеспечивать связь системы и нашей программы
// при помощи бесконечного цикла, который повторяется до тех пор, 
// пока программа запущена, и перехватывает сообщения от системы
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // эти строки сгенерированы автоматически
    // с ними пока заморачиваться не нужно, они вспомогательные для компилятора
    // в двух словах это еще одна разновидность переменной,
    // с которой ассоциированные вспомогательные функции для освобождения
    // памяти от неиспользуемых переменных
    //UNREFERENCED_PARAMETER(hPrevInstance);
    //UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Разместите код здесь.

    // Инициализация глобальных строк
    // 
    // подгружаем (при помощи функций) из ресурсов нашего файла название окна программы для системы
    // и параметры самого окна (в данном случае окно типа "полноценное" с изменяемым
    // размером, сворачиванием, и тд)
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);

    // адаптируем нашу программу для системы при помощи функции
    // MyRegisterClass, в качестве параметра передаем идентификатор
    // нашего окна программы, который получаем из текущей функции 
    // (wWinMain) в параметре hInstance
    MyRegisterClass(hInstance);
    
    // создаем условие, если функция (InitInstance) вернула на место своего вызова
    // значение false, вызываем встроенное в идею функций ключевое слово
    // return, при встрече которого любая функция прекращает построчное выполнение
    // кода, и возвращаем ложь в функции wWinMain (в место вызова функции)
    // Выполнить инициализацию приложения:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    // это вспомогательный код для отлова событий нажатия клавиш,
    // ассоциируем с переменной hAccelTable (типа HACCEL) результат
    // выполнения функции LoadAccelerators, сама функция извлекает из
    // ресурсов нашего файла готовые схемы сочетания клавиш и названия функций
    // для этих сочетаний (которые были сгенерированны автоматически)
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));

    // создадим переменную типа MSG (сложносоставной тип данных) под названием MSG
    // в эту переменную будут попадать команды, которые в цикле будут перехватываться 
    // универсальной функцией GetMessage
    MSG msg;

    // Цикл основного сообщения:

    // создаем цикл, до тех пор пока GetMessage, с пустыми параметрами 
    // (2,3,4, не нужно вдавятся сейчас в подробности почему)
    // возвращает какой-либо результат (в переменную msg) 
    // выполняем код в теле цикла
    //
    // важный момент, почему &msg (ассоциация значений), а не
    // просто msg (присвоение значений)? потому что если создать 
    // "условный" объект кнопка, задать ему какие-либо параметры, 
    // после чего создать еще один, и попробовать скопировать параметры 
    // простым присвоением, присвоены будут не параметры кнопки, 
    // а сама кнопка (объект), следовательно просто кнопка один 
    // будет также доступна под названием кнопка два
    while (GetMessage(&msg, nullptr, 0, 0))

    // это начало тела цикла
    {
        // нестандартная конструкция условия, она подразумевает
        // простую логику, если не "!" результат выполнения функции
        // TranslateAccelerator (функция для горячих клавиш)
        // то выполняем код в условии, иначе говоря, если 
        // нажатая клавиша не выполнила функцию, которая ей
        // назначена, то выполняем код условия, а именно обрабатываем 
        // горячую клавишу (не обязательно вдавятся в подробности 
        // нам сейчас это не нужно)
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            // код обработки нажатий горячих клавиш
            // которые были указаны в схеме, которая
            // была загружена из ресурсов ранее
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // это конец тела цикла

    // возвращаем результат выполнения функции
    // в качестве подстановочного значение 
    // (которое встает в никуда) возвращаем параметры 
    // последней команды GetMessage
    return (int) msg.wParam;
}



//
//  ФУНКЦИЯ: MyRegisterClass()
//
//  ЦЕЛЬ: Регистрирует класс окна.
//

// выше уже была упомянута эта функция, она адаптирует
// в рамках системы нашу программу, вот, собственно, само 
// содержимое это функции, оно нам не интересно, двигаемся дальше
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    // просто настраиваем параметры окна программы
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT2));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT2);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
//   ЦЕЛЬ: Сохраняет маркер экземпляра и создает главное окно
//
//   КОММЕНТАРИИ:
//
//        В этой функции маркер экземпляра сохраняется в глобальной переменной, а также
//        создается и выводится главное окно программы.
//

// я думаю из сгенерированных комментариев понятно, для чего эта функция
// так же выше было сказано об этой функции, ее тоже можно пропустить
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Сохранить маркер экземпляра в глобальной переменной

   // разве что вот ключевая функция для создания окна, которая называется
   // CreateWindow (с модификатором W, то есть одна из вариаций этой функции)
   // и возвращает идентификатор на место своего вызова, который мы сразу
   // ассоциируем с переменной hWnd
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   // добавим условие, если "не hWnd" (то есть
   // нет ассоциированного объекта) возвращаем FALSE
   // и завершаем выполнение функции (ключевое слово
   // return помимо возврата данных в место вызова функции,
   // подразумивает завершение этой функции
   if (!hWnd)
   {
      return FALSE;
   }

   // после создания окна программы вызываем функции для отображения
   // окна программы и его перерисовки
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   // если условие не сработа и дошли до этого
   // места возвращяем TRUE так как по логике
   // функция выполенилась без сбоев
   return TRUE;
}

//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  ЦЕЛЬ: Обрабатывает сообщения в главном окне.
//
//  WM_COMMAND  - обработать меню приложения
//  WM_PAINT    - Отрисовка главного окна
//  WM_DESTROY  - отправить сообщение о выходе и вернуться
//
//

// еще одна сгенерированная функция, эта функция подвязывается
// к нашей форме (окну программы), и обрабатывает события нашей формы (к примеру
// изменение размера окна, тык мышкой, активность формы и тд)
// это основная функция нашей программы, которая отвечает за какие нибудь события
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // добавим ассоциацию идентификатора окна программы
    // с нашей глобальной переменной, в которой храним
    // указатель на идентификатор
    Form1 = hWnd;

    // switch разновидность условия, не совсем понятно
    // целесообразность использования альтернативного
    // условного конструктора, поскольку то же самое 
    // можно было описать и конструкцией if (но предположу
    // что несмотря на то что switch не является "канонистической 
    // конструкцией, она обеспечивает одно единственное выполнение
    // условия, затем завершает, это может обеспечивает стабильность,
    // избегая множественных срабатываний на одну команду message)

    // в данном случае условие обрабатывает сообщения,
    // которые получает из GetMessage, в соответствии
    // с определенным содержимым message (который передается
    // в параметрах функции), и если значение message соответствует
    // какой либо конструкции case, выполняет этот фрагмент кода
    switch (message)
    {
        // не стоит забывать про зажатую Ctrl и левый тык мышкой
        // это перекинет в файл, где описано это ключевое слово
        

        // WM_COMMAND это универсальное событие обработки команд
        // (к примеру отправленных через SendMessage), сама конструкция
        // подразумевает условие, если содержимое message, соответствует
        // содержимому ключевого слова WM_COMMAND, выполняет блок кода внутри
        // скобочек (содержимое, читай какие-то данные, ассоциированные с 
        // этим ключевым словом)
        case WM_COMMAND:
        {
            // тут обработка элементов меню
            // сами элементы в ресурсах файла, 
            //сгенерированы автоматически
            int wmId = LOWORD(wParam);
            // Разобрать выбор в меню:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

        // условие, если данные в message соответствуют WM_PAINT,
        // или событию отрисовки выполняем код внутри
    case WM_PAINT:
        {
            // создаем переменную типа параметры
            // области рисования
            PAINTSTRUCT ps;

            // создаем переменную типа область рисования
            // и ассоциируем с ней результат выполнения функции
            // BeginPaint (в параметрах которой передаем
            // идентификатор нашего окна, и ассоциируем данные
            // из переменной с параметрами
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Добавьте сюда любой код прорисовки, использующий HDC...

            // завершаем процесс рисования,
            // вызывая специальную функцию,
            // данным примером хитроумно затопарен
            // процесс рисования объектов на форме,
            // функцией BeginPaint, который продолжится,
            // после выполнения функции EndPaint
            EndPaint(hWnd, &ps);
        }
        break;

        // если переменная message 
        // содержит WM_DESTROY
        // выполняем код для завершения
        // работы приложения
    case WM_DESTROY:
        PostQuitMessage(0);
        break;

        // если message не содержит никаких сообщений
        // (или сообщения не соответствуют ни одному из условий)
        // выполняем другой код, функцию, которая гарантирует обработку
        // "неопределенного" message, она нас тоже пока не интересует
        // (но может понадобится, так как мы можем ее "переописать",
        // для каких-либо наших задач)
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    // вернем результат выполнения функции 0
    // можно изменить на 1 чтобы мониторить 
    // работоспособность программы, исходя
    // из возвращаемого значения этой функции
    // (то есть если функция WndProc выполнилась
    // и не вернула на место вызова 1, значит
    // какое то решение не работает, для этого
    // просто можно создать переменную (в начале
    // блока кода, с которой ассоциировать что
    // нибудь (к примеру 0), и если функция смогла
    // выполнить свой код до этой позиции, возвращяем 1
    return 0;
}

// Обработчик сообщений для окна "О программе".

// аналогично только не для основного окна программы,
// а для диалога "Об программе", его разбирать смысла нет
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

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

Вот код этой функции:

MessageBox(NULL, TEXT("Текст"), TEXT("Заголовок"), 0);

Она принимает в себя разные данные, и показывает текст в маленьком окошке. Важный момент! Для редактора кода (компилятора) весь текст внутри потенциальные «ключевые слова», с которыми ассоциировано что ни будь, соответственно, чтобы текст использовать ни как ключевое к как «текст», его нужно помещать в «« двойные кавычки.

Вернемся к коду, найдем функцию с названием «WndProc» и переделаем ее, внутри условия добавим новый блочок условия для события нажатия левой кнопки мыши:

        case WM_LBUTTONDOWN:
        {

        }
        break;

Вот примерно такая конструкция, это аналог условия — если содержимое message соответствует данным, ассоциированным с клочимым словом WM_LBUTTONDOWN, выполняем код внутри, после чего ключевым словом break прерываем дальнейшее выполнение конструкции switch.

Разместим функцию показа сообщения внутри этого блока (между {} у тела конструкции switch), а этот блок в функции WndProc. Далее запустим ткнем мышкой в любом месте окна нашей программы, и увидим следующее:

Отлично мы теперь ты «попытка в программиста»!

Этап №2 самое сложное закончилось, начинается АД

Правым тыком по папке с исходными файлами (в обозревателе решений) создаем новый файл, в моем случае это «Source.cpp», в вашем думаю тоже (не суть как важно). Мы создали новый файл, он абсолютно пустой, но мы сейчас наполним его (ручками, не копирую мой код, это очень важно!).

Создадим «свой тип данных», а также познакомимся поближе:

Разворачивай меня полностью

// подключим вспомогательные библиотеки
#pragma once 
#include "stddef.h"
#include <string> 
#include <Windows.h>
#include <Strsafe.h>

// не будем заморачиватся, с++ очень старый язык
// это еще одна разновидность подключения библиотек
// (просто подключает не весь файл, а кусок из файла)
using namespace std;

// создадим наш собственный тип данных,
// что создавать переменные нашего типа данных
// (для удобства)
class MyVariableUniversalType {

	// опишем несколько переменных
	// разных типов данных, внутри нашего
	//
	// мы опишем их в "приватном" блоке кода
	// описывающего наш тип данных, чтобы они
	// были видны только нашему типу данных
	// и недоступны другим
private:

	bool BooleanType;
	int IntegerType;
	string TextType;

	//
	// единственное, это опишем еще одну функцию
	// что такое protected пока не нужно думать,
	// просто сделайте так же
	//
protected:
	// так как тип данных LPCWSTR Long Pointer to Constant Wide String
	// не совсем классический тип данных строка (не совсем "канонистический"), 
	// я использовал сводный тип данных строка из другой библиотеки, 
	// которую подключил сверху, поэтому опишем конвертор из str::string в LPCWSTR
	// вдаваться в подробности на этом этапе не нужно, дальше можно 
	// пропустить и просто скопировать
	LPWSTR TransformStringToWideString(string agrument1)
	{

		// создадим вспомогательную переменную
		// типа мультипеременная с кодами символов
		// (с которой мы ассоциируем результат создания 
		// нового массива типа WCHAR), длинной в 
		// количество символов из параметра, получаемого 
		// в функцию, при помощи встроенной функции 
		// length (встроенной в тип string)
		LPWSTR tmp_return = new WCHAR[agrument1.length()];

		// вызовем функцию, которая конвертирует набор символов строки
		// str::string в нужный нам набор символов, и сразу помещает его
		// в мультипеременную, функция принимает в себя указатель кодировки,
		// указатель способа конвертирования, текст, размер в байтах,
		// указатель переменной (в которую поместить результат), длину
		// в символах
		MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, agrument1.c_str(), agrument1.size(), tmp_return, agrument1.length());

		// из за особенностей типа LPWSTR
		// код последнего символа должен 
		// быть 0, это указатель того что
		// это конец текста
		tmp_return[agrument1.length()] = 0;

		// вернем результатом содержимое 
		return tmp_return;
	}

	// далее опишем общедоступные части нашего
	// типа данных (то есть доступные не только 
	// внутри нашего типа данных)
public:
	LPWSTR TransformStringToWideString2(string agrument1)
	{

		// создадим вспомогательную переменную
		// типа мультипеременная с кодами символов
		// (с которой мы ассоциируем результат создания 
		// нового массива типа WCHAR), длинной в 
		// количество символов из параметра, получаемого 
		// в функцию, при помощи встроенной функции 
		// length (встроенной в тип string)
		LPWSTR tmp_return = new WCHAR[agrument1.length()];

		// вызовем функцию, которая конвертирует набор символов строки
		// str::string в нужный нам набор символов, и сразу помещает его
		// в мультипеременную, функция принимает в себя указатель кодировки,
		// указатель способа конвертирования, текст, размер в байтах,
		// указатель переменной (в которую поместить результат), длину
		// в символах
		MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, agrument1.c_str(), agrument1.size(), tmp_return, agrument1.length());

		// из-за особенностей типа LPWSTR
		// код последнего символа должен 
		// быть 0, это указатель того, что
		// это конец текста
		tmp_return[agrument1.length()] = 0;

		// вернем результатом содержимое 
		return tmp_return;
	}

	// создадим общедоступную переменную
	// типа LPCWSTR, с которой ассоциируем
	// ничего (L вначале нужно, потому что
	// у компилятора и программы разные кодировки)
	// вникать в детали пока не нужно
	LPCWSTR LPCWSTRTextType = L"";

	// создадим функцию, одноименную с нашим типом данных,
	// такая функция будет вызывается при попытке создать новый
	// экземпляр переменной нашего типа данных (конструктор, или
	// инициирующий блок), в момент создания будем наполнять нашу переменную
	// базовыми данными (ложью для типа булевых данных, -1 для числовых,
	// ничем для строковых)
	MyVariableUniversalType() {
		BooleanType = false;
		IntegerType = -1;
		TextType = "";
	}

	// теперь для закрепления знаний опишем несколько вспомогательных
	// функций, которые будут сразу в нашем типе данных (то есть будут
	// доступны в каждой новой копии переменной), без необходимости
	// описания этих функций по новый

	// создадим функцию для конвертирования
	// булевой переменной в числовую
	int BooleanTypeToInteger(bool argument1)
	{

		// создадим условие, если значение
		// передаваемое в качестве параметра (аргумента)
		// соответствует истине, завершаем функцию
		// и возвращаем 1, иначе 0 (числовые аналоги)
		if (argument1 = true)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	// создадим обратную функцию
	bool IntegerTypeToBoolean(int argument1)
	{

		// создадим условие, если значение
		// передаваемое в качестве параметра (аргумента)
		// больше 0, завершаем функцию с
		// возвратом результата true, иначе false
		if (argument1 > 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	// создадим функцию для конвертирования 
	// булевой переменной в строковую
	string BooleanTypeToString(bool argument1)
	{

		// создадим условие, если значение
		// передаваемое в качестве параметра (аргумента)
		// соответствует истине, завершаем функцию
		// и возвращаем текст true, иначе false
		if (argument1 = true)
		{
			return "true";
		}
		else
		{
			return "false";
		}
	}

	// и обратную
	// тут немного сложнее, поскольку
	// вариантов написания может быть много,
	// и надо обработать их все
	bool StringTypeToBoolean(string argument1)
	{

		// создадим вспомогательную
		// переменную, сразу ассоциируем
		// с ней значение false, чтобы
		// в случае если ни одно из условий
		// не сработает, функция вернула результат
		// в место вызова
		bool tmp_return = "false";

		// создадим переменную (мультипеременную, много значений в одной)
		// в которую специальным образом сразу поместим
		// все возможные варианты текстового значения true
		string true_variants[]
		{
			"true",
			"True",
			"TRUE",
			"truE",
			"TRue"
			/* ... и тд */
		};

		// создадим переменную счетчик для цикла
		int a = 0;

		// создадим переменную с общим количеством записей в
		// мультипеременной при помощи встроенной функции size
		int b = true_variants->size();

		// и в цикле проверим каждое из значений массива true_variants
		// на соответствие 
		while (a < b) {

			// создадим условие, если значение
			// передаваемое в качестве параметра (аргумента)
			// соответствует нашему теоретическому значению
			// из мультипеременной (к конкретному значению мы
			// обращаемся по индексу записи [], который берем
			// из переменной счетчика), во вспомогательную переменную
			// tmp_return помещаем true, и завершаем цикл
			if (argument1 == true_variants[a])
			{
				tmp_return = true;
				a = b + 1;
			}

			// после каждого повтора увеличиваем счетчик на 1
			// чтобы таким образом перебрать все варианты
			a = a + 1;
		}

		// вернем значение из вспомогательной переменной
		return tmp_return;
	}

	//
	// 
	// нечто подобное можно сделать и для числовых данных
	// 
	//

	// чтобы не растягивать код еще больше опишем
	// обращения к данным в нашем типе данных
	// (в канонистических языках программирования есть
	// такое понятие как свойства, в этом подходе с++
	// нет (явно), поэтому мы опишем свойства "получить" и 
	// "изменить" в виде функций (сейчас непонятно что
	// это и как это полезно, и удобно, но со временем 
	// понимание придет)


	// опишем функцию для получения из нашего типа
	// содержимое в формате LPCWSTR, в качестве
	// параметра укажем тип данных, которые нужно
	// превратить в LPCWSTR и вернуть (параметром
	// будем передавать четко описанный текстовый
	// указатель
	LPCWSTR GetVariableValue(string call_back_type)
	{

		// создадим условие, если параметр,
		// передаваемый в функцию соответствует
		// слову boolean, выполняем возврат результата,
		// который получается в результате использования
		// функции BooleanTypeToString, в которой в качестве параметра
		// был передан параметр из скрытой переменной BooleanType, а результат
		// выполнения функции BooleanTypeToString был сразу передан
		// в качестве параметра в функцию TransformStringToWideString,
		// которая уже на свое место вернула результат (уже совей обработки 
		// результата) на место, где должны быть данные для выполнения возврата 
		// из этой функции, и в переменную LPCWSTRTextType
		if (call_back_type == "Boolean")
		{
			LPCWSTRTextType =
				TransformStringToWideString(
					BooleanTypeToString(BooleanType)
				);

			return
				TransformStringToWideString(
					BooleanTypeToString(BooleanType)
				);
		}

		// сделаем нечто подобное для строковой переменной
		if (call_back_type == "String")
		{
			LPCWSTRTextType =
				TransformStringToWideString(
					TextType
				);

			return
				TransformStringToWideString(
					TextType
				);
		}

		// вернем ничего, если
		// ни одно из условий не сработало
		LPCWSTRTextType = L"";
		return L"";
	}

	//
	//
	// далее должна быть аналогичная функция 
	// для определения из типа данных из LPCWSTRTextType,
	// конвертирования в соответствующий простой тип,
	// и перемещение в переменную, но так как тут уже 400
	// строчек кода (хотя я планировал уложиться в 100) 
	// рекомендую погуглить как ее реализовать
	//
	//

};

// классы (так называются наши типы данных), подразумевают
// такое явление как наследование решений, это позволяет 
// создать новый тип данных, но при этом использовать в качестве
// заготовки все содержимое типа данных, который является
// родителем этого, в данном примере я создаю MyChildrenUniversalType
// и через : (двоеточие) указываю в какую секцию этого класса
// поместить все решения из класса родителя (я выбрал private,
// чтобы все лишние функции не мешали при обращении к этому классу),
// далее идет название класса, который должен является родителем,
// таким подходом к программированию (это называется ООП) можно
// значительно упростить процесс для крупных программ, перенося
// уже готовые функции в другие классы, и использовать их когда нужно
class MyChildrenUniversalType : private MyVariableUniversalType {
public:

	// конструктор класса можно оставить пустым,
	// хотя можно было даже не описывать ни конструктор,
	// ни деструктор
	void MyVariableUniversalType() {

	}

	// еще одно полезное свойство класса
	// это создание множества функций под одним
	// ключевым словом (это называется перегрузка),
	// и позволяет в зависимости от ситуации (от набора 
	// параметров, которые были переданы в функцию) 
	// использовать разные решения, в данном случае я 
	// создаю несколько функций для конвертирования
	// текста (AnyTo), и задаю им разные аргументы,
	// чтобы во время вызова этой функции в коде не
	// возится с разными типами данных, пытаясь их
	// подвести под соответствие одного к другому
	LPCWSTR AnyTo(string Any_std_string) {

		// получить доступ к функции TransformStringToWideString
		// из другого типа данных мы можем благодаря секции protected,
		// она создана специально для того, чтобы все что находится внутри
		// секции не было видимо для всех, но было доступно для 
		// использования в дочерних классах
		return (LPCWSTR)TransformStringToWideString(Any_std_string);
	}

	LPCWSTR AnyTo(LPCSTR Any_LPCSTR) {
		return (LPCWSTR)Any_LPCSTR;
	}

	LPCWSTR AnyTo(char* Any_CHAR) {
		return (LPCWSTR)Any_CHAR;
	}
};

Теперь, когда мы получили базовое представление о том, что такое программирование, и как это работает, можем приступить к созданию чего‑либо полезного, а именно кнопки (но, если дополнить наш тестовый Source.cpp недостающими функциями, он станет вполне себе полезным, а иногда даже незаменимым).

Этап №3 ада больше нет, сейчас только не осмысляемо сложное

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

Для начала правым тыком по исходным файлам создадим еще один файл cpp, у меня это Source1.cpp, у вас, решайте сами.

Бом бом бом (наш класс кнопки)

// новый уровень сложности, комментариев будет меньше

// эта строчка описана в первом этапе в самом начале
#pragma once 

// подключим библиотеки
#include "framework.h"
#include "windows.h"
#include "winuser.h"

// создадим наш тип данных, и опишем его сущность
// (на этот раз создадим кнопку)
class ObjectButton {
private:
	// поскольку целесообразность
	// "свойств класса" крайне условна
	// в с++ (и носит собой в основном
	// задачу избавить код от потенциальных
	// конфликтов с занятыми словами, под
	// какие-либо кунштюки, так как язык очень емкий, и
	// старый), опишем свойства в публичной секции
	// и сразу оттуда будем управлять объектом
public:

	// создадим простые числовые переменные
	// с которыми ассоциируем размеры и расположение
	// нашей кнопки на форме (ширину, высоту, отступ
	// из левого края окна программы, отступ
	// сверху окна программы)
	int Width = 100;
	int Height = 40;
	int Left = 10;
	int Top = 10;

	// код цвета для бордюра кнопки
	// и текста кнопки (аналогично для
	// ситуации, когда мышь над кнопкой)
	COLORREF BorderColor = RGB(0, 0, 0);
	COLORREF TextColor = RGB(0, 0, 0);
	COLORREF BorderActiveColor = RGB(120, 150, 120);
	COLORREF TextActiveColor = RGB(120, 150, 120);

	// переменную, где будет хранится текст
	// который мы будем отрисовывать на кнопке
	// (L перед текстом я использую, потому что
	// у компилятора и создаваемого приложения
	// разные кодировки), не заморачивайтесь,
	// просто делайте также
	LPCWSTR Caption = L"Button";

	// внутренние вспомогательные переменные
	// (вот их имеет смысл описать в приватной секции,
	// ну да ладно)
	int cfg_use_animation = 1;
	int cfg_enable_click_on_button = 1;
	int cfg_enable_border = 1;
	LPCSTR cfg_text_style = "Tahoma";
	int cfg_text_size = 18;
	int help_button_active_status = 0;
	int help_left_window_magrin_size = 8;
	int help_top_window_magrin_size = 51;
	int help_button_first_draw_status = 0;

	//
	//
	// далее опишем тело нашего типа данных (тело класса)
	//
	//


	// создадим "конструктор" для объекта
	// (эта функция будет вызывается, когда
	// мы будет создавать в переменную новый экземпляр
	// этого объекта)
	ObjectButton(int width, int height, int left, int top) {
		Width = width;
		Height = height;
		Left = left;
		Top = top;
	}

	// это "заголовок" функции, которая 
	// не определена, но должна быть у нашего
	// объекта кнопка (то есть эта заготовка,
	// на место которой мы поместим функцию,
	// которая будет выполнятся в момент нажатия
	// кнопки, а поскольку мы описываем тип данных
	// (шаблон), а не конкретную кнопку, мы не знаем
	// (пока) какой функционал будет у этой конкретной кнопки
	// (не обязательно вдавятся в подробности), но о назначении
	// звездочки я говорил в конце этапа 2)
	void(*ButtonClick)();

	// сразу же опишем функцию для проверки
	// координат, в которых происходит нажатие,
	// если координаты пересекаются с ожидаемым
	// расположением нашей кнопки, вызываем функцию
	// ButtonClick, о которой я говорил выше
	// (в качестве аргумента будет передавать уникальный
	// идентификатор окна программы, на которой будет находится
	// создаваемый нами объект кнопка)
	bool CheckMouseCoordinatesBeforeStartClickFunction(HWND form_link) {

		// создадим переменную, в которую
		// получим координаты мыши (текущие),
		// тип данных POINT, по сути, две числовых
		// переменных в одной (такой тип принимает
		// готовая функция для получения координат)
		POINT mouse_coordinates;

		// вызовем функцию (из библиотек),
		// которая позволяет получить в какую-либо переменную
		// текущие координаты мыши (в качестве параметра указываем
		// ранее созданную для этого переменную
		GetCursorPos(&mouse_coordinates);

		// создадим еще одну переменную (типа RECT,
		// он же квадрат, переменная состоит из 4 числовых
		// переменных в одной), именно такую переменную
		// принимает в себя готовая функция из библиотек
		// для получения размеров окна программы, и его
		// расположения на рабочем столе
		RECT program_size_and_position_on_screen;

		// вызовем функцию для получения размеров и позиции
		// окна программы (в качестве параметров указываем
		// идентификатор окна программы, и переменную, с 
		// которой можно ассоциировать результат выполнения функции)
		GetWindowRect(form_link, &program_size_and_position_on_screen);

		// создадим вспомогательную переменную,
		// в которой будем хранить состояние о статусе
		// кнопки (была ли вызвана функция по нажатию,
		// или нет)
		bool help_execute_status = false;

		// создадим "сложносоставное" условие (&& двойной амперсант это
		// логическое и, || а это логическое или),
		// если координаты мыши по оси Х (отступ слева)
		// меньше, чем отступ окна программы (отступ слева у окна), который был
		// приплюсован к отступу слева у кнопки (на форме, в переменной Left), 
		// и (&&) координаты мыши по оси Х меньше, чем отступ окна программы +
		// отступ конкретной кнопки на форме + ширина кнопки + 8 (пикселей бордюра
		// окна программы), тогда выполняем фрагмент кода внутри этого сложного условия
		if (
			mouse_coordinates.x > program_size_and_position_on_screen.left + Left
			&& mouse_coordinates.x < program_size_and_position_on_screen.left + Left + Width
			)
		{
			// выполняем еще одно условие (идентичное первому, но теперь для высоты,
			// то есть вместо Left -> Top, вместо Width -> Height), и таким образом
			// проверяем находится ли мышь в диапазоне от начала кнопки, до ее конца,
			// то есть в рамках размеров кнопки
			if (
				mouse_coordinates.y > program_size_and_position_on_screen.top + Top
				&& mouse_coordinates.y < program_size_and_position_on_screen.top + Top + Height)
			{
				// во вспомогательную переменную отправляем
				// (меняем) значение, которое будет сигнализировать что
				// функция CheckMouseCoordinatesBeforeStartClickFunction
				// вызвала функцию нажатия кнопки
				help_execute_status = true;

				// вызываем саму функцию (которую мы пока не знаем,
				// поэтому просто указываем заголовок этой теоретической
				// функции), но при условии, что в свойстве кнопки (переменной вверху)
				// с названием cfg_enable_click_on_button ассоциированно число 1
				if (cfg_enable_click_on_button == 1) {
					ButtonClick();
				}
			}
			else {
				help_execute_status = false;
			}
		}

		// возвращаем содержимое вспомогательной переменной со статусом нажатия
		return help_execute_status;
	}

	// опишем функцию, эта функция будет проверять находится ли мышь
	// над областью, где находится кнопка (по сути, дублирующийся функционал
	// с кнопкой нажатия, поэтому имеет смысл переписать ту функцию выше,
	// а в функции нажатия уже вызвать эту функцию для проверки координат)
	// отличие этой функции в том, что она может проверять по какой-либо одной 
	// оси, а не по обеим сразу
	bool CheckIsMouseOnThisButtonOrNo(HWND form_link, int axis_direction) {

		// дублируем код аналогично
		// представленному в функции нажатия
		POINT mouse_coordinates;
		RECT program_size_and_position_on_screen;
		//
		GetCursorPos(&mouse_coordinates);
		GetWindowRect(form_link, &program_size_and_position_on_screen);

		// но теперь создаем другую конструкцию условия,
		// если в параметре при вызове функции передано число 0
		// проверяем координаты только по оси Х, иначе по оси У
		if (axis_direction == 0) {

			// условие проверки координат по оси Х
			if (
				mouse_coordinates.x > program_size_and_position_on_screen.left + Left
				&& mouse_coordinates.x < program_size_and_position_on_screen.left + Left + Width
				) {

				// возвращаем истину, если проверка
				// пройдена
				return true;
			}
		}

		// конструкция else встречается впервые, это тот самый блочок кода, который выполняется
		// как альтернатива (если условие не выполнилось), для дополнения условия этим фрагментом,
		// надо просто удалить ; в конце основного условия, добавить слово else и развернуть тело
		// {} блока кода, но конструкция if может работать и без него
		else {

			// все то же самое только теперь по оси У
			if (
				mouse_coordinates.y > program_size_and_position_on_screen.top + Top
				&& mouse_coordinates.y < program_size_and_position_on_screen.top + Top + Height
				) {

				// возвращаем истину, если проверка
				// пройдена
				return true;
			}
		}

		// если проверки не завершили функцию,
		// соответственно координаты неправильные,
		// и завершаем функцию с false
		return false;
	}

	// опишем функцию, которая будет
	// отрисовывать текст (надпись на кнопке),
	// в качестве параметров передаем в функцию
	// указатель на область рисования, текст,
	// цвет отрисовки текста
	void DrawButtonText(HDC hdc, LPCWSTR text, COLORREF color)
	{

		// создаем вспомогательную переменную, с которой
		// ассоциируем результат создания стиля текста,
		// при помощи функции (из библиотек) CreateFontA,
		// функция принимает в себя очень много параметров
		HFONT hFont;

		// ассоциируем с нашей переменной результат
		// выполнения функции для создания нашего 
		// стиля шрифта, при этом в качестве параметров
		// имени шрифта и размера шрифта указываем наши
		// переменные со свойствами стиля текста, остальное
		// оставляем по умолчанию
		hFont = CreateFontA(
			cfg_text_size, // задаем высоту в em
			cfg_text_size / 3, // ширину (в em), делим на 3 от размера текста
			0, // градус наклона (вращение текста)
			0, // градус угла плоскости (наклон горизонта, от которого высчитывается градус наклона)
			FW_NORMAL, // толщина символов (тонкий, средний, толстый, очень толстый и тд)
			false, // рисовать курсивом (да или нет)
			false, // подчеркивать
			false, // зачеркивать
			OUT_DEFAULT_PRECIS, // указатель степени подгона шрифта под заданные параметры
			CLIP_DEFAULT_PRECIS, // указатель степени обрезки букв, которые вылезают за пределы своей области
			DEFAULT_CHARSET, // указатель кодировки для символов
			DEFAULT_QUALITY, // общее качество отрисовки (применения сглаживания, обводки, и тд)
			DEFAULT_PITCH | FF_DONTCARE, // отступ между символами (передаю сразу два 
			// параметра, через условную "запятую", второй
			// параметр указывает может ли расстояние между 
			// символами быть постоянным, или любым,
			// в параметре указано любое расстояние)
			cfg_text_style // имя шрифта
		);

		// вызываем функцию, которая привяжет
		// созданный нами шрифт с областью рисования,
		// в качестве параметров передаем идентификатор области
		// рисования, и наш стиль шрифта
		SelectObject(hdc, hFont);

		// создаем вспомогательную переменную типа SIZE
		// (две числовых переменных в одной), в которую будем
		// получать размер текста в пикселях, для конкретной
		// области рисования (формы программы), из библиотечной
		// (готовой функции)
		SIZE help_text_size;

		// получаю размеры текста в пикселях (в качестве параметров
		// область рисования, текст, размер строки (в байтах)),
		// в переменную, с которой нужно ассоциировать результат выполнения
		// этой функции (help_text_size)
		GetTextExtentPoint32(hdc, text, lstrlen(text), &help_text_size);

		// вызываем другую готовую функцию, эта функция
		// меняет цвет, которым будет напечатан текст,
		// в качестве параметра передаем указатель на область рисования
		// (нашу форму), и цвет текста (из аргумента color)
		SetTextColor(hdc, color);

		// вычисляем середину области кнопки, чтобы
		// отрисовать текст "по центру", для этого из ширины 
		// и высоты кнопки (разделенные на 2) вычитаем
		// ширину, и высоту текста (также разделенную на 2)
		// результат ассоциируем с переменными х и у
		int x = (Width / 2) - (help_text_size.cx / 2);
		int y = (Height / 2) - (help_text_size.cy / 2);

		// вызываем функцию для отрисовки текста на области рисования
		// в качестве параметров функция принимает область рисования,
		// отступ слева (от края формы), отступ сверху (от края формы),
		// текст для рисования, размер текста в байтах (текст берем из
		// второго параметра, получаемого при вызове функции)
		TextOut(hdc, Left + x, Top + y, text, lstrlen(text));
	}

	// теперь опишем функцию для отрисовки рамки у кнопки
	// для этого используем функцию SetPixel (позволяет
	// изменить цвет конкретного пикселя на области рисования),
	// используем циклы и размеры кнопки (из свойств объекта),
	// эта функция не возвращает никаких значений на место своего
	// вызова, поэтому тип данных у функции void (ничего), команда
	// return также не требуется, функция принимает параметры 
	// с указанием области рисования и цветом отрисовки
	void DrawButtonBorder(HDC hdc, COLORREF color) {

		// создадим условие, если значение свойства
		// cfg_enable_border равно 0, завершаем
		// выполнение функции еще до начала
		if (cfg_enable_border == 0)
		{
			return;
		}

		// создаем числовые переменные а и б, ассоциируя с ними
		// свойства объекта, но так чтобы можно было нарисовать
		// прямую линию в цикле, к примеру из верхней левой точки 
		// вправо, изменяя цвет каждого пикселя на пути, до тех пор
		// пока значение переменной старта (а, или левого верхнего края)
		// не станет равным (б, или левого верхнего края + ширина кнопки)
		int a = Left;
		int b = Left + Width;

		// вот пример
		// создаем цикл, до тех пор, пока, а меньше б повторяем код цикла
		while (a <= b) {

			// вызываем функцию изменения цвета пикселя, в качестве параметров
			// передаем указатель на область рисования, координаты оси х и у,
			// новый цвет пикселя (берем из color)
			SetPixel(hdc, a, Top, color);

			// эта упрощенная форма записи a = a + 1;
			// созданная специально для циклов
			a++;
		}

		// аналогичным образом протягиваем линию из правого
		// верхнего края вниз по размеру высоты кнопки
		a = Top;
		b = Top + Height;

		while (a <= b) {
			SetPixel(hdc, Left + Width, a, color);
			a++;
		}

		// еще одну из левого нижнего края, по ширине
		a = Left;
		b = Left + Width;

		while (a <= b) {
			SetPixel(hdc, a, Top + Height, color);
			a++;
		}

		// и последнюю из левого верхнего края по высоте
		a = Top;
		b = Top + Height;

		while (a <= b) {
			SetPixel(hdc, Left, a, color);
			a++;
		}
	}

	// теперь опишем функцию для отчистки области рисования
	void ClearButtonDraw(HWND hwnd) {

		// создадим вспомогательную переменную, с которой
		// ассоциируем координаты и размеры области нашей кнопки,
		// эта переменная нужна для готовой функции, которая очистит
		// область (переменная RECT это 4 числовых переменных в одной)
		RECT help_button_coordinates_and_size;

		// наполняем переменную из свойств нашей кнопки
		// (из значения Left мы вычитаем отступы меню
		// (которое было сгенерировано + бордюр окна, 19 и 32 пикселей),
		// слева вычитаем бордюр окна, 8 пикселей), для того чтобы
		// удалить всю рамку кнопки, вылезаем за 1 пиксель
		// для right и botton (конструкция help_left_window_magrin_size - 1)
		help_button_coordinates_and_size.left = Left - (help_left_window_magrin_size);
		help_button_coordinates_and_size.top = Top - (help_top_window_magrin_size);
		help_button_coordinates_and_size.right = (Left + Width) - (help_left_window_magrin_size - 1);
		help_button_coordinates_and_size.bottom = (Top + Height) - (help_top_window_magrin_size - 1);

		// вызываем функцию очистки
		// (ВАЖНО! даже если указать строго
		// область для стирания, функция
		// все равно сделает невалидной
		// всю область окна программы,
		// соответственно необходимо перерисовать
		// все объекты, но не всегда, типичная
		// наследованная функция с неполной
		// обратно совместимостью), в качестве 
		// параметров область для рисования,
		// координаты (прямоугольник) области
		// стирания, третий параметр ответ на вопрос о
		// стирании
		InvalidateRect(hwnd, &help_button_coordinates_and_size, TRUE);
	}

	// опишем функцию анимации при наведении курсора
	// в качестве параметров функция будет принимать идентификатор
	// окна программы, и указатель на область рисования
	void ActiveButtonAnimate(HWND hwnd, HDC hdc) {

		// создаем условие, которое вызывает нашу функцию для
		// проверки координат мыши, и ее пересечения области кнопки
		if (
			CheckIsMouseOnThisButtonOrNo(hwnd, 0) == true
			&& CheckIsMouseOnThisButtonOrNo(hwnd, 1) == true
			) {
			// добавим условие, завязанное на вспомогательном свойстве объекта
			// оно позволит отрисовывать кнопку один раз при наведении курсора,
			// и один раз при покидании курсором области кнопки (суть в 4 условиях,
			// то есть может быть 4 разных состояния (курсор над кнопкой, но 
			// help_button_active_status равно 0, курсор над кнопкой, но 
			// help_button_active_status равно 1 и тд)
			if (help_button_active_status < 1) {

				// рисуем рамку с цветом из свойств объекта
				DrawButtonBorder(hdc, BorderActiveColor);

				// рисуем текст с цветом из свойств нашего объекта
				DrawButtonText(hdc, Caption, TextActiveColor);

				// изменяем значение вспомогательной переменной на активный
				help_button_active_status = 1;
			}
		}
		// если курсор не над кнопкой проверяем установлено ли
		// help_button_active_status в 1, если да, перерисовываем
		// кнопку не активным, а простым (основным) цветом
		else {
			if (help_button_active_status > 0) {

				// рисуем рамку с цветом из свойств объекта
				DrawButtonBorder(hdc, BorderColor);
				// рисуем текст с цветом из свойств нашего объекта
				DrawButtonText(hdc, Caption, TextColor);
				// изменяем значение вспомогательной переменной на активный
				help_button_active_status = 0;
			}
		}
	}

	// напишем последнюю функцию, это будет функция конструктора
	// на самом деле у нас уже есть функция конструктора (одноименная
	// с названием типа данных, в самом начале), но так как у нас
	// последовательный для чтения код, опишем еще раз
	void Create(HWND hwnd, HDC hdc) {

		// создаем условие (с помощью вспомогательного свойства
		// нашего класса), благодаря которому выполняем функцию
		// Create только один раз после создания объекта, в любой
		// удобный момент
		if (help_button_first_draw_status == 0) {
			help_button_first_draw_status = 1;
		}
		else {
			// так как функция ничего не возвращает на свое место, команду return
			// используем для того, чтобы прервать выполнение функции, в случае
			// если значение вспомогательного свойства help_button_first_draw_status
			// не равно 0 (то есть функция уже была выполнена ранее)
			return;
		}
		// рисуем рамку с цветом из свойств объекта
		DrawButtonBorder(hdc, BorderColor);

		// рисуем текст с цветом из свойств нашего объекта
		DrawButtonText(hdc, Caption, TextColor);
	}

	// это упрощенная функция для отрисовки линий по "перемещению пера",
	// то есть мы сначала перемещаем в какие-то координаты условную точку
	// начала отрисовки, и из нее протягиваем линию в другу точку, потом 
	// снова перемещаем и тд, я просто оставлю это здесь
	void DrawLine(HDC hdc, COLORREF color, int x_from, int y_from, int x_to, int y_to) {

		MoveToEx(hdc, x_from, y_from, NULL);
		LineTo(hdc, x_to, y_to);

	}
};

Итак, теперь у нас есть базовый класс для объекта кнопка, поэтому теперь мы можем перейти непосредственно к основному коду программы.

Для начала подключим наш файл «нашу библиотеку» к основному файлу программы (лучше это сделать вверху, но можно где угодно, главное, чтобы не было логических ошибок (функции вызываются раньше, чем они описаны).

Для этого воспользуемся командой #include.

#include "Source1.cpp"

Переместимся чуть глубже и где ни будь в коде, объявим новые переменные, и сразу ассоциируем с ними результат инициации (создания) этих переменных, у меня это будет секция глобальных переменных.

ObjectButton* MOYA_KNOPKA_AHAHAHA = new ObjectButton(100, 50, 100, 200);

Назвать можно как угодно, у меня это «МОЯ КНОПКА АХАХАХА», которая будет создаваться в координатах 100 пикселей от левого края, 200 сверху, размером 100 на 50 пикселей.

Синтаксис не простой, так как тип данных идентичен с функцией. Звездочка говорит нам о том, что ключевое слово (MOYA_KNOPKA_AHAHAHA) является указателем на данные типа «наш тип данных» (ObjectButton), с которым мы ассоциируем результат выполнения встроенной функции new, которая создает новый экземпляр объекта, попутно вызывая инициирующую функцию (ObjectButton).

Надеюсь, с этим понятно, но даже если нет, ничего страшного, все придет со временем. Наша кнопка создана, но, если запустить программу мы ничего не увидим (потому что кнопка не отрисовывается), поэтому давайте перейдем к секции с «событиями» (это функция WinProc).

И сразу добавим несколько «событий»:

    case WM_CREATE:
    {

    }
    break;

    case WM_LBUTTONDOWN:
    {
        MessageBox(NULL, TEXT("Текст"), TEXT("Заголовок"), 0);

    }
    break;

    case WM_KEYDOWN:
    {

    }
    break;


    case WM_MOUSEMOVE:
    {

    }
    break;

Думаю, из названий понятно, за что отвечают эти события. Для начала дополним событие WM_PAINT (событие отрисовки):

            // добавим условие, если значение свойства
            // help_button_first_draw_status равно 0,
            // значит функция Create еще никогда не выполнялась,
            // после создания нового экземпляра объекта, выполним
            // ее, при этом вычитая размеры бордюра и меню, так как
            // событие отрисовки формы вызывается много раз,
            // в том числе до того, как буду объявлены объекты меню и
            // бардюра окна программы
            if (MOYA_KNOPKA_AHAHAHA->help_button_first_draw_status == 0) {
                MOYA_KNOPKA_AHAHAHA->Left = MOYA_KNOPKA_AHAHAHA->Left - 8;
                MOYA_KNOPKA_AHAHAHA->Top = MOYA_KNOPKA_AHAHAHA->Top - 51;
                MOYA_KNOPKA_AHAHAHA->Create(Form1, hdc);
                MOYA_KNOPKA_AHAHAHA->Left = MOYA_KNOPKA_AHAHAHA->Left + 8;
                MOYA_KNOPKA_AHAHAHA->Top = MOYA_KNOPKA_AHAHAHA->Top + 51;
            }

Запустим программу и увидим нечто подобное (не забывайте указать этот код между BeginPaint и EndPaint).

Отлично! Все проще чем кажется, теперь добавим анимацию:

И события для нажатия, для которого создадим функцию, в которую перетащим код отображения сообщения.

void AHAHAHA_BTN_ShowMessage()
{
    MessageBox(NULL, TEXT("Текст"), TEXT("Заголовок"), 0);
}

В событии создания формы (WM_CREATE) настроим нашу кнопку:

    case WM_CREATE:
    {
        MOYA_KNOPKA_AHAHAHA->ButtonClick = &AHAHAHA_BTN_ShowMessage;
        MOYA_KNOPKA_AHAHAHA->Caption = TEXT("Ткни и увидишь");

    }

Привяжем созданную функцию к событию нажатия кнопки, и изменим текст на кнопке, «условная функция» TEXT() позволяет трансформировать кодировку, обращение к свойствам объекта происходит через «→», но если нажать «.» (точку, выскочит меня со свойствами объекта, и при выборе из меня, точка заменится на стрелочку).

И последняя — это обработка «тыка» мышкой по кнопке:

    case WM_LBUTTONDOWN:
    {
        MOYA_KNOPKA_AHAHAHA->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
    }

Сохраняем, запускаем, видим следующее:

У меня текст вылез за пределы кнопки, но не суть. Не будем останавливается, создаем последний новый файл (у меня по старинке Source2.cpp).

Этап №4 Настолько неописуемо сложно что легко!

На этот раз опишем тип данных для создания объекта, который сможешь загружать, и предоставлять удобную работу с текстовыми файлами (ну и сохранять разумеется), назовем его собирательным термином StringList, подобный класс есть во многих языках программирования.

Ближе к делу (ВПЕРЕД СОБАКИ! К ПОБЕДЕ!)

// подключаем библиотеки
#pragma once 
#include <iostream>
#include <fstream>
#include "windows.h"
#include "winuser.h"
#include "winbase.h"
#include <string> 
#include "stddef.h"
#include <Strsafe.h>
#include <vector>
using namespace std;

// теперь заключительный этап создание кода,
// с минимумом комментариев, и максимумом 
// терминологии (для придания "каноничности"
// нашей программе)
class ObjectTextFileLoader {

    // для простоты понимания объясню один момент,
    // мало кто помнит, но точно также как function, 
    // if, while, switch, и тд, являются 
    // "заголовками конструкций", private секция
    // в классе является заголовком класса, а public его
    // телом (по аналогии с {телом конструкций}), и реализацией
private:

    // путь к файлу
    LPCWSTR HFilePath;

    // оригинальное содержимое файла
    string HText;

    // массив строк файла, так как в с++ нет такого понятия 
    // как "безразмерный массив", который можно было бы
    // дополнить, определим переменную типа vector (из 
    // библиотеки std), она подразумевает под собой аналог массива
    std::vector<std::string> HStringsList;

public:


    // конструктор класса (функция, которая выполняется
    // в момент создания нового экземпляра объекта)
    ObjectTextFileLoader(LPWSTR file_path)
    {
        // перенесем путь к файлу (из аргумента функции)
        // в свойство FilePath (переменную в классе, или
        // конкретном экземпляре объекта)
        HFilePath = file_path;
    }

    // загрузка и чтение файла, для этого мы используем
    // функцию CreateFileW, которая подготовит файл для чтения
    // 
    LPSTR LoadFromFile(LPCWSTR file_path)
    {
        HFilePath = file_path;

        // открываем файл универсальной командой для создания,
        // перезаписи файла
        HANDLE original_file = CreateFileW(HFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        // создаем переменную file_size типа DWORD (разновидность
        // числовой переменной, для работы с двоичными числами),
        // с переменной ассоциируем результата выполнения функции
        // GetFileSize, чтобы получить размер загружаемого файла
        // в битах
        DWORD file_size = GetFileSize(original_file, NULL);

        // создаем вспомогательные переменные
        // для чтения данных из файла, и переноса
        // в переменную внутри программы,
        // для этого создаем числовую переменную
        // current_read_position (нужна для функции
        // ReadFile, для реализации частичного, или
        // последовательного (полного) чтения файла),
        // в данном случае переменная не используется,
        // но обязательна для функции
        DWORD current_read_position = 0;

        // создаем вспомогательный обработчик для создания
        // переменной tmp_result, с которым ассоциируем результат
        // выделения памяти в программе, размером в file_size,
        // при этом сразу заполняем выделенную память нулями
        HGLOBAL handler_tmp_result = GlobalAlloc(GHND, file_size);

        // фиксируем выделенный фрагмент памяти (для защиты от
        // использования в других решениях, внутри нашей программы),
        // в качестве временного хранилища (то есть для сохранения
        // целостности загружаемых из файлов данных), который сразу 
        // описываем как тип данных LPSTR (функция ReadFile
        // может принимать в себя любой тип данных переменной,
        // в которую нужно направлять прочитанное)
        LPSTR tmp_result = (LPSTR)GlobalLock(handler_tmp_result);

        // вызываем функцию ReadFile (в качестве аргументов передаем
        // переменную с "файловым потоком", хранилище для перемещения считанных
        // данных, общие количество байтов, которые нужно прочитать, позицию, с
        // которой нужно начинать чтение (мы загружаем содержимое
        // файла целиком, и сразу помещаем в переменную tmp_result, но лучше
        // выполнять фрагментированное чтение (с фиксированным количеством
        // байтов, читаемых за 1 вызов функции, к примеру 1024) через промежуточную
        // переменную, в цикле (чтобы сразу интерпретировать, или дешифровывать
        // прочитанные данные), последней аргумент (NULL), предназначен для случаев,
        // когда файл открыт не в режиме чтения, а с мультидоступом (чтение, запись,
        // изменение другими программами, и тд), для смещения в соответствующую
        // позицию байт
        ReadFile(original_file, tmp_result, file_size, &current_read_position, NULL);

        // разблокируем, ранее зафиксированный кусок памяти, так как
        // переменная уже наполнена (использовать ее просто невозможно)
        GlobalUnlock(handler_tmp_result);

        // закрываем файловый поток, так как файл больше не нужен
        CloseHandle(original_file);

        // вернем результат
        HText = tmp_result;

        // выводим сообщение
        MessageBox(NULL, TEXT("Текст был загружен из файла"), TEXT("Показать содержимое из текста"), 0);
        return tmp_result;

    }

    // добавим функцию для конвертирования строки string в строку LPCWSTR,
    // на самом деле у нас уже есть такая функция (в другом классе), но
    // так как функция не большая, ничего страшного не случиться если ее 
    // продублировать (кроме того, архитектура нашей программы не предполагает
    // использование компонентов программы между собой, так как программа
    // спроектированна по схеме зависимостей "все к одному")
    LPCWSTR ConvertToLPCWSTR(string a) {
        LPWSTR tmp_return = new WCHAR[a.length()];
        MultiByteToWideChar(
            CP_ACP,
            MB_ERR_INVALID_CHARS,
            a.c_str(),
            a.size(),
            tmp_return,
            a.length()
        );
        tmp_return[a.length()] = 0;
        return tmp_return;
    }

    // добавим функцию для добавления
    // текста в конец оригинального
    // содержимого файла
    void Append(string text)
    {
        // используем стандартную функцию
        // из класса string
        HText.append(text);
    }

    // добавим функцию для получения
    // конкретной буквы, исходя из индекса 
    // этой буквы в тексте
    string GetOneLetterByIndex(int letter_index)
    {
        string tmp_return = "";
        // for это разновидность цикла, внутри позволяет
        // сразу определить счетчик, условие, и операция для повтора
        for (int i = 0; i < HText.length(); i++)
        {
            // если индекс счетчика равен
            // индексу аргумента возвращаем результат
            // и завершаем цикл
            if (i == letter_index)
            {
                tmp_return = HText[letter_index];
                i = HText.length();
            }
        }
        return tmp_return;
    }

    // добавим функцию для добавления новых строк
    // в HStringsList
    void AddEnd(int count)
    {
        // получаем в переменную текущее количество
        // строк в HStringsList, к которому добавляем
        // count
        int a = Count() + count;

        // имзеняем размер на новый
        HStringsList.resize(a);
    }

    // добавим функцию для получения количества строк в HStringList
    int Count()
    {
        return HStringsList.size();
    }

    // добавим функцию для показа на экране
    // содержимого конкретной строки
    void ShowStringText(int string_index)
    {
        // вызываем диалоговое окно, в котором отображаем
        // содержимое строки из текста
        MessageBox(NULL, ConvertToLPCWSTR(HStringsList[string_index]), TEXT("Показать содержимое из текста"), 0);
    }

    // создадим функцию для разбора
    // текста файла по отдельным строкам
    void DetouchInToStringList()
    {
        //
        //
        // Сначала получаем количество строк из текста
        // (было бы проще сделать для этого отдельную функцию)
        //
        //

        // создаем переменную счетчик строк из текста
        int strings_count = 0;

        // если хранилище текста пустое
        // завершаем функцию
        if (HText.empty() == true)
        {
            // exit аналог прерывания функции
            // (ну точнее return выполняется
            // аналогично exit, но не суть)
            exit;
        }

        // создадим переменные для цикла
        int a = 0;
        int b = HText.length();

        // запускаем цикл (это инвертированный цикл),
        // он создан чтобы действия внутри цикла 
        // были выполнены хотя бы один раз, даже
        // если условие цикла не выполняется
        do {
            
            // если код символа (текущей буквы) соответствует коду
            // ключевого слова, которое означает символ (символ n 
            // через косую черту, я говорил об этом раньше "\n")
            // переноса на новую строку, выполняем код условия
            // (на самом деле это два кода #10+#13, 10 завершение
            // строки, 13 новая строка)
            if (HText[a] == 13)
            {
                strings_count = strings_count + 1;
            }
            a++;
        } while (a < b);

        // по завершении цикла добавляем последнюю строку
        strings_count = strings_count + 1;

        //
        //
        // Теперь когда мы знаем количество строк в оригинальном тексте
        // изменяем размер HStringsList, вырезаем из текста переносы строк
        // и перемещаем этот текст в соответствующие строки массива HStringsList
        //
        //

        // переопределим количество строк в HStringsList
        HStringsList.resize(strings_count);

        // создаем (переопределим, они уже созданы) переменные
        a = 0;
        b = HText.length();

        // первый символ текста теряется (не понятно куда,
        // предположу кривизна компилятора, или программы в 
        // работе с памятью), поэтому добавим его
        string c = GetOneLetterByIndex(0);
        int d = 0;

        // запускаем цикл 
        do {

            // если код символа соответствует коду
            // ключевого слова, которое означает символ (\n)
            // переноса на новую строку, выполняем код условия
            if (HText[a] == 13)
            {
                // помещаем значение в строку в массиве строк
                HStringsList[d] = c;
                c = "";
                d++;
            }
            a++;

            // помещаем символ в переменную, которая
            // собирает строку, но при условии, что код символа
            // не соответствует символу переноса на новую строку
            if (HText[a] != '\n')
            {
                c = c + HText[a];
            }
        } while (a < b);

        // заполняем последнюю строку, так как
        // наша переменная strings_count содержи количество
        // строк, а счет массива HStringsList начинается с 0,
        // чтобы получить индекс последней строки strings_count - 1
        HStringsList[strings_count - 1] = c;
    }

    // создадим функцию для изменения
    // значений в HStringsList
    void ChangeInString(int index, string new_value)
    {
        HStringsList[index] = new_value;
    }

    // создадим функцию для получения содержимого строки
    string Strings(int index)
    {
        return HStringsList[index];
    }

    // и ее альтернативу для LPCWSTR
    LPCWSTR StringsA(int index)
    {
        return ConvertToLPCWSTR(HStringsList[index]);
    }

    // создадим функцию для сохранения файла
    void SaveToFile(LPCWSTR file_path)
    {
        // создаем вспомогательные переменные
        string tmp_buffer = "";
        int a = 0;
        int b = Count() - 1;

        // запускаем цикл для сборки
        // нового содержимого файла
        // из HStringsList
        while (a < b)
        {
            tmp_buffer = tmp_buffer + HStringsList[a] + "\n";
            a++;
        }

        // так как в последней строке указатель новой строки не нужен
        // добавляем последнюю строку без него
        tmp_buffer = tmp_buffer + HStringsList[a];

        // конвертируем новое содержимое в LPCWSTR (других функций под рукой нет)
        LPCWSTR tmp_result = ConvertToLPCWSTR(tmp_buffer);

        // получаем количество байтов для записи
        DWORD file_size = tmp_buffer.length() * sizeof(wchar_t);

        // создаем переменную (счетчик записанных байтов)
        DWORD current_read_position = 0;

        // создаем файловый поток (теперь GENERIC_WRITE в режиме записи)
        HANDLE original_file = CreateFileW(file_path, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

        // записываем в файл все за один раз (так лучше не делать)
        WriteFile(original_file, tmp_result, file_size, &current_read_position, NULL);

        // закрываем файловый поток
        CloseHandle(original_file);

        // вызываем сообщение
        MessageBox(NULL, TEXT("Текст был записан в файл"), TEXT("Показать содержимое из текста"), 0);
    }
};

Ну и немного переделаем код программы (приведем его в порядок), добавим несколько кнопок, добавим работу с клавишами, надписи (можно поиграться со свойствами cfg в нашей кнопке, и уже из одной кнопки у нас получается 3 компонента, кнопка, текст, панель), работу с файлом, ну и так по мелочи.

Пара па па

// WindowsProject1.cpp : Определяет точку входа для приложения.
//

// этот кусок можно пропустить
// директива pragma once подразумевает
// исключение логического бага в программе,
// который может произойти из-за команды для
// подключения библиотек (файлов с заготовками кода)
// include, которая просто вставляет на свое место
// все содержимое файла, который был указан
// (включая и команды include внутри подключаемого
// файла) при вызове, из за чего могу возникать логические
// ошибки и множественные самоподключения (когда
// одна библиотека ссылается на вторую, а вторая на
// первую), в двух словах все исходники собираются
// в одни длинный исходный код (командами include),
// а команда pragma once удаляет все дубликаты,
// оставляя только самую первую include,
// лично я начинаю любой файл с pragma once,
// что бы не беспокоится о зависимостях, но это
// не обязательно (кароче пока не заморачивайтесь)
#pragma once 

// и начать отсюда
// подключаем вспомогательные библиотеки
// наборы готового кода как в этом файле, 
// где уже описанные какие-то функции (заготовки) для
// реализации и запуска нашей программы
#include "framework.h"
#include "WindowsProject1.h"
#include "windows.h"
#include "winuser.h" 
#include <string> 
#include <stdlib.h>  
using namespace std;

#include "Source.cpp"
#include "Source1.cpp"
#include "Source2.cpp"

// определим вспомогательный термин 
// (читай создадим разновидность переменной)
// под названием MAX_LOADSTRING и ассоциируем
// с ней число 999, теперь везде в коде, где 
// будет встречается термин MAX_LOADSTRING
// он будет заменятся на 999
#define MAX_LOADSTRING 999

// Глобальные переменные:
// 
// hInst это глобальная переменная экземпляра нашей программы
HINSTANCE hInst;                                // текущий экземпляр

// я сделаю глобальную переменную для простоты понимания
// с которой ассоциирую уникальный идентификатор нашего окна
// программы
HWND Form1;

// создадим новую переменную типа наша кнопка,
// с которой ассоциируем результат создания 
// нового экземпляра объекта (звездочка (*)
// после ObjectButton обозначает (для компилятора)
// что мы не вызываем одноименную функцию из файла Source1.cpp,
// а обращаемся к типу данных ObjectButton, после чего уже
// вызываем конструктор с функцией ObjectButton (не нужно сейчас над этим
// заворачивается все придет со временем)

// Создадим еще несколько объектов кнопка
ObjectButton* lbl_text_hello = new ObjectButton(400, 50, 150, 200);
ObjectButton* btn_change_windows_size = new ObjectButton(150, 50, 50, 320);
ObjectButton* lbl_text_hide_button = new ObjectButton(550, 50, 210, 380);
ObjectButton* btn_show_message = new ObjectButton(150, 50, 50, 440);
ObjectButton* btn_exit = new ObjectButton(150, 50, 50, 560);

ObjectButton* btn_load_file = new ObjectButton(150, 50, 50, 380);
ObjectButton* btn_save_file = new ObjectButton(150, 50, 50, 440);
ObjectButton* btn_show_file = new ObjectButton(150, 50, 50, 500);
ObjectButton* btn_add_one = new ObjectButton(150, 50, 210, 500);

// теперь создадим новый экземпляр нашего объекта ObjectTextFileLoader
ObjectTextFileLoader* TextLoader1 = new ObjectTextFileLoader((LPWSTR)L"test.txt");

// создадим функцию, которая будет меня положение окна программы,
// и размер окна программы, в качестве параметров в функцию будем
// передавать размер окна программы (двумя параметрами - ширина и высота)
void execute_application_postion_and_size(HWND Form, int width, int height) {

    // системная функция ожидания
    // в качестве параметра принимает
    // период ожидания в мс (1000 
    // соответствует 1 секунде)
    Sleep(1000);

    // создадим переменные, с которыми ассоциируем
    // результат выполнения функции для получения
    // информации о компьютере, с параметров SM_CXSCREEN,
    // требующим ширину экрана в пикселях
    int screen_width = GetSystemMetrics(SM_CXSCREEN);

    // аналогично для высоты экрана
    int screen_height = GetSystemMetrics(SM_CYSCREEN);

    // создадим две переменные, с новыми размерами окна
    int program_width = width;
    int program_height = height;

    int program_left = (screen_width / 2) - (program_width / 2);
    int program_top = (screen_height / 2) - (program_height / 2);

    // изменим положение окна (при помощи готовой функции),
    // разместив окно по центру экрана
    SetWindowPos(
        Form,
        HWND_TOP,
        program_left,
        program_top,
        program_width,
        program_height,
        SWP_ASYNCWINDOWPOS
    );
}
// опишем ее альтернативный вариант без аргументов
void execute_application_postion_and_sizeA() {
    execute_application_postion_and_size(Form1, 1024, 700);
}

// опишем вспомогательную функцию для начальной отрисовки
void ExecuteMyBotton(ObjectButton* btn, HDC Canvas) {
    if (btn->help_button_first_draw_status == 0) {
        btn->Left = btn->Left - 8;
        btn->Top = btn->Top - 51;
        btn->Create(Form1, Canvas);
        btn->Left = btn->Left + 8;
        btn->Top = btn->Top + 51;
    }
}

// опишем функцию выхода из программы              
void ExitFromProgram() {

    // вызовем функцию для завершения программы,
    // все программы во всех системах (на всех языках)
    // завершаются кодом 1, в случае завершения без
    // сбоев, и 0 (со сбоями)
    PostQuitMessage(1);
}

// опишем функцию для загрузки файла test.txt из
// папки с программой (если файл находится в одной
// директории с программой не надо указывает полный путь)
void LoadFileFromComputer()
{ 

    // создадим переменную пути
    // я использую путь к моему файлу на диске
    // (прямой), потому что для указания релитивного
    // пути нужyа библиотека, которой нет в стандартном
    // проекте программы (который создан в стандарте
    // с++ 14), в то же время функции из библиотеки 
    // shellapi не представляется возможным использовать 
    // так как они не имеют полной обратной совместимости
    // с windows 10 и ограничения на размер пути, 
    // такой вот порочный круг, а еще мне лень, поэтому так,
    // насколько я помню вызов стандартного диалога выбора
    // файла решает эту проблему (но эт не точно, так как я
    // использую другой язык (в повседневности), а с++ (нативным)
    // давно не работаю, двойной \\ слеш в пути потому 
    // что символ \ зарезервирован редактором для ключевых обозначений
    string file_path = "D:\\a\\test.txt";
     
    // загрузим файл   
    TextLoader1->LoadFromFile((LPWSTR)TextLoader1->ConvertToLPCWSTR(file_path));

    // разберем на строки
    TextLoader1->DetouchInToStringList();
}

// опшием функцию для сохранения файла test.txt
void SaveToFileInComputer()
{
    // создадим переменную с путем к файлу
    LPWSTR fp = (LPWSTR)L"D:\\a\\test.txt";

    // сохраним файл
    TextLoader1->SaveToFile(fp);
}

// создадим функцию для отображения содержимого 
void ShowContent()
{
    // соберем текст в переменную, 
    // выполним текстовую конкатенацию 
    // (от слова контакт),
    // функция std::to_string позволяет 
    // трансформировать число в строку
    string a = "Содержимое строки #" + std::to_string(TextLoader1->Count()) + " - " + TextLoader1->Strings(TextLoader1->Count() - 1);

    // вызовем диалог с сообщением
    MessageBox(NULL, TextLoader1->ConvertToLPCWSTR(a), TEXT("Показать содержимое из текста"), 0);
}

// создадим функцию для добавления строки в конец файла
void AddOneString()
{
    // добавим 1 строку в конец HStringList
    TextLoader1->AddEnd(1);

    // создадим переменную для текста строки
    string a = "New String #" + std::to_string(TextLoader1->Count());

    // изменим содержимое последний строки на содержимое переменной а
    TextLoader1->ChangeInString(TextLoader1->Count() - 1, a);

    // вызовем диалог с сообщением
    MessageBox(NULL, TEXT("Добавлена 1 строка в конец файла!"), TEXT("Показать содержимое из текста"), 0);
}

// в общем то создаем пару переменных типа "буквенный массив",
// и сразу задаем его длину в параметре [MAX_LOADSTRING]
WCHAR szTitle[MAX_LOADSTRING];                  // Текст строки заголовка
WCHAR szWindowClass[MAX_LOADSTRING];            // имя класса главного окна

// Отправить объявления функций, включенных в этот модуль кода:
// 
// эти вспомогательные функции сейчас не важны, по сути, они просто "нормализуют"
// нашу программу для системы, создают новый экземпляр окна (из заготовок в системе) 
// и добавляют мониторинг действий (нет смысла вдаваться в подробности, и забивать голову)
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

// создаем функцию
// эта функция принимает в себя несколько параметров,
// это "точка входа", когда программа запускается, она 
// автоматически начинает выполнять эту функцию
//
// эта функция будет обеспечивать связь системы и нашей программы
// при помощи бесконечного цикла, который повторяется до тех пор, 
// пока программа запущена, и перехватывает сообщения от системы
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    // эти строки сгенерированы автоматически
    // с ними пока заморачиваться не нужно, они вспомогательные для компилятора
    // в двух словах это еще одна разновидность переменной,
    // с которой ассоциированные вспомогательные функции для освобождения
    // памяти от неиспользуемых переменных
    //UNREFERENCED_PARAMETER(hPrevInstance);
    //UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Разместите код здесь.

    // Инициализация глобальных строк
    // 
    // подгружаем из ресурсов нашего файла название окна программы для системы
    // и параметры самого окна (в данном случае окно типа "полноценное" с изменяемым
    // размером, сворачиванием, и тд)
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);

    // адаптируем нашу программу для системы при помощи функции
    // MyRegisterClass, в качестве параметра передаем идентификатор
    // нашего окна программы, который получаем из текущей функции 
    // (wWinMain) в параметре hInstance
    MyRegisterClass(hInstance);

    // Выполнить инициализацию приложения:

    // создаем условие, если функция вернула на место своего вызова
    // значение false, вызываем встроенное в идею функций ключевое слово
    // return, при встрече которого любая функция прекращает построчное выполнение
    // кода, и возвращаем ложь в функции wWinMain (в место вызова функции)
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }


    // это вспомогательный код для отлова событий нажатия клавиш,
    // ассоциируем с переменной hAccelTable (типа HACCEL) результат
    // выполнения функции LoadAccelerators, сама функция извлекает из
    // ресурсов нашего файла готовые схемы сочетания клавиш и названия функций
    // для этих сочетаний
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));

    // создадим переменную типа MSG (сложносоставной тип данных) под названием MSG
    // в эту переменную будут попадать команды, которые в цикле будут перехватываться 
    // универсальной функцией GetMessage
    MSG msg;

    // Цикл основного сообщения:

    // создаем цикл, до тех пор пока GetMessage, с пустыми параметрами 
    // (2,3,4, не нужно вдавятся сейчас в подробности почему)
    // возвращает какой-либо результат (в переменную msg) 
    // выполняем код в теле цикла
    //
    // важный момент, почему &msg (ассоциация значений), а не
    // просто msg (присвоение значений)? потому что если создать 
    // "условный" объект кнопка, задать ему какие-либо параметры, 
    // после чего создать еще один, и попробовать скопировать параметры 
    // простым присвоением, присвоены будут не параметры кнопки, 
    // а сама кнопка (объект), следовательно просто кнопка один 
    // будет также доступна под названием кнопка два
    while (GetMessage(&msg, nullptr, 0, 0))
        // это начало тела цикла
    {
        // нестандартная конструкция условия, она подразумевает
        // простую логику, если не "!" результат выполнения функции
        // TranslateAccelerator (функция для горячих клавиш)
        // то выполняем код в условии, иначе говоря, если 
        // нажатая клавиша не выполнила функцию, которая ей
        // назначена, то выполняем код условия, а именно обрабатываем 
        // горячую клавишу (не обязательно вдавятся в подробности 
        // нам сейчас это не нужно)
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            // код обработки нажатий горячих клавиш
            // которые были указаны в схеме, которая
            // была загружена из ресурсов ранее
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // это конец тела цикла

    // возвращаем результат выполнения функции
    // в качестве подстановочного значение 
    // (которое встает в никуда) возвращаем параметры 
    // последней команды GetMessage
    return (int)msg.wParam;
}



//
//  ФУНКЦИЯ: MyRegisterClass()
//
//  ЦЕЛЬ: Регистрирует класс окна.
//

// выше уже была упомянута эта функция, она адаптирует
// в рамках системы нашу программу, вот, собственно, само 
// содержимое это функции, оно нам не интересно, двигаемся дальше
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    // просто настраиваем параметры окна программы
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
//   ЦЕЛЬ: Сохраняет маркер экземпляра и создает главное окно
//
//   КОММЕНТАРИИ:
//
//        В этой функции маркер экземпляра сохраняется в глобальной переменной, а также
//        создается и выводится главное окно программы.
//

// я думаю из сгенерированных комментариев понятно, для чего эта функция
// так же выше было сказано об этой функции, ее тоже можно пропустить
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Сохранить маркер экземпляра в глобальной переменной

    // разве что вот ключевая функция для создания окна, которая называется
    // CreateWindow (с модификатором W, то есть одна из вариаций этой функции)
    // и возвращает идентификатор на место своего вызова, который мы ассоциируем
    // с глобальной переменной, в которой держим уникальный идентификатор нашего окна
    // программы
    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    // добавим условие, если "не hWnd" (то есть
    // нет ассоциированного объекта) возвращаем FALSE
    // и завершаем выполнение функции
    if (!hWnd)
    {
        return FALSE;
    }

    // после создания окна программы вызываем функции для отображения
    // окна программы и его перерисовки
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  ЦЕЛЬ: Обрабатывает сообщения в главном окне.
//
//  WM_COMMAND  - обработать меню приложения
//  WM_PAINT    - Отрисовка главного окна
//  WM_DESTROY  - отправить сообщение о выходе и вернуться
//
//
// еще одна сгенерированная функция, эта функция подвязывается
// к нашей форме, и обрабатывает события нашей формы (к примеру
// изменение размера окна, тык мышкой, активность формы и тд)
// это основная функция нашей программы
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // добавим ассоциацию идентификатора окна программы
    // с нашей глобальной переменной, в которой храним
    // указатель на идентификатор
    Form1 = hWnd;

    // switch разновидность условия, не совсем понятно
    // целесообразность использования альтернативного
    // условного конструктора, поскольку то же самое 
    // можно было описать и конструкцией if (но предположу
    // что несмотря на то что switch не является "канонистической 
    // конструкцией, она обеспечивает одно единственное выполнение
    // условия, затем завершает, это может обеспечивает стабильность,
    // избегая множественных срабатываний на одну команду message)

    // в данном случае условие обрабатывает сообщения,
    // которые получает из GetMessage, в соответствии
    // с определенным содержимым message (который передается
    // в параметрах функции), и если значение message соответствует
    // какой либо конструкции case, выполняет этот фрагмент кода
    switch (message)
    {
        // не стоит забывать про зажатую Ctrl и левый тык мышкой
        // это перекинет в файл, где описано это ключевое слово
        //
        // WM_COMMAND это универсальное событие обработки команд
        // (к примеру отправленных через SendMessage)	
        case WM_COMMAND:
        {
            // тут обработка элементов меню
            // сами элементы в ресурсах файла, 
            //сгенерированы автоматически
            int wmId = LOWORD(wParam);
            // Разобрать выбор в меню:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
        //
        // 
        // дополним наш код новым условием обработки
        // если message соответствует значению WM_LBUTTONDOWN
        // выполняем код в условии (WM_LBUTTONDOWN левый тык мышки)
        //
        //
        // это событие создания окна программы
        // в нем ассоциируем со свойством нашего объекта кнопка (под
        // названием ButtonClick, ссылку на функцию, которую мы недавно 
        // описали, а также настраиваем другие нужные параметры)
        case WM_CREATE: {
            execute_application_postion_and_size(Form1, 900, 700);

            btn_change_windows_size->ButtonClick = &execute_application_postion_and_sizeA;
            btn_change_windows_size->Caption = TEXT("Изменить размер окна");

            lbl_text_hello->Caption = TEXT("Я у мамки программииииист!");
            lbl_text_hello->cfg_enable_border = 0;
            lbl_text_hello->cfg_text_size = 48;
            lbl_text_hello->TextColor = RGB(126, 134, 231);


            lbl_text_hide_button->Caption = TEXT("<- Нажми Esc чтобы скрыть эту кнопку и Q чтобы снова показать!");
            lbl_text_hide_button->cfg_enable_border = 0;
            lbl_text_hide_button->cfg_text_size = 24;
            lbl_text_hide_button->TextColor = RGB(126, 190, 231);

            btn_exit->Caption = TEXT("Выход");
            btn_exit->TextColor = RGB(255, 52, 9);
            btn_exit->BorderColor = RGB(255, 52, 9);
            btn_exit->ButtonClick = &ExitFromProgram;

            btn_load_file->Caption = TEXT("Открыть файл");
            btn_load_file->ButtonClick = &LoadFileFromComputer;

            btn_show_file->Caption = TEXT("Последняя строка");
            btn_show_file->ButtonClick = &ShowContent;

            btn_save_file->Caption = TEXT("Сохранить");
            btn_save_file->ButtonClick = &SaveToFileInComputer;

            btn_add_one->Caption = TEXT("Добавить строку");
            btn_add_one->ButtonClick = &AddOneString;
        }
        break;

        // добавим событие нажатия кнопки клавиатуры
        // (есть несколько состояний, нажимается, зажата, отпускается),
        // внутри события добавим код, который будет проверять полученное
        // (в wParam) значение, во время вызова события WM_KEYDOWN
        case WM_KEYDOWN: {

            // создаем условие, если нажата клавиша Esc,
            // удаляем с экрана кнопку
            if (wParam == VK_ESCAPE)
            {
                btn_load_file->ClearButtonDraw(Form1);
            }

            // добавим еще одно
            // 
            // если функция (из библиотек), дял получения
            // текущей нажатой клавиши с заданным кодом клавиши Q (0x51)
            // вернула код, соответствующий состоянию нажатия и удерживания,
            // снова отрисовываем кнопку (при помощи изменения вспомогательного
            // свойства help_button_active_status, которое намеренно устанавливаем
            // в 1, чтобы функция (ActiveButtonAnimate) подумала, что сейчас кнопка 
            // отрисаована в активном состоянии), получив область рисования при помощи
            // другой функции из библиотек (GetWindowDC), конструкция условия
            // использует & потому что мы ассоциируем битовые данные (коды состояния
            // клавиш), а компилятор не способен это трансформировать в логическое условие
            // соответствия (==), функция GetAsyncKeyState возвращает на место своего вызова
            // код, дополняющий 0x8000 таким образом чтобы вместе они сделали 0x0001, или
            // битовое выполнение конструкции условия, через параметр истины (отправку кода
            // "истины" в условие), соответствующий истине в архитектуре конструкции if (не надо над 
            // этим заморачиваться и забивать себе голову)
            if (0x8000 & GetAsyncKeyState(0x51)) {
                HDC hdc = GetWindowDC(Form1);
                btn_load_file->help_button_active_status = 1;
                btn_load_file->ActiveButtonAnimate(Form1, hdc);
            }

        }
        break;

        // добавим событие срабатывающие при перемещении
        // мыши, в него добавим функцию, которая вызывает
        // проверку и анимацию кнопки, так как наша функция требует
        // в себя область рисования, получаем идентификатор
        // области рисования в переменную соответствующего типа
        // по результату вызова функции GetWindowDC (в которую
        // передаем идентификатор окна программы)
        case WM_MOUSEMOVE: {
            HDC hdc = GetWindowDC(Form1);
            btn_change_windows_size->ActiveButtonAnimate(Form1, hdc);
            btn_exit->ActiveButtonAnimate(Form1, hdc);

            btn_load_file->ActiveButtonAnimate(Form1, hdc);
            btn_show_file->ActiveButtonAnimate(Form1, hdc);
            btn_save_file->ActiveButtonAnimate(Form1, hdc);
            btn_add_one->ActiveButtonAnimate(Form1, hdc);
        }
        break;

        // добавим событие, выполняющееся при нажатии левой кнопки мыши
        // внутри события добавим срабатывание функции 
        // CheckMouseCoordinatesBeforeStartClickFunction
        case WM_LBUTTONDOWN: 
        {
            btn_change_windows_size->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
            btn_exit->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
             
            btn_load_file->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
            btn_show_file->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
            btn_save_file->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
            btn_add_one->CheckMouseCoordinatesBeforeStartClickFunction(Form1);
        }
        break;

        // если условие соответствует событию отрисовки выполняем код внутри
        case WM_PAINT:
        {
            // создаем переменную типа параметры
            // области рисования
            PAINTSTRUCT ps;

            // создаем переменную типа область рисования
            // и ассоциируем с ней результат выполнения функции
            // BeginPaint (в параметрах которой передаем
            // идентификатор нашего окна, и ассоциируем данные
            // из переменной с параметрами
            HDC hdc = BeginPaint(hWnd, &ps);

            // добавим условие, если значение свойства
            // help_button_first_draw_status равно 0,
            // значит функция Create еще никогда не выполнялась,
            // после создания нового экземпляра объекта, выполним
            // ее, при этом вычитая размеры бордюра и меню, так как
            // событие отрисовки формы вызывается много раз,
            // в том числе до того, как буду объявлены объекты меню и
            // бардюра окна программы
            if (lbl_text_hello->help_button_first_draw_status == 0) {
                lbl_text_hello->Left = lbl_text_hello->Left - 8;
                lbl_text_hello->Top = lbl_text_hello->Top - 51;
                lbl_text_hello->Create(Form1, hdc);
                lbl_text_hello->Left = lbl_text_hello->Left + 8;
                lbl_text_hello->Top = lbl_text_hello->Top + 51;
            }

            // чтобы не дублировать код исключения (описан сверху),
            // и не вмешивается в целостность нашего класса кнопки
            // имеет смысл описать функцию, которая будет в себя
            // принимать переменную с объектом кнопка, и в функции
            // делать тоже самое, а в месте, где нужен этот код
            // вставим эту функцию
            ExecuteMyBotton(btn_change_windows_size, hdc);
            ExecuteMyBotton(lbl_text_hello, hdc);
            ExecuteMyBotton(lbl_text_hide_button, hdc);
            ExecuteMyBotton(btn_exit, hdc);

            ExecuteMyBotton(btn_load_file, hdc);
            ExecuteMyBotton(btn_show_file, hdc);
            ExecuteMyBotton(btn_save_file, hdc);
            ExecuteMyBotton(btn_add_one, hdc);


            // TODO: Добавьте сюда любой код прорисовки, использующий HDC...
            // завершаем процесс рисования,
            // вызывая специальную функцию
            EndPaint(hWnd, &ps);
        }
        break;

        // если переменная message 
        // содержит WM_DESTROY
        // выполняем код для завершения
        // работы приложения
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
            // если message не содержит никаких сообщений
            // (или сообщения не соответствуют ни одному из условий)
            // выполняем другой код, функцию, которая гарантирует обработку
            // "неопределенного" message, она нас тоже пока не интересует
            // (но может понадобится, так как мы можем ее "переописать",
            // для каких-либо наших задач)
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }


        // вернем результат выполнения функции 0
        // можно изменить на 1 чтобы мониторить 
        // работоспособность программы, исходя
        // из возвращаемого значения этой функции
        // (то есть если функция WndProc выполнилась
        // и не вернула на место вызова 1, значит
        // какое то решение не работает, для этого
        // просто можно создать переменную (в начале
        // блока кода, с которой ассоциировать что
        // нибудь (к примеру 0), и если функция смогла
        // выполнить свой код до этой позиции, возвращяем 1
    return 0;
}

// Обработчик сообщений для окна "О программе".
// аналогично только не для основного окна программы,
// а для диалога "Об программе", его разбирать смысла нет
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

В итоге должно получится это:

Поздравляю, теперь ты полноценный Junior C++ Desktop Developer, если сверху изучить работу с памятью и работу с базами данных, будешь уже Middle C++ Engine Developer, ну а если овладевать всеми тонкостями нет смысла LOL, поэтому добавить сверху только знания библиотек и с++ последней версии, и получиться «ценный специалист».

На это пожалуй все.

Что можно сказать по итогу: Теперь ты знаешь как создавать графические приложения на С++, знаешь базовые понятия классов (и самое главное умеешь их использовать), знаком с азами работы в RunTime, и умеешь работать с файлами.

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

Спасибо за внимание!

Последнее обновление: 25.11.2023

Для создания графических приложений на C# можно использовать .NET CLI, но также можно
использовать бесплатную и полнофункциональную среду разработки — Visual Studio Community 2022, которая в ряде случаев облегчает проектирование
приложения. Так, загрузим установщик Visual Studio по адресу:
https://www.visualstudio.com/en-us/downloads.

Установка Visual Studio для Windows Forms

Чтобы добавить в Visual Studio поддержку проектов для Windows Forms и C# и .NET, в программе установки среди рабочих нагрузок нужно
выбрать только пункт Разработка классических приложений .NET. Можно выбрать и больше опций или вообще все опции, однако стоит
учитывать свободный размер на жестком диске — чем больше опций будет выбрано, соответственно тем больше места на диске будет занято.

Разработка классических приложений .NET Windows Forms в Visual Studio

После установки среды и всех ее компонентов, запустим Visual Studio и создадим проект графического приложения.
На стартовом экране выберем Create a new project (Создать новый проект)

Разработка классических приложений на C# и Windows Forms в Visual Studio

На следующем окне в качестве типа проекта выберем Windows Forms App:

создание первого проекта Windows Forms на C#

Стоит отметить, что среди шаблонов можно увидеть еще тип Windows Forms App (.NET Framework) — его НЕ надо выбирать, необходим именно тип
Windows Forms App.

Далее на следующем этапе нам будет предложено указать имя проекта и каталог, где будет располагаться проект.

первый проект Windows Forms на C#

В поле Project Name дадим проекту какое-либо название. В моем случае это HelloApp.

На следующем окне Visual Studio предложит нам выбрать версию .NET, которая будет использоваться для проекта. Выберем последнюю на данный момент версию — .NET и нажмен на кнопку Create (Создать) для создания проекта.

Версия .NET для проекта Windows Forms на C#

После этого Visual Studio откроет наш проект с созданными по умолчанию файлами:

первый проект Windows Forms на C# в Visual Studio

Справа находится окно Solution Explorer, в котором можно увидеть структуру нашего проекта. Практически этот тот же проект, который создается с
помощью .NET CLI:

  • Dependencies — это узел содержит сборки dll, которые добавлены в проект по умолчанию.
    Эти сборки как раз содержат классы библиотеки .NET, которые будет использовать C#

  • Form1.Designer.cs: он содержит определение компонентов формы, добавленных
    на форму в графическом дизайнере

  • Далее идет файл единственной в проекте формы — Form1.cs, который по умолчанию открыт в центральном окне.

  • Program.cs определяет точку входа в приложение

Запуск приложения

Чтобы запустить приложение в режиме отладки, нажмем на клавишу F5 или на зеленую стрелочку на панели Visual Studio.

Запуск проекта Windows Forms в Visual Studio

После этого запустится пустая форма Form1 по умолчанию.

После запуска приложения студия компилирует его в файл с расширением exe. Найти данный файл можно, зайдя в папку проекта и далее в каталог
\bin\Debug\net8.0-windows

Рассмотрев вкратце создание проекта графического приложения, мы можем перейти к обзору основных компонентов и начнем мы с форм.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Windows driver kit install
  • Как зайти в биос на windows 10 кнопка
  • Samsung health для windows
  • Windows 10 просит пароль на шару
  • Как открыть диспетчер учетных данных на windows 10