Комментарии 45
А какой вес получается, например с GUI окном + кнопкой при статик линковке (без внешних зависимостей от runtime)? Жаль что у Microsoft до сих пор нет для C/C++ нормального решения для GUI ,без тяжелых фреймворков, например как это было удобно в VB6. Я лично использую WinAPI это тот еще ад, но компактно и если есть уже свои наработки то- быстро. Конечно ко всему нужно привыкать.
у Microsoft до сих пор нет для C/C++ нормального решения для GUI ,без тяжелых фреймворков
MFC, WinForms никуда не делись (Forms правда без C++/CLI сомнительно). На шарпе для классик .NET 4.8 тоже мелкие приложения будут без зависимостей, еще и тогда WPF можно.
MFC -это довольно объемный доп. портфель с собой таскать или статик линковка делает exe огромных размеров.
WTL - это тот же самый по сути WinAPI, просто с ООП и поделенный на классы -так что излишний.
MFC всегда уже есть в системе. Линкуемся к уже существующей DLL.
WTL/MFC это просто один уровень абстракции и удобства к WinAPI, как он может быть излишним.
Уровень тонкий -> накладных на размер мало.
MFC -это довольно объемный доп. портфель
Он еще и тормозной. Помню, как делал на нем UI под Win9x на Celeron - то, что через WinAPI летало без задержек, через MFC ощутимо тормозило.
А еще, чем больше нужно менять его стандартное поведение, тем больше времени уходит на копание в потрохах, и тем больше кривых и уродливых костылей нужно приколхозить. Довольно быстро приходит понимание, что написать все самому было бы проще. :)
Было дело, потом с выходом библиотеки стандартных макросов для WinAPI писать на голом этом WinAPI стало местами проще, чем под MFC, тоже можно было в одной WinMain накатать относительно несложный диалог.
MFC тормозил, потому что держал глобальный map отображения объектов MFC на хендлы окон, т.е. по каждому событию под защитой мьютекса лез в эту мапу, доставал оттуда объект и смотрел - есть ли перехватчики на это сообщение. Причём, в одноядерных системах мьютексы были относительно дешевы, но как только началась многоядерность, так сразу всё - стали нужны interlocked-операции. В любом случае, поиск по мапе по каждому событию и затем линейный поиск по перехваченным номерам сообщений - это тормоза.
Для сравнения, ATL/WTL хранили указатель на свой объект в данных окна (GetWindowData или как там её), поэтому WTL и была одно время популярна из-за своей легковесности в рантайм, хотя писать на ATL/WTL тот еще мазохизм. ))
MFC тормозил, потому что держал глобальный map отображения объектов MFC на хендлы окон, т.е. по каждому событию под защитой мьютекса лез в эту мапу,
Что то путаете, каждый оконный объект происходит от CWnd и свой хендл держит сам в родительском CWnd::m_hWnd.
Возможно, речь о таблицах маршрутизации сообщений.
И уж тем более, какие мьютексы в одноядерных системах, тогда даже CLib была в однопоточной версии.
Нет, всё верно. В m_hWnd хранился сам handle окна, но не все окна были обязаны быть созданы через механизм MFC. Мы могли получить CWnd* на любое окно или контрол в диалоге например, созданному из шаблона диалога. В таком случае при получении указателя на окно за кулисами создавался прокси-объект наследник от CWnd*, который помещался в служебную маp'у и в его m_hWnd устанавливался нужный внешний handle.
Верно, CWnd держит HWND, но это не помогает перенаправлять сообщения из оконной процедуры в методы CWnd и наследников.
В глобальной AfxWndProc вызывается CWnd::FromHandlePermanent(hWnd) - она лезет в глобальную мапу потока (в версиях тех лет - процесса), чтобы найти нужный C++ объект.
А регистрация в этой мапе происходит через механизм хуков WH_CBT, который тоже достаточно тяжеловесный. Т.е., CWnd в своём конструкторе выставляет хук на создание хендла, захватывает глобальный мьютекст, инициализирует глобальную переменную указателем на свой экземпляр, создаёт HWND, в системном хуке перехватывается хендл созданного окна еще до возврата от CreateWindow, потому что оконная процедура начинает получать сообщения раньше, чем CWnd получит свой хендл по возврату от CreateWindow.
А в схеме ATL/WTL указатель на С++ объект писали прямо в данные окна, т.е. без мьютексов и хуков.
Программировать на WTL намного неудобнее, чем на MFC, но именно легковесность/отзывчивость заставляли терпеть неудобства. ))
И уж тем более, какие мьютексы в одноядерных системах
Обычные мьютексы, которые в одноядерной системе работали не на interlocked-инструкциях, а на временном запрещении и затем разрешении прерываний внутри ядра при доступе к разделяемым полям (т.е. запретили прерывание, "захватили мьютекс" через запись служебных полей, разрешили прерывание; аналогичная церемония при отпускании мьютекса).
В выталкивающей многопоточности единственность ядра не спасает от конкуренции за ресурсы - прерывание от таймера может прийти в любой момент.
И самое забавное, что многоядерность сделала обслуживание юзер-мод мьютексов дешевле, потому что часть логики по захвату критической секции стала выполняться без захода в ядро на interlocked CAS. А в одноядерном варианте без interlocked-операций каждый захват критической секции переключал кольцо исполнения в ядро и тормозил систему.
тогда даже CLib была в однопоточной версии.
CLib была однопоточной в кооперативной многозадачности (Win16 API), а затем шла как опция для линковки в WinNT/Win95 (Win32 API).
Но однопоточность Clib не влияет на WinAPI, т.е. ты мог вызывать многопоточные функции WinAPI, будучи прилинкован к однопоточному рантайму, что было причиной глупых ошибок и даже проходов по памяти.
В итоге однопоточный рантайм выпилили из поставки где-то к середине нулевых.
Много слов, проще было линк на исходники внутри MFC скинуть =)
В любом случае, по нынешним временам, в сравнении с WinRT/WinUI это всё очень тонкий слой абстракции.
MFC плох не толщиной слоя, а своей механикой отображения хендлов на экземпляры классов. Да и просто косяков проектирования хватало - сама область знаний была еще молодая. Например, мутабельные строки со счётчиком ссылок - источник досаднейших ошибок. Сегодня расшаренные строки могут быть только иммутабельными (hstring в WinRT), но к этому ж надо было еще прийти через набитые шишки.
И сравнивать WinUI 3 с MFC не получится, её нужно сравнивать с USER (HWND-система) + GDI (HDC-девайс), т.е. она заменяет именно эту сладкую парочку, реализуя с 0-ля набор системных контролов. И этих контролов примерно в 10 раз больше, как и методов в них. Т.е. уровень кастомизации UI не сравним и близко.
И это тоже выглядит забавным и досадным одновременно - в WinUI 3 в связке с композитором Windows заложены натурально технологии уровня футуристических бластеров инопланетян, но из XAML ими пользоваться, считай, никак (можно, но гемморойно). Этот как сидеть на мешке золота и умирать от голода.
Хочется организовать всему этому шаговую доступность, конечно...
Разумеется, я тут сравниваю не по технологичности, а по размеру бинарника. Использование MFC даст exe-шник размером в мегабайт максимум при динамической линковке с системной.
но из XAML ими пользоваться, считай, никак (можно, но гемморойно). Этот как сидеть на мешке золота и умирать от голода.
Ждите WinUI 4. Я не буду.
Всё верно. Но приложения выглядят морально устаревшими. Ситуация такова, что WinUI - это официальное будущее. Стоит начинать окучивать. ))
WinUI - ну да сейчас же модно "программистов" плодить с их "Hello World!" на 60 - 100 МБ
Так и есть, но это потому что приложение таскает свой "дом" на себе.
Привязка к WinUI удобна тем, что это общесистемная библиотека, т.е. измеряться будет только "добавочная стоимость" размера приложений, и она получается смехотворной по нынешним временам - первые версии читалки были в районе 300КB, пока туда не напихалась куча функционала, включая поддержку MathML для epub-формата и свистелки навроде псевдо-3D и настройки параметров этого псевдо 3D-отображения, чтобы буквы повторяли изгибы подложки, но сохраняли типографскую чёткость.

Репу читалки открыл: https://github.com/dmitry-valyukov/bukvitsa
Но смотреть пока не рекомендую - в wxl появляются новые хелперы, Bukvitsa должна будет переехать на последнее поколение "достижений", ужав код генерации UI, а то многовато самоповторов, надо повыносить стили отдельно (механизм пресетов уже есть в wxl - это легковесная альтернатива XAML-описанию стилей и объекта Style поверх него):
https://github.com/dmitry-valyukov/bukvitsa/blob/main/Reader/src/reader_panel.cpp
(Вся кухня разрабатывалась по принципу "читалка first", в том числе чтобы увидеть - а что вообще требуется, что имеет смысл автоматизировать, что нет, и т.д.)
В отличие от классического Win32 API, элементы которого «зашиты» в ядро и библиотеки ОС, WinUI 3 поставляется отдельно от Windows как часть независимого пакета Windows App SDK.
Это рантаймы нужные только вашей проге ставить на машину - как то не гуд.
А статик линковка вам даст минимум 60Мб.
Вы прежде чем вайбговнокодить с вашим другом клодом, сами бы поучили матчасть.
Статической линковки быть не может, могут быть разные сценарии деплоя.
И там не 60MB, конечно. Эти 60MB содержат в себе все 4 конфигурации (два для x86/x64 и два для ARM), но в систему конкретной программой будет установлена одна конфигурация. Уже установленный рантайм для x64 (не путать с SDK) весит в районе 20+ MB.
>рантаймы нужные только вашей проге
Рантайм нужен любой программе, использующей WinUI3. Да, есть вариант package-установки, когда весь рантайм устанавливается локально с приложением и используется только им, но это не лучший вариант деплоя, как по мне. Именно по тем причинам, которые ты озвучил.
Теперь по матчасти.
Механизм обновлений WinUI 3 примерно такой же, как и для WinUI 2, на котором написано много системных приложений самой Windows, т.е. через фоновый процесс магазина приложений (даже если юзер не залогинен в нём).
Именно такое распространение обновлений графического бэкенда было обкатано на WinUI 2 и унаследовано следующей версией 3.
Т.е., MS переводит обновление приличной части зависимостей Windows на механизмы доставки обновлений магазина.
На практике это выглядит так:
- Зависимость программы будет только от мажорных версий, т.е. конкретно от WinUI 2, от WinUI 3, от WinUI 4 (гипотетически через десяток лет). Сегодня актуальна 3-я версия, поэтому у wxl зависимость от неё.
- Инсталляха целевой программы может таскать с собой redistributable MSIX-пакеты целиком, либо тонкий бутстрапер, который выкачает и установит. Если пакет уже стоит, то бутстрапперская часть инсталляции либо обновит минорную версию, либо ничего не сделает, если уже стоит такая же, либо более свежая версия.
- Пакет можно поставить на уровень пользователя или на уровень машины. Если поставить под админскими правами - будет один на всех пользователей. И это тоже приятная развилка - можно будет устанавливать программы туда, где юзер не имеет админских прав.
В любом случае, если будет установлено N программ, зависящих от WinUI 3, то система распухнет не на N x 60MB, а на 60 дополнительных мегабайт.
Или, допустим, очередная версия Windows Terminal обновится в зависимостях до WinUI 3 - и этот пакет окажется в системе автоматически.
Минорные версии прилетают в систему автоматом точно так же, как они прилетают сегодня для WinUI 2.
Но самое важное в другом - WinUI 2 зависит от системного XAML-движка, который обновляется вместе с операционкой и это принесло самой MS адскую головную боль, потому что ради внесения небольших патчей в сугубо в движок XAML им приходило выпускать обновление аж для всей операционки.
В WinUI 3 они окончательно отделили обновление графической библиотеки от операционки, теперь эти обновления приходят тихо и незаметно через магазин.
Я исходил из того, что раз MS позиционирует эту технологию как отныне мейнстримовую, то вероятность того, что либа окажется на машине потенциального юзера будет расти с каждым годом. Я ведь разрабатываю библиотеку для будущих UI-приложений (или же для будущих обновления графической оболочки имеющихся).
Да, в будущем какое-то из приложений у потенциального пользователя будет "оплодотворителем", т.е. именно тем, кто "посеет" WinUI 3 в систему, это очевидно и это тоже лично мне нравится, потому что подсистема WinUI установится на конечную машину как бы "по требованию", а не безусловно.
И еще лично я ожидаю, что немного допилят сам бутстрап, чтобы сделать первый посев максимально гладким, коль он будет идти по линии приложений, не контролируемых MS. Ну или не MS допилит, а появится готовый со стороны (или даже несколько на выбор), но не суть, это мне видится неизбежным в любом случае, потому что отрасль уже обратила внимание на WinUI 3 и теперь стоит ожидать приложений на этой подсистеме не только от MS (и очень редких от других игроков), как оно было для WinUI 2.
если будет установлено N программ, зависящих от WinUI 3, то система распухнет не на N x 60MB, а на 60 дополнительных мегабайт.
Это будет справедливо для любой программы? А то я как-то не видел двух независимых программ, которые бы использовали общие библиотеки Qt. :)
Да, для любой программы, использующей такой способ деплоя.
Qt — это классическая «вещь в себе», Windows о ней ничего не знает. Более того, есть несколько несовместимых конфигураций/сборок, смотря какая подсистема используется (msys или еще что-нибудь). Т.е., QT - это не только GUI-библиотека, это еще половина собственного рантайма, заменяющего рантайм Windows
С Windows App SDK / WinUI 3 ситуация иная:
Пакет устанавливается в системный каталог
C:\Program Files\WindowsAppsкак глобальный MSIX Framework Package.Операционная система Windows знает про него всё: она ведет зависимостей и подставляет этот слой рантайма программе через легкий 400-килобайтный бутстраппер (
Microsoft.WindowsAppRuntime.Bootstrap.dll).Обслуживание и минорные апдейты пакета делегированы фоновым службам операционной системы и Магазина приложений.
То есть WinUI 3 для Windows — это не сторонняя библиотека «сбоку», это новый, штатный, отчуждаемый платформенный слой GUI, который поставляется по схеме динамического разделения ресурсов. Если на машине крутится пять независимых программ на базе WinUI 3 / WXL, все они в рантайме будут лениво подхватывать один и тот же установленный в систему Framework-пакет.
И да, именно эта особеность деплоя была для меня одной из весомых причин выбрать WinUI 3 в качестве бэкенда для декларативных игрищ с GUI.
Официальное будущее - это кроссплатформа. И WinUI никак сюда не проходит.
Популярности у нее нет, так что я не одинок.
Кроме того, производительностью XAML я недоволен. Что WPF, что Avalonia - тормоза. Что там в WinUI с учетом п1, даже неохота смотреть.
Согласен насчёт кроссплатформы, именно поэтому одной из целей было отсутствие протекания зависимостей WinUI в WXL-приложения. Потому что настоящая кроссплатформа - на уровне исходников. ))
На некоторой стадии разработки будут попытки разработки альтернативного бэкенда.
Таки, Flutter, а затем SwiftUI показали удобный принцип рисования, убрав с задачи разработки GUI налёт сакральности, превратив процесс в полную ерунду в пересчёте на когнитивные затраты.
Необходимость в кроссплатформе уже отпала. Агент прекрасно генерит код по одному и тому же промпту под разные платформы на нативном для платформы языке и фреймворке.
Не согласен насчёт "прекрасно".
Просто генерит.
Хотя, таким темпами это "прекрасно" может наступить очень скоро.
Может быть даже через 3-5 лет всего (через столько лет ему отдадут вычислительных мощностей на порядок больше).
Вайбкодингу примерно 4 года уже, кстате, и результаты одновременно и весьма высокие, и неожиданно низкие - чему ИИ не научили, то он "понимает" с большим трудом.
ИИ всё еще с трудом генерирует новые смыслы, хотя лет 10 назад обещалось, что к 25-му году смогут. Не смогли.
Разрабатываемое приложение-читалка 1.2 метра, внутри вся типографика и прочие радости, будут отдельные статьи по ней,
А зависимости есть? Смотрели импорт?
Зависимости:
---
api‑ms‑win‑core‑winrt‑error‑l1-1-1.dll
api‑ms‑win‑core‑winrt‑l1-1-0.dll
api‑ms‑win‑crt‑heap‑l1-1-0.dll
api‑ms‑win‑crt‑locale‑l1-1-0.dll
api‑ms‑win‑crt‑math‑l1-1-0.dll
api‑ms‑win‑crt‑runtime‑l1-1-0.dll
api‑ms‑win‑crt‑stdio‑l1-1-0.dll
api‑ms‑win‑crt‑string‑l1-1-0.dll
COMCTL32.dll
ole32.dll
OLEAUT32.dll
d2d1.dll d3d11.dll
DWrite.dll
SHELL32.dll
USER32.dll
GDI32.dll
KERNEL32.dll
Microsoft.WindowsAppRuntime.Bootstrap.dll
MSVCP140.dll
MSVCP140_ATOMIC_WAIT.dll
VCRUNTIME140.dll
VCRUNTIME140_1.dll
---
Microsoft.WindowsAppRuntime.Bootstrap.dll — вот эта весит 400 KB.
И да, в планах обязательно проверить статическую линкову с рантаймом, сколько оно добавит, потому что CRT/CppRT тоже надо деплоить, если линковаться динамически.
Обычно рантайм добавляет немного - смотря, что из него используется. Обычный диапазон от 300 KB до 1.5 MB, где подсистема iostream занимает сразу под пол‑метра, именно поэтому в wxl есть свой тоненький file и async_file прямо поверх WinAPI.
VCRUNTIME140 и т.д. это еще и Си-шный рантайм ставить (х64 весит 18Мб, х86 7 Мб). Почему бы не слинковать с ним статически и посмотреть итоговый размер приложения?
В итоге вместе с WinUI получается что вы в систему из- за какой то читалки слишком дохрена всякого, возможно не нужного пользователю мусора ставите.
Мой совет бросьте вайбкодить, включите голову и пишите с DirectWrite / GDI+ или WebView2 или MSHTML + Windows Compression API + MSXML тогда ваш проект будет весить не более 100кб и не требовать установки чегото извне. Хватит плодить чудовищ.
По особенностям вайбкода тоже будет отдельная статья.
Забавным оказалось то, что вайбкодить мой подход практически не позволяет.
Да и цели ставятся разные - в классическом вайбкоде главное внешний результат, а не сколько он стоит процессору, насколько качественный получается код в целом, вернее, каково качество получаемых решений. Т.е. вайбкодер часто не знает толком, как именно решена его задача (потеря связи с собственным кодом).
У меня наоборот - все решения написаны явно, ручками. Клод может только повторять то, что ему показали как пример или то, что уже знал до этого - такова специфика любого уникального решения (т.е. незнакомого для ИИ).
ИИ впадает в панику, протестует, отговаривает. А которые попроще - те тупо гонят чушь, типа "глюков" нейронок одно-двухгодичной давности.
Дождись следующих статей (смогу себе позволить выкладывать примерно раз в неделю, чтобы не деградировало их качество). Уверен, ты будешь приятно удивлён.
И да, ты можешь прямо сейчас посмотреть, чем именно был занят клод даже хотя бы в читалке - это в основном обслуживанием учёта принятых решений и отметок "почему именно так". Он даже не справился с простейшим потоковым листанием, не смог доработать алгоритм Кнута для пагинации, когда исходный алгоритм не имеет решения (мне пришлось допилить алгоримт Кнута), и еще кучи моментов. И постоянные приписки самому себе "пользователь поймал меня на неточностях дважды за час, зафикисировано - меньше спорить с пользователем", что приходится постоянно вычищать, потому что пассивный ИИ никому не нужен. Нужен активный партнёр, который одновременно умный поисковик и код-ревьювер - вот всякие досадные мелочные ошибки/ляпы он видит неплохо, за что всем этим ИИ, без пафоса, низкий поклон. ))
Это ж классика "парного программирования" - вторые глаза. А для слишком бегущей вперёд мысли - банальная страховка, чтобы реже бить себя себя ладонью по лбу. ))
Даже самые сильные программеры порой ну такие ляпы выдают... Ну вот замылился глаз - дело известное.
Насчёт HTML - весь этот проект являет попыткой ухода от настольного HTML с его прожорливостью и неторопливостью. Хочется, чтобы приложения запускались мгновенно и натурально летали, а не отжирали терпение. Асбсолютно все имеющиеся сегодня читалки для десктопа откровенно недотягивают, даже нейтивные. А которые поверх HTML (типа официального клиента Литрес) - там бы просто руки разработчкам поотрывать бы, за такое отсутствие совести.
Читалка на HTML будет и вовсе безбожно тормозить (вылизанный нейтивный алгоритм пагинации - это маленькое тихое чудо моей читалки, ну просто чтобы показать, как должны быть написаны такие приложения).
Деплой библиотек, да еще разделяемых и обслуживаемых системно - это уровень важности примерно десятый, хотя тут тоже все звёзды сошлись (в сравнении с QT или Электроном, которые тянут с собой для каждого отдельного приложения половину операционки).
Про XML - тоже будет отдельная статья. Книги (и справочники) бывают очень большие, хочется, чтобы GUI не создавало даже микропауз, в итоге библиотека wxl.xml парсит до полугига в секунду, системная отстаёт по эффективности примерно на порядок. Никакой клоуд такое не пишет, конечно (там сплошная циничность и верчение всех мейнстримовых принципов на одном месте) - это мои наработки из области HFT.
Клоуд на мой вкус пока что пишет откровенно слабо, на уровне неплохого мидла (но даже не мидла+), но получше остального нейрослопа.
И да, как раз с самописным xml-парсером, аллкаторами и пагинацией костяк читалки весил 300 KB (это уже полностю рабочая функциональность для FB3), что подтолкнуло довести всю идею до ума, а не ограничиться читалкой. Потому что не в читалке там оказася фокус, хотя фокусы избретались изначально сугубо для читалки. Я и сам не ожидал, что подходы из HFT так гладко лягут, казалось бы, куда меньше всего ожидаешь - на область GUI. ))
Это всё был один большой фан, который неожиданно показал больше, чем от него ожидалось.
Так и как можно попробовать wxl?
Примерно в следующий вторник вместе с очередной статьёй.
DSL-описание требует хелперов под популярные сценарии - это ожидаемое "открытие" для таких технологий. На одних мини-примерах далеко не уедешь.
Поэтому, wxl разрабатывается не как вещь в себе, а как фундамент для реальных приложений - читалки книг и форума RSDN. За выходные хочется отполировать читалку, чтобы можно было уже пользоваться ею хотя бы минимально (сам с неё читаю).
целые армии разработчиков, 100% времени пишущих под Linux, делают это из‑под Windows
Чо? Откуда это? Кто собирал эту статистику? Где можно взглянуть?
Огромное кол-во коллег и знакомых пишут под Linux, но большинство из них делают это из под Windows. Разводить флейм не хотелось бы, это вопрос вкуса и привычек. Сам с удовольствием периодами работаю из под Linux, но это, скорее, удовлетворение любопытства и исследовательской жилки. Отношение к компьютерам и ПО у меня, как у системщика по образованию и образу мыслей, цинично-утилитарное. ))
Ещё есть кроссплатформенная Avalonia. Да, она не для C++. Но странно, что её не упомянули в статье.
Верно, под дотнет есть несколько родственных XAML-фреймврков и Avalonia одна из них. Но для меня представляли интерес библиотеки на C# поверх Авалонии и аналогичных, призванных писать во Flutter-стиле без XAML. Именно так я знакомился с объёмом задачи и с тем, какие хелперы придётся накрутить для поддержки декларативной записи.
В С++ оказалось несколько проще с перегрузкой операторов, это помогло отшлифовать синтаксис описания, он выглядит чище, чем в аналогичных проектах на C#.
И, самое главное, современные корутины C++ принесли полные аналоги async/await в экосистему С++, и GUI-первейшее законное их место, чтобы переводить кучерявый событийный код в уютный-плоский через новомодную асинхронщину.
Меня одного настораживает обилие в статье восторженных эпитетов, прилагательных в превосходной степени и подобного? :)
Есть немного. ))
Потому что "за державу обидно" (С).
Сложившееся реноме современного GUI (как такового) обитает где-то возле понятий "грязь", "тормоза", "говнокодинг" и прочее, и прочее... Могу ошибаться, конечно, но я "слышу" это примерно так. Хотя в этой области есть настоящие находки и отличные технологии, но которым просто не дали ума или которые незаслуженно малозаметны, или слишком нишевые и еще куча причин.
Но это всё был лишь фон, эдакий затянувшийся на десятки лет дискомфорт. Настоящим мотивом было еще одно важное слово - "гемморой". ))
А вот это уже вызов! С этим можно попробовать пободаться...

WinUI 3 без XAML: как написать декларативный GUI на чистом C++23, сжав проекцию WinRT в 50 раз