Минимальный код каркаса windows приложения

Типовой шаблон (каркас) windows-приложения (тшп)

Изучение свойств типового шаблона

Изучить листинг типового шаблона

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

Текст, похожий на приведенный здесь, можно получить автоматически, используя мастер Win32 Application (Hello). Однако он будет отличаться от приведенного здесь (об отличиях см. ШаблонHello).

#include //указания на более чем 30 отдельных хедеров с описаниями > 2000

//функций, 3000 констант и др. описания Windows

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

char szWindowClass [] = «myWindowClass»;

int WINAPI WinMain (HINSTANCE hInst, //идентификатор текущего экземпляра приложения

HINSTANCE hPreInst, //всегда NULL

LPSTR lpszCmdLine, //указатель на командную строку приложения

int nCmdShow) //способ изображения окна при первом выводе

// TODO: Place code here:

HWND hWnd; //дескриптор, хендл, идентификатор окна

MSG lpMsg; //структура для хранения параметров сообщений

// Initialize global strings

WNDCLASS wcApp; //структура для хранения параметров класса (стиля) окна

wcApp.lpszClassName = szWindowClass; //имя класса окна

wcApp.hInstance = hInst; //дескриптор этого приложения., регистрирующий класс окна

wcApp.lpfnWndProc = WndProc; //указатель на функцию-обработчик сообщений окна

wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); //тип курсора окна (стрелка)

wcApp.hIcon = 0; //вид пиктограммы при выводе окна в свернутом виде (здесь без)

wcApp.lpszMenuName = 0; //строка – имя ресурса меню (здесь без меню)

wcApp.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); // цвет для фона

//wcApp.hbrBackground = GetStockObject(WHITE_BRUSH); // кисть по умолчанию

// см. [У. Мюррей, с.90]. CS_DBCLKS, CS_KEYCVTWINDOW !

wcApp.style = CS_HREDRAW|CS_VREDRAW; //перерисовывать окно при resize

wcApp.cbClsExtra = 0; //число доп. байт, выделяемых структуре WNDCLASS

wcApp.cbWndExtra = 0; //число доп. байт для всех доп. структур

if ( ! RegisterClass (&wcApp)) //регистрация окна, описанного в структуре по адресу &wcApp

// Perform application initialization: создает окно и возвращает его идентификатор hWnd

hWnd = CreateWindow (szWindowClass, //имя зарегистрированного класса окна

«A Template», //заголовок (название) окна

WS_OVERLAPPEDWINDOW,//стиль окна (перекрыв.&сист.меню&кнопки maxmin)

CW_USEDEFAULT, //х – начальное положение окна по оси Х

CW_USEDEFAULT, //y – начальное положение окна по оси Y

CW_USEDEFAULT, //ширина окна в ед. устройства

CW_USEDEFAULT, //высота окна в ед. устройства

(HWND)NULL, //указатель на родит. окно (у перекрывающегося окна его нет)

(HMENU) NULL, //зависит от стиля окна – указывает его меню

hInst, //определяет копию модуля, связанного с окном (кто создал)

NULL); //адрес доп. информации, нужной для создания окна

ShowWindow (hWnd, nCmdShow); //вывод окна

UpdateWindow (hWnd); //перерисовка окна, т.к. генерируется сообщение WM_PAINT

// Main message loop: копирует очередное сообщение в структуру по адресу &lpMsg

//т.е. передает его в основной блок программы без фильтрации – 0,0

while (GetMessage (&lpMsg, NULL, 0, 0))

TranslateMessage (&lpMsg); //если нужно — преобразует виртуальн. клавиши в символы

DispatchMessage (&lpMsg); //передача сообщения соответствующей функции окна

// ФУНКЦИЯ ГЛАВНОГО ОКНА (функция окна = оконная функция = функция обратного вызова).

// Имя функции (здесь WndProc) выбирает пользователь.

// PURPOSE: Processes messages for the main window:

// WM_COMMAND — process the application menu,

// WM_PAINT — paint the main window,

// WM_DESTROY — post a quit message and return.

LRESULT CALLBACK WndProc (HWND hWnd, //дескриптор окна, получившего сообщение

UINT messg, //сообщение для обработки

HDC hdc; //дескриптор контекста изображения (устройства для этого окна)

PAINTSTRUCT ps; //структура, хранящая информацию о клиентской области окна

case WM_PAINT: //первый раз это сообщение вызывается функцией UpdateWindow()

//подготовка окна hWnd к выводу информации, получение дескриптора устройства hdc

hdc = BeginPaint (hWnd, &ps);

//——Начало фрагмента пользователя

//——Конец фрагмента пользователя

ValidateRect (hWnd,NULL); //перерисовка не нужна

EndPaint (hWnd, &ps); //завершение отрисовки в окне и освобождение hdc

case WM_DESTROY: //если команда ГМ-Выход

default: //очистка очереди от необработанных сообщений

return ( DefWindowProc (hWnd, messg, wParam, lParam));

Простой каркас Android приложения

Подготовка
  • Скачиваем и импортируем в рабочее пространство проекты actionbar-sherlock и sliding-menu.
  • Для проекта sliding-menu устанавливаем зависимость от проекта actionbar-sherlock.
    При установке зависимостей можно выскочить ошибка, что jar файлы имеют разные SHA. В таком случае удалите файлы android-support-v4.jar из папки libs проекта sliding-menu
  • Создаем новый проект для своего приложения, как обычно. При установке выбираем Minimum-Required-SDK как API8 (android-2.2) и Target SDK какой требуется (API14 и выше). Устанавливаем нашему проекту зависимости от проектов actionbar-sherlock и sliding-menu.
    Возможно, что будет опять конфликт jar файлов, как в пункте выше. Тогда просто удалите из проекта своего приложения файл android-support-v4.jar в папке libs.

Теперь почти все готово для создания нашего приложения, но если Вы запустите проект на android-2.2 и android-4 то увидите, что приложения выглядят абсолютно по-разному. Чтобы это исправить провернем несколько несложных манипуляций:

  • В папке res создадим папку drawable
  • Из папки drawable-mdpi перенесем файл иконки приложения (по-умолчанию eclipse обзывает его ic_launcher.png) в папку drawable
  • Удалим папки drawable-XXX и папки values-XXX (мое мнение, что данные папки должны создаваться по мере необходимости и/или предрелизной подготовки приложения)
  • В файл res/values/styles.xml укажем, что стиль AppBaseTheme будет наследоваться от стиля style/Theme.Sherlock.Light
Читайте также:  Simple smtp server linux
Сама соль

У нас все готово для создания приложения – у нас одинаковый внешний вид приложения в стиле 4-го android и это все одинаково прекрасно смотрится и на android-2.2.
На данный момент у нас не хватает только бокового меню. Для его реализации создадим файл разметки в res/layout и назовем его, к примеру, sidemenu.xml. Пока трогать его не будем.
Перейдем к главной activity (если не создано, создайте). Наша activity будет наследоваться не от стандартного класса activity, а от класса SherlockFragmentActivity.
В методе onCreate опишем реализацию нашего бокового меню:

После этих действий у нас готова болванка для создания приложения, которое имеет боковое меню (не забудьте внести свою разметку в файл res/layout/sidemenu.xml), которое открывается/закрывается по «потягиванию» вправо/влево, а так же наше приложение имеет одинаковый внешний вид для всех версий android. В качестве приятного бонуса мы получили еще и полностью кастомизируемый actionbar (о нем я постараюсь рассказать в следующем посте).

Приложу дополнительно исходник такого болванистого проекта: Скачать
В итоге болванка будет выглядеть примерно так:

Всем спасибо за внимание и приятной разработки!

Сервис на языке Dart: каркас серверного приложения

  • 1. Введение
  • 2. Backend
  • 2.1. Инфраструктура.
  • 2.2. Доменное имя. SSL
  • 2.3. Каркас серверного приложения (мы находимся здесь)
  • .
  • 3. Web
  • 3.1. Flutter web-страница
  • .
  • 4. Mobile
  • .

Подготовка

В прошлый раз мы закончили на том, что разместили статическую веб страницу-заглушку, разработанную с использованием Flutter для web. Страница отображает прогресс разработки нашего сервиса, однако данные о датах начала разработки и релиза пришлось захардкодить в приложении. Таким образом мы лишились возможности изменить сведения на странице. Пришло время разработать приложение — сервер данных. Схема всех приложений сервиса — в статье «Сервис на языке Dart: введение, инфраструктура бэкэнд».

В этой статье мы напишем приложение с использованием фреймворка Aqueduct, оценим его производительность и потребление ресурсов в разных режимах, напишем инструментарий для компиляции в нативное приложение для Windows и Linux, разберемся с миграциями схемы базы данных для доменных классов приложения и даже опубликуем наш инструментальный docker образ в публичный регистр DockerHub.

  • все скриншоты с кодом «кликабельные»
  • полный код здесь
  • консольные команды собраны в README.md проекта
  • комментарии, вопросы, советы — приветствуются
  • пообщаться с автором можно в Телеграмм канале

Установка Aqueduct

Начнем с установки dart-sdk — набора средств разработки на языке Dart. Установить его можно с использованием пакетного менеджера вашей операционной системы как предложено здесь. Однако, в случае Windows никакого пакетного менеджера в вашей системе по умолчанию не установлено. Поэтому просто:

  • Скачаем архив и распакуем его на диск C:
  • Теперь, чтобы наша операционная система знала, где искать исполняемые файлы, добавим необходимые пути. Откроем переменные ОС. Для этого начнем вводить «изменение переменных среды текущего пользователя» в строке поиска

  • В открывшемся окне выберем переменную Path и нажмем Изменить. В открывшемся списке создадим новую строку с адресом до исполняемых файлов dart в файловой системе, например, C:\dart-sdk\bin
  • Проверим, что dart и pub (пакетный менеджер dart) доступны

  • Возможно, чтобы новые пути стали доступны, придется перезагрузиться
  • Установим утилиту командной строки aqueduct CLI (command line interface)

    Теоретически можно установить локально также сервер баз данных PostgreSQL. Однако Docker позволит нам избежать этой необходимости и сделает среду разработки подобной среде выполнения на сервере.

    Генерация приложения

    Итак, откроем папку нашего сервера в VsCode

    Для тех, кто не видел первую и вторую статьи, исходный код можно склонировать из guthub репозитория:

    Создадим шаблон приложения:

    Ознакомимся с содержимым шаблона проекта:

    • README.md — заметка с описанием, как работать с aqueduct проектом, запускать тесты, генерировать API документацию и пр. Вспомогательный файл.
    • pubspec.yaml — спецификция для пакетного менеджера pub. Здесь находятся сведения об используемых пакетах, названии, описании, версии проекта и пр.

  • config.yaml и config.src.yaml — конфигурация для отладки и тестирования проекта соответственно. Мы не будем использовать этот способ конфигурирования.
  • analysis_options.yaml — правила для линтера (утилиты подсветки ошибок в исходном коде). Вспомогательный файл.
  • .travis.yml — конфигурация для системы автоматической сборки и тестирования (continuous Integration). Вспомогательный файл.
  • pubspec.lock и .packages — автоматически сгенерированные файлы пакетного менеджера pub. Первый — список всех зависимостей проекта, включая транзитивные и их конкретные версии, второй — расположение скачанных пакетов зависимостей в файловой системе (кэше).
  • .dart_tool/package_config.json — файл отчета о генерации кода нашего проекта, созданный aqueduct CLI. Вспомогательный файл.
  • bin/main.dart — точка входа в приложение при локальном запуске (например, для отладки). Мы не будем использовать такой способ запуска (исключая тесты).



    lib/channel.dart — Фактически ApplicationChannel — это и есть экземпляр нашего приложения. Aqueduct умеет запускать несколько таких экземпляров для более эффективной утилизации ресурсов CPU и RAM. Такие экземпляры работают в изолированных потоках (в Dart их называют isolate) и никак (почти) не могут взаимодействовать друг с другом.


    lib/data_app.dart — файл инкапсуляции импортов зависимостей. Позволяет объединить необходимые пакеты в условную (library) библиотеку dart_app

  • test/ — автотесты. Здесь можно разместить юнит-тесты, поскольку механизм тестирования сетевого слоя рассчитан на локальный запуск приложения и не будет использоваться при разработке. Для тестов будем использовать Postman.
  • Конфигурация

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

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

    В папке /lib создадим несколько папок и первый репозиторий для доступа к переменным окружения:

    EnvironmentRepository в конструкторе считывает переменные окружения из операционной системы в виде словаря Map и сохраняет в приватной переменной _env. Добавим метод для получения всех параметров в виде словаря:

    lib/service/EnvironmentService — логический компонент доступа к данным EnvironmentRepository:

    Инъекция зависимостей

    Здесь необходимо остановиться и разобраться с зависимостями компонентов:

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

    Эти задачи решим с помощью библиотеки GetIt. Подключим необходимый пакет в pubspec.yaml:

    Создадим экземпляр контейнера инжектора lib/di/di_container.dart и напишем метод с регистрацией репозитория и сервиса:

    Метод инициализации контейнера DI вызовем в методе подготовки приложения:

    Cетевой слой

    lib/controller/ActuatorController — сетевой http компонент. Он содержит методы доступа к служебным данным приложения:

    Задекларируем обработчики маршрутов для контроллеров в lib/controller/Routes:

    Первый запуск

    Для запуска необходимо:

    • приложение упаковать в Docker образ,
    • добавить контейнер в сценарий docker-compose,
    • настроить NGINX для проксирования запросов.

    В папке приложения создадим Dockerfile. Это скрипт сборки и запуска образа для Docker:

    Добавим контейнер приложения в сценарий docker-compose.yaml:

    Создадим файл data_app.env с переменными конфигурации для приложения:

    Добавим новый location в отладочный конфиг NGINX conf.dev.d/default.conf:

    Запускаем отладочный сценарий с флагом предварительной сборки образов:

    Сценарий успешно запустился, но настораживают несколько моментов:

      официальный образ со средой dart от google занимает 290MБ в виде архива. В распакованном виде он займет кратно больше места — 754МБ. Посмотреть список образов и их размер:

  • Время сборки и JIT-компиляции составило 100+ сек. Многовато для запуска приложения на проде
  • Потребление памяти в docker dashboard 300 МБ сразу после запуска


    В нагрузочном тесте (только сетевые запросы GET /api/actuator/) потребление памяти находится в диапазоне 350—390 МБ для приложения, запущенного в одном изоляте

    Предположительно ресурсов нашего бюджетного VPS не хватит для работы такого ресурсоемкого приложения. Давайте проверим:

      Создадим на сервере папку для новой версии приложения и скопируем содержимое проекта

  • Теперь необходимо перенести в эту папку проект web-страницы из /opt/srv_1/public/ и все содержимое папки /opt/srv_1/sertbot/ (в ней находятся SSL сертификаты для NGINX и логи Let’s encrypt бота), также скопируем ключ из /opt/srv_1/dhparam/
  • Запустим в отдельной консоли монитор ресурсов сервера



    Выполним docker-compose сценарий в папке /opt/srv_2/

    Так выглядит сборка приложения перед запуском:



    А так — в работе:

    Из доступного 1ГБ оперативной памяти наше приложение потребляет 1,5ГБ «заняв» недостающее в файле подкачки. Да, приложение запустилось, но ни о какой нагрузочной способности речь не идет.
    Остановим сценарий:

    Нам предстоит решить три задачи:

    • снизить потребление оперативной памяти dart приложением,
    • уменьшить время запуска,
    • снизить размер docker-контейнера приложения.

    Решением станет отказ от dart в рантайме. Начиная с версии 2.6, dart-приложения поддерживают компиляцию в нативный исполняемый код. Aqueduct поддерживает компиляцию начиная с версии 4.0.0-b1.

    Начнем с локального удаления aqueduct CLI:

    Установим новую версию:

    Поднимем зависимости в pubspec.yaml:

    Соберем нативное приложение:

    Результатом будет однофайловая сборка data_app.aot размером около 6 МБ. Можно сразу запустить это приложение с параметрами, например:

    Потребление памяти сразу после запуска — менее 10 МБ.

    Посмотрим под нагрузкой. Параметры теста: сетевые запросы GET /actuator, 100 потоков с максимальной доступной скоростью, 10 минут. Результат:

    Итого: средняя скорость — 13к запросов в сек для тела ответа JSON 1,4кВ, среднее время ответа — 7 мсек, потребление оперативной памяти (на два инстанса) 42 MB. Ошибок нет.

    При повторном тесте с шестью инстансами приложения средняя скорость, конечно, повышается до 19к/сек, но и утилизация процессора достигает 45% при потреблении памяти 64 МБ.
    Это превосходный результат.

    Упаковка в контейнер

    Здесь мы столкнемся еще с одной сложностью: скомпилировать dart-приложение в натив мы можем только под текущую ОС. В моем случае это Windows10 x64. В docker-контейнере я, конечно, предпочел бы один из дистрибутивов Linux — например, Ubuntu 20.10.

    Решением здесь станет промежуточный docker-стенд, используемый только для сборки нативных приложений под Ubuntu. Напишем его /dart2native/Dockerfile:

    Теперь соберем его в docker-образ с именем aqueduct_builder:4.0.0-b1, перезаписав, если есть, старые версии:

    Напишем сценарий сборки нативного приложения docker-compose.dev.build.yaml:

    Запустим сценарий сборки:

    Файл скомпилированного под Ubuntu приложения data_app.aot занимает уже 9 МБ. При запуске утилизирует 19 МБ оперативной памяти (для двух инстансов). Проведем локальное нагрузочное тестирование с теми же условиями, но в контейнере с проксированием NGINX (GET, 100 потоков):

    В среднем 5,3к запросов в секунду. При этом потребление оперативной памяти не превысило 55 МБ. Размер образа уменьшился по сравнению с установленным dart и aqueduct c 840 МБ до 74 МБ на диске.

    Напишем новый сценарий docker-compose.aot.yaml запуска приложения. Для этого заменим блок описания data_app, установив базовым образ “пустой” Ubuntu:20.10. Смонтируем файл сборки и изменим команду запуска:

    Решим еще одну сервисную задачу: фактически сборочный docker-образ c установленными dart и aqueduct — вполне себе переиспользуемый инструмент. Имеет смысл выгрузить его в общедоступный регистр и подключать как готовый скачиваемый образ. Для этого необходимо:

    • зарегистрироваться в публичном регистре, например, DockerHub,
    • авторизоваться локально с тем же логином

    переименовать выгружаемый образ по схеме login/title:tag

    выгрузить образ в регистр

    https://hub.docker.com/repository/docker/andx2/aqueduct/general

    Теперь можно изменить наш сценарий сборки для использования публичного образа

    Подключение базы данных

    В aqueduct уже встроен ORM для работы с базой данных PostgreSQL. Для его использования необходимо:

    • Создать доменные объекты, описывающие записи в базе данных.
    • На основе доменных объектов и связей между ними сгенерировать файл миграции. Заметка: для записи в базу данных необходимо, чтобы в БД были подготовлены таблицы, чьи схемы подходят для хранения доменных объектов. Aqueduct предоставляет инструмент миграции, который фактически обходит все классы проекта, являющиеся расширением ManagedObject (управляемые объекты), прочитывает типы их полей и связей с другими управляемыми объектами и создает специальный файл, в котором описано изменение схемы таблиц и связей в базе данных по сравнению со схемой предыдущего файла миграции. Файлы миграции добавляются при каждой перегенерации схемы.
    • Применить файлы миграции к базе данных. Применение происходит последовательно, начиная с версии, которая записана в БД текущей.
    • В файлы миграции, сгенерированные aqueduct, можно вносить свои изменения, например определить реализацию метода seed() — для добавления в БД каких-либо начальных данных.
    • Генерация и применение миграций производится aqueduct CLI.

    Начнем с подключения нового docker-контейнера с БД PostgreSQL в сценарии docker-compose.aot.yaml. Готовый образ на основе Linux Alpine («компактная» версия Linux для встраиваемых применений):

    Здесь необходимо обратить внимание на файл переменных окружения data_db.env. Дело в том, что образ заранее настроен на использование этих переменных в качестве имени пользователя, хоста, порта и пароля доступа. Добавим эти переменные в файл:

    Значения приведены условно.

    Также смонтируем папку хоста ./data_db/ в контейнер для хранения данных БД.
    Далее в приложении data_app добавим класс /service/DbHelper для подключения к базе данных, используя переменные окружения:

    Создадим доменный объект, управляемый ORM, для получения настроек клиентского приложения:

    Добавим репозиторий и сервис для добавления настроек и получения актуальной версии:

    Зарегистрируем новые компоненты в DI контейнере:

    Добавим новый контроллер и эндпойнт в маршрутизатор:

    Теперь сгенерируем файл миграции базы данных. Выполним:

    Результатом будет создание файлов миграции в папке проекта:

    Теперь нужно решить сервисную задачу: миграции нужно применять из системы с установленным aqueduct (и dart) к базе данных, работающей в контейнере, и это нужно выполнять как при локальной разработке, так и на сервере. Используем для этого кейса ранее собранный и опубликованный образ для AOT-сборки. Напишем соответствующий docker-compose сценарий миграции БД:

    Интересная деталь — строка подключения к БД. При запуске сценария можно передать в качестве аргумента файл с переменными окружения, а затем использовать эти переменные для подстановки в сценарии:

    Также обратим внимание на флаги запуска:

    • —compatibility — совместимость с версиями docker-compose сценариев 2.х. Это позволит использовать параметры deploy для ограничения использования ресурсов контейнером, которые игнорируются в версиях 3.х. Мы ограничили потребление оперативной памяти до 200МБ и использование процессорного времени до 50%
    • —abort-on-container-exit — этот флаг устанавливает режим выполнения сценария таким образом, что при остановке одного из контейнеров сценария будут завершены все остальные. Поэтому, когда выполнится команда миграции схемы базы данных и контейнер с aqueduct остановится, docker-compose завершит также и работу контейнера базы данных

    Публикация

    Для подготовки к публикации приложения необходимо:

    • Изменить переменные окружения в data_app.env и data_db.env. Напомню, что сейчас у нас POSTGRES_PASSWORD=postgres_password
    • Переименовать сценарий запуска нативного приложения docker-compose.aot.yaml в docker-compose.yaml. Команда запуска приложения на сервере не должна иметь аргументов
    • Временно заблокировать маршрут просмотра переменных окружения запущенного приложения /api/actuator. В следующей статье мы реализуем механизм авторизации по ролям и откроем доступ к этому маршруту только для администратора.

    Скопируем на сервер папку приложения ./data_app/. Важным моментом здесь будет ключ -p (копировать с сохранением атрибутов файлов) в команде копирования. Напомню, что при сборке нативного приложения мы установили права на исполнение файлу data_app.aot:

    • Измененную конфигурацию NGINX ./conf.d/default.conf
    • Сценарии запуска и миграции docker-compose.yaml, docker-compose.migrations.yaml
    • Файлы с переменными окружения data_app.env и data_db.env

    Добавим на сервере папку /opt/srv_1/data_db. Это том файловой системы хоста для монтирования в контейнер базы данных. Здесь будут сохраняться все данные PostgreSQL.

    Выполним сценарий миграции схемы базы данных:

    Читайте также:  Кодировки ascii windows 1251 та unicode
  • Оцените статью