Я от лица команды хочу показать вам SophiApp — графический наследник Sophia Script for Windows: бесплатная, портативная и полностью опенсорная программа для тонкой настройки Windows 10 и Windows 11.

В этой статье я расскажу, как оброненная мной фраза в комментарии 3 года назад под моей статьей из цикла про тонкую настройку Windows развернула мою жизнь на 180°, а чуть позже — и еще одного человека.

Все это время у меня была идея сделать графическую версию моего модуля на PowerShell, чтобы показать пользователям, каким должен быть современный твикер для Windows, какие функции может в себе нести, а главное — посыл программы: настроить (а не оптимизировать) ОС официальным образом, задокументированным Microsoft, ничего не сломав и не обещая мнимое увеличение производительности, чем грешат аналогичные программы, целенаправленно вводя пользователей в заблуждение.

Уже есть идеи насчет версии 2.0 с более современными UI а-ля Windows 11 и UX, а также расширенной функциональностью, но первый блин, вроде как, не оказался комом. Программа все это время делалась на голом энтузиазме, и мы искренне хотим, чтобы пользователи Windows перестали воспринимать так называемые твикеры как что-то по определению вредное, не несущее пользы, а узнали, как можно настроить современные Windows 10 и 11 и что они в себе таят.

Как появилась идея программы, и знакомство с Дмитрием

Как-то летом 2019 года в моей первой статье Скрипт настройки Windows 10 в ответ на предложение о создании графической версии моего PowerShell-скрипта я посетовал, что PowerShell-грамоте не обучены мы, но есть желание что-нибудь сотворить. На тот момент знания в PowerShell-ремесле были скудны (как и сейчас), потому максимум, на что я рассчитывал, — сварганить что-нибудь на Windows Forms, как делают многие на GitHub. Но вдруг 3 сентября 2019 года в личные сообщения на Хабре мне написал некто, представившись Дмитрием (старый аккаунт на Хабре, GitHub), с предложением сделать то, о чем я мечтал! Сказать, что я был удивлен, что кто-то откликнулся мне помочь, — ничего не сказать. Как выяснилось, он уже собаку съел на такого рода GUI-окнах с кнопками, так как это была часть его работы. А показав нам реальные примеры своих работ, он укрепил меня во мнении, что у нас все получится. Ну, скажем, месяца за 2—3. Кто бы мог помыслить, во что это все выльется для нас обоих…

Что не так с "рынком" твикеров

Наверное, надо немного отвлечься и затронуть тему особенности "рынка" так называемых твикеров для Windows. Фундаментально все программы такого назначения можно разделить на 2 категории:

  1. Настраивают внешний вид ОС;

  2. Вмешиваются в работу ОС (и иногда ломая ее работоспособность на корню):

    1. Удаление Microsoft Defender, вырывая его с корнем из системы;

    2. Удаление несчастных UWP-приложений, варварски выкорчевывая файлы из %ProgramFiles%\WindowsApps;

    3. Отключение получения обновлений через Центр обновления Windows.

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

Но это все крайности. В основном, когда выходит очередной обзор такого рода программы на YouTube, на превью-картинку ставится текст, что во всех играх у вас повысятся до 300 FPS (вне зависимости от текущих характеристик ПК) и также обязательно употребляется слово "оптимизация". 

В случае с англоязычным сегментом интернета обязательно употребляются "debloat" или "debotnet", намекая, что Windows состоит чуть менее, чем полностью из ненужных программ. Ведь только разработчики такого рода программ знают, что Windows "из коробки" работает нестабильно, а Microsoft скрывает от нас секретные ключи реестра, которые-то и сделают из вашего ПК ракету. И вообще всему виной, по их мнению, Microsoft Defender, сжирающий МБ ОЗУ, предустановленные UWP-приложения и логи, создаваемые бесчисленными сборщиками из Просмотрщика событий, — практически всадники Апокалипсиса!

Пользователи, недовольные быстродействием Windows
Пользователи, недовольные быстродействием Windows

Такого рода действия могут совершаться по следующим причинам:

  1. Привлечение внимания пользователей, у которых в большинстве своем могут быть не самые мощные ПК;

  2. Искренняя убежденность в правильности своих действий в силу отсутствия знаний о работе Windows;

  3. Целенаправленное введение в заблуждение пользователей с целью создания вокруг себя ауры гуру в вопросах работы Windows и того, как ее "ускорить";

  4. Распространение зловредных программ с целью извлечения прибыли от доверчивых пользователей.

Первые попытки на PowerShell, или осознание, что надо писать на C#

Мы с Дмитрием быстро нашли общий язык, и, обговорив вектор разработки, я объяснил, как работают функции в скрипте, чтобы он перенес их в графику.

Долго ли, коротко ли, но уже к концу сентября, когда количество строк кода в его PowerShell-скрипте перевалило за 20 000, powershell.exe встал колом: запуск уже занимал около 10 секунд, и никто не понимал, что там написано и как это работает. Надо было идти дальше. Но куда? Мы явно не рассчитали ни наши силы, ни знания, ни время, необходимое на такой проект. Ответ родился сам по себе: я позвонил Дмитрию и робко предложил ему написать программу на чистом C#. Судя по звукам, которые он начал издавать, я подумал, случилось примерно следующее:

Он не отрицал, что пишет иногда для себя простенькие консольные программы на C# для облегчения своей работы, но это не входит в его круг обязанностей — это как хобби, и он не потянет. Уж не знаю, какими словами, но я убедил его попробовать. Тут же встала новая проблема: как на предыдущем GUI-приложении не напихаешь кнопок — тут нужен настоящий дизайн программы! Наверное, именно в то время Дмитрий начал догадываться, что его втягивают в какую-то авантюру.

Такого твикера не дай бог никому! Кадры из к/ф Ширли-мырли
Такого твикера не дай бог никому! Кадры из к/ф Ширли-мырли
Страшные наброски 3000

Все прочие скриншоты с видео утрачены или удалены, и никто уже не увидит, как плохо у нас все выглядело. :)

Где-то к июню 2020 мы окончательно признались себе, что ничего у нас не выйдет с таким подходом, и надо заказывать у какого-нибудь фрилансера UI с нормальным, проработанным UX. И вот впервые нам улыбнулась удача, когда на сайте фрилансеров мы наткнулись на Владимира.

Не будем заострять внимание на этой итерации разработки, так как и она потерпела крах даже с привлеченным разработчиком интерфейсов. Вся проблема в том, что если ты сам до конца не понимаешь, что тебе надо, то и результат будет соответствующий. После получения готового макета в Zeplin наша эйфория длилась недолго: его вариант даже нельзя было сравнивать с нашими потугами, но все разбилось о скалы "незакладывания масштабирования". Мы не учли столько вещей, что было даже стыдно признаться ему, что по сути надо все переделывать. Но осознание провала еще не накрыло нас, и мы барахтались с тем вариантом дизайна еще где-то до декабря 2020, когда пришло окончательно понимание, что так больше не может продолжаться.

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

Дмитрий: Ah shit, here we go again
Дмитрий: Ah shit, here we go again

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

Текущая версии SophiApp и то, как она работает

Полномочия в команде мы разделили следующим образом: на мои плечи возложено было написание всех логически верных проверок на PowerShell, чтобы Дмитрий мог в дальнейшем понять, как выставлять чекбоксы в интерфейсе, то бишь, когда ложь, а когда истина. Кроме того, перевод интерфейса на английский язык, тестирование сборок, PR, написание начальных проверок и проверок для определения работоспособности Microsoft Defender (чтобы отсеять пользователей, у которых он сломан или сломан кэш WMI), логика работы каждой функции. Дмитрию же достались написание кода и отладка.

Стоит заострить внимание, почему мы так трепетно относимся к проверке версии сборки Windows и работоспособности Microsoft Defender. На самом деле все достаточно банально: как показала практика поддержки моего скрипта Sophia Script for Windows (более 500 000 скачиваний за 2,5 года, а также более 5 000 звезд на GitHub), есть достаточная прослойка пользователей, которые целенаправленно ломают Windows, используя сомнительные программы, с целью  выключения встроенного антивируса или его полного вырезания из системы. Все опять же растет из YouTube, где нечистые на руку блогеры специально ведут риторику о том, что все беды в ОС от наличия в ней Microsoft Defender. 

Другая крайность, с которой мы столкнулись, — это отказ некоторых пользователей вообще обновлять их Windows, так как они уверены (опять же с подачи блоггеров), что обновления к ОС выходят каждый день, и их надо срочно отключить. Эти два фактора напрямую влияют, какой фидбек мы получаем от пользователя после использования SophiApp. И чем сильнее укрепляется в вере пользователь, что Defender нужно сломать, а ОС никогда не обновлять, тем больше ошибок возникает в работе нашей программы.

С учетом этих вводных, волевым усилием было принято решение где-то раз в полгода повышать требование к минорной версии билда Windows. На текущий момент это 1904x.1766+ для Windows 10 и 22000.739+, 22509+ для Windows 11 и Windows 11 Insider Preview соответственно.

То же касается и проверки через интернет, последняя ли версия программы запущена: в каждом билде исправляются ошибки (и, конечно, добавляются новые), потому мы хотим предоставить лучший опыт использования пользователям. Для этого программа не только уведомляет об обнаружении новой версии, но и блокирует текущую, если ее версия ниже, чем уже имеется. А проверка осуществляется достаточно примитивно. Как у многих софтверных компаний в облаке хранится JSON-файл, где прописываются последние версии для стабильной ветки и для бета-версии.

Итак, мы плавно подходим к описанию того, как работает под капотом SophiApp, но сначала напомню особенности программы:

  • Динамически отрисовывающийся UI: все элементы НЕ захардкожены;

  • 25 000+ строк кода (не считая JSON-конфигов);

  • Более 130 твиков;

  • Копировать описания функций через ПКМ;

  • Переведена носителями на английский, украинский, немецкий, итальянский, французский, чешский и турецкий языки;

  • SophiApp использует паттерн MVVM;

  • Поддержка многопоточности;

  • SophiApp проверяется статическим анализатором, лицензию на который любезно предоставили в PVS-Studio;

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

  • Описание к функциям при наведении курсора на функцию;

  • Имеет встроенный движок поиска по заголовкам и описанию;

  • Программа поддерживает темную и светлую темы. Может менять тему мгновенно в зависимости от выставленного режима приложений в Windows;

  • Настроить конфиденциальность и телеметрию;

  • Выключить задания диагностического характера в Планировщике заданий;

  • Настроить UI и персонализацию;

  • Правильно и до конца удалить OneDrive, не нарушив целостность ОС;

  • Удалить UWP-приложения, отображая локализованные имена пакетов. Список приложений рендерится динамически, используя локальные иконки самих приложений. Ничего не захардкожено;

  • Скачать и установить расширение HEVC Video Extensions from Device Manufacturer, чтобы появилась возможность открывать файлы формата .heic и .heif;

  • Создать задание "Windows Cleanup" по очистке неиспользуемых файлов и обновлений Windows в Планировщике заданий. Перед началом очистки всплывет нативный тост, где вы сможете выбрать: отложить, отменить или запустить задание;

  • Создать задание "SoftwareDistribution" по очистке папок %SystemRoot% \SoftwareDistribution\Download и %TEMP% в Планировщике заданий;

  • Настроить безопасность Windows;

  • Программа полностью портативная: в реестре не сохраняются никакие специальные ключи, а после закрытия чистится лог .NET Framework от программы;

  • Огромное количество твиков по кастомизации проводника и контекстного меню;

  • Все настройки проводятся задокументированными возможностями ОС, что исключает шанс навредить работоспособности системы.

Системные требования

  • Windows 10 2004/20H2/21H1/21H2/22H2 x64;

    • Билд 1904x.1706+.

  • Windows 11 21H2/22H2/23H2;

    • 22000.739+, 22509+.

  • Чтобы запустить SophiApp, вы должны быть единственным вошедшим пользователем с правами администратора на ПК;

  • Правильная работоспособность программы гарантируется лишь при использовании оригинального образа ОС. SophiApp может не работать на сломанных сборках Windows;

  • Некоторые функции зависят от доступа в интернет. При отсутствии последнего соответствующие функции будут скрыты в UI до тех пор, пока не появится доступ;

  • Вы можете включить скрытые функции в UI, включив «Расширенные настройки» в Настройках программы. Скрытые функции будут помечены соответствующей шестеренкой;

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

Сторонние библиотеки

Много скриншотов!

Передаю слово Дмитрию, который и написал SophiApp.

Здравствуйте. У меня нет какого-то опыта в написании статей, но постараюсь описать, как мы пришли к идее принципа работы SophiApp в том виде, в котором она сейчас и работает. Жаль, что нельзя, как математики в древней Индии, просто дать ссылку на папку с кодом на GitHub и подписать: "Смотри!". В общем по мере возможности я буду приводить примеры кода, того, как реализован тот или иной аспект работы программы.

В сумме SophiApp мной переписывалась 5 раз с нуля, и каждый раз я был готов бросить сию затею, так как нервы дороже. Но, как видите, мы живы, и программа работает. :) 

Мое перманентное состояние в начале разработки
Мое перманентное состояние в начале разработки

В начале этой истории мне почему-то казалось, что максимально правильно будет не использовать сторонние библиотеки UI, а делать все средствами WPF. К тому же дизайн программы не подразумевал стандартных элементов UI — все должно быть нестандартно, стильно и вызывать wow-эффект. И вообще в программе не используются какие-то особые хаки или ноу-хау. Все достаточно приземленно выглядит.

Пришлось с нуля изучать стилизацию и анимации в WPF. Простой переключатель (switch) я делал неделю, перечитав множество тем на StackOverflow десятилетней давности и испортив тонны кода. А когда он заработал — Дмитрий "обрадовал" меня тем, что все элементы должны поддерживать две темы: тёмную и светлую, и темы должны иметь возможность переключаться "на лету".

Премного благодарен
Премного благодарен

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

Думаю, если бы я не пришел к пониманию, что нужна поддержка шаблона MVVM, то еще бы долго и упорно копал не туда. Сначала я очень скептически отнесся к этой идее: "Зачем усложнять и без того сложный код?". Но после тестового приложения все встало на свои места, и я снова переписал SophiApp, добавив поддержку MVVM и заодно RelayCommand для элементов интерфейса. Самое лучшее в коде — это то, что его всегда можно переписать!

This is fine.
This is fine.

Для того чтобы проверить, как будет выглядеть программа "вживую", я сделал специальный дебаг-билд, который считывал из главного JSON-файла названия, описания и тип элементов, и отрисовывал их в интерфейсе. Этот файл послужил основой для текущей версии SophiApp. В папке рядом с исполняемым файлом лежал JSON-файл, и при редактировании последнего без перекомпиляции исполняемого файла программа отрисовывала новые элементы интерфейса, их тип и описание к заголовкам и кнопкам. Это надолго заняло Дмитрия созданием и редактированием текста на русском и английском языках, заодно обогатив его жизненный опыт тонкостями различий между флажком (checkbox) и радиокнопкой (radiobutton) и т. п. Как оказалось, емко и грамотно сформулировать описание ко всем функциям достаточно тяжело, не скатываясь в популизм и не опускаясь до уровня фраз и словечек вроде "выпилить телеметрию", "бессовестное поведение Microsoft", "шпионский модуль", "назойливый Центр безопасности" и прочего, не относящегося к работе Windows. Кстати, все фразы — выдержки из реальных программ.

Так как в приложении много текста, его нужно было как-то хранить. Выбор ожидаемо пал на JSON. JSON — это модно и молодежно, думали мы. Так оно и есть, когда его используют рационально, у нас получился огромный файл, где хранятся все локализации, описания и заголовки ко всем функциям. Огромное преимущество в использовании такого подхода было в том, что его удобно парсить — хоть тем же PowerShell, но про минусы мы узнали, когда нам стали предлагать переводы интерфейса SophiApp. И тут стало понятно, что никого палкой не заставишь вписывать сотни новых строк в этот огромный файл! Выход виделся лишь один: искусственно разбить единый файл на множество маленьких файлов под каждую локализацию, чтобы человек мог перевести файл только с английской локализацией или улучшить уже существующий. Сказано — сделано.

Выбор пал на Json.NET, так как это де-факто стандарт парсинга JSON. Благодаря библиотеке, парсим и превращаем JSON в объекты:

private async Task DeserializeTextedElementsAsync()
{
    await Task.Run(() =>
    {
        var deserializedElements = JsonConvert.DeserializeObject<IEnumerable<TextedElementDto>>Encoding.UTF8.GetString(Properties.Resources.UIData))
                                              .Where(dto => IsWindows11 ? dto.Windows11Supported : dto.Windows10Supported)
                                              .Select(dto => FabricHelper.CreateTextedElement(dto: dto, errorHandler: OnTextedElementErrorAsync, statusHandler: OnTextedElementStatusChanged, language: Localization.Language))
                                              .OrderByDescending(element => element.ViewId);
        TextedElements = new ConcurrentBag<TextedElement>(deserializedElements);
    });
}

Все было безветренно, пока не встал вопрос: "как добавить новый перевод в главный файл". На этот раз на помощь пришел PowerShell. В данном примере показывается, как можно интегрировать турецкую локализацию в основной JSON-файл.

# Compare 2 JSONs and merge them into one
Remove-TypeData System.Array -ErrorAction Ignore

$Parameters = @{
	Uri             = "https://raw.githubusercontent.com/Sophia-Community/SophiApp/master/SophiApp/SophiApp/Resources/UIData.json"
	UseBasicParsing = $true
}
$Full = Invoke-RestMethod @Parameters

$Parameters = @{
	Uri             = "https://raw.githubusercontent.com/Sophia-Community/SophiApp/master/SophiApp/SophiApp/Localizations/UIData_TR.json"
	UseBasicParsing = $true
}
$Translation = Invoke-RestMethod @Parameters

# In this case we add Turkish translation
$ID = "TR"

$Full | ForEach-Object -Process {
	$UiData = $_
	$Data = $Translation | Where-Object -FilterScript {$_.Id -eq $UiData.Id}

	$UiData.Header | Add-Member -Name $ID -MemberType NoteProperty -Value $Data.Header.$ID -Force
	$UiData.Description | Add-Member -Name $ID -MemberType NoteProperty -Value $Data.Description.$ID -Force
	
	if ($UiData.ChildElements)
	{
		$UiData.ChildElements | ForEach-Object -Process {
			$UiChild = $_
			$Child = $Data.ChildElements | Where-Object -FilterScript {$_.Id -eq $UiChild.Id}

			$UiChild.ChildHeader | Add-Member -Name $ID -MemberType NoteProperty -Value $Child.ChildHeader.$ID -Force
			$UiChild.ChildDescription | Add-Member -Name $ID -MemberType NoteProperty -Value $Child.ChildDescription.$ID -Force
		}
	}
}

ConvertTo-Json -InputObject $Full -Depth 4 | ForEach-Object -Process {$_.Replace("\u0027", "'")} | Set-Content -Path "D:\3.json" -Encoding UTF8 -Force

# Re-save in the UTF-8 without BOM encoding due to JSON must not has the BOM: https://datatracker.ietf.org/doc/html/rfc8259#section-8.1
Set-Content -Value (New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false).GetBytes($(Get-Content -Path "D:\3.json" -Raw)) -Encoding Byte -Path "D:\3.json" -Force

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

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

Hidden text
Windows 11 Pro 21H2 build 22000.856
Computer name: DESKTOP-B3E2G5O
User: Sanctuary
User domain: DESKTOP-B3E2G5O
User culture: Russian (Russia)
User region: Russia
App version: 1.0.77.0
App is release: False
App folder: "D:\Downloads\SophiApp\"
App localization: RU
App theme: DARK
App has access to Internet: True
Release version is available: 1.0.77
Pre-release version is available: 1.0.77
No update required

An error occured in element: 313
Information: PC is not domain-joined
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 502
Information: The UWP package MicrosoftTeams wasn't found in OS
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 230
Information: The UWP package MicrosoftWindows.Client.WebExperience wasn't found in OS
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 262
Information: Unsupported Windows edition
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 601
Information: The UWP package Microsoft.XboxGamingOverlay or Microsoft.GamingApp wasn't found in OS
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 501
Information: The UWP package Microsoft.549981C3F5F10 wasn't found in OS
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 349
Information: OneDrive is not installed on this PC
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 351
Information: The latest version of Visual C++ Redistributable 2015–2022 x64 is installed
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 354
Information: .Net version 6.0.8 already installed on this PC
The method that caused the error: SophiApp.Customisations.CustomisationStatus

An error occured in element: 357
Information: .Net version 6.0.8 already installed on this PC
The method that caused the error: SophiApp.Customisations.CustomisationStatus

The 100 element was initialized in 0,019 second(s)
The 101 element was initialized in 0,001 second(s)
The 104 element was initialized in 0,004 second(s)
The 105 element was initialized in 0,000 second(s)
The 108 element was initialized in 0,015 second(s)
The 118 element was initialized in 0,000 second(s)
The 119 element was initialized in 0,000 second(s)
The 120 element was initialized in 0,000 second(s)
The 121 element was initialized in 0,000 second(s)
The 122 element was initialized in 0,000 second(s)
The 123 element was initialized in 0,000 second(s)
The 124 element was initialized in 0,000 second(s)
The 125 element was initialized in 0,000 second(s)
The 126 element was initialized in 0,000 second(s)
The 127 element was initialized in 0,000 second(s)
The 200 element was initialized in 0,003 second(s)
The 204 element was initialized in 0,000 second(s)
The 205 element was initialized in 0,000 second(s)
The 206 element was initialized in 0,000 second(s)
The 207 element was initialized in 0,000 second(s)
The 208 element was initialized in 0,000 second(s)
The 212 element was initialized in 0,000 second(s)
The 213 element was initialized in 0,000 second(s)
The 214 element was initialized in 0,000 second(s)
The 215 element was initialized in 0,000 second(s)
The 216 element was initialized in 0,000 second(s)
The 220 element was initialized in 0,000 second(s)
The 222 element was initialized in 0,000 second(s)
The 223 element was initialized in 0,000 second(s)
The 224 element was initialized in 0,000 second(s)
The 227 element was initialized in 0,000 second(s)
The 229 element was initialized in 0,000 second(s)
The 230 element was initialized in 0,075 second(s)
The 241 element was initialized in 0,000 second(s)
The 242 element was initialized in 0,001 second(s)
The 246 element was initialized in 0,000 second(s)
The 249 element was initialized in 0,000 second(s)
The 253 element was initialized in 0,000 second(s)
The 254 element was initialized in 0,000 second(s)
The 257 element was initialized in 0,000 second(s)
The 258 element was initialized in 0,000 second(s)
The 259 element was initialized in 0,000 second(s)
The 260 element was initialized in 0,000 second(s)
The 261 element was initialized in 0,000 second(s)
The 262 element was initialized in 0,001 second(s)
The 268 element was initialized in 0,007 second(s)
The 300 element was initialized in 0,002 second(s)
The 301 element was initialized in 0,001 second(s)
The 304 element was initialized in 0,000 second(s)
The 305 element was initialized in 0,000 second(s)
The 306 element was initialized in 0,000 second(s)
The 309 element was initialized in 0,000 second(s)
The 310 element was initialized in 0,000 second(s)
The 311 element was initialized in 0,000 second(s)
The 312 element was initialized in 0,000 second(s)
The 313 element was initialized in 0,007 second(s)
The 314 element was initialized in 0,000 second(s)
The 315 element was initialized in 0,146 second(s)
The 316 element was initialized in 0,030 second(s)
The 319 element was initialized in 0,000 second(s)
The 320 element was initialized in 0,077 second(s)
The 321 element was initialized in 0,000 second(s)
The 324 element was initialized in 0,000 second(s)
The 327 element was initialized in 0,000 second(s)
The 330 element was initialized in 0,000 second(s)
The 331 element was initialized in 0,000 second(s)
The 332 element was initialized in 0,000 second(s)
The 333 element was initialized in 0,000 second(s)
The 334 element was initialized in 0,000 second(s)
The 335 element was initialized in 0,000 second(s)
The 336 element was initialized in 0,000 second(s)
The 337 element was initialized in 0,000 second(s)
The 338 element was initialized in 0,000 second(s)
The 339 element was initialized in 0,005 second(s)
The 340 element was initialized in 0,000 second(s)
The 341 element was initialized in 0,000 second(s)
The 342 element was initialized in 0,013 second(s)
The 347 element was initialized in 0,002 second(s)
The 350 element was initialized in 0,241 second(s)
The 353 element was initialized in 1,921 second(s)
The 356 element was initialized in 0,576 second(s)
The 500 element was initialized in 0,018 second(s)
The 501 element was initialized in 0,004 second(s)
The 502 element was initialized in 0,081 second(s)
The 503 element was initialized in 0,001 second(s)
The 600 element was initialized in 0,002 second(s)
The 601 element was initialized in 0,097 second(s)
The 602 element was initialized in 0,025 second(s)
The 700 element was initialized in 0,020 second(s)
The 701 element was initialized in 0,001 second(s)
The 702 element was initialized in 0,002 second(s)
The 800 element was initialized in 0,051 second(s)
The 801 element was initialized in 0,012 second(s)
The 803 element was initialized in 0,173 second(s)
The 804 element was initialized in 0,043 second(s)
The 805 element was initialized in 0,040 second(s)
The 806 element was initialized in 0,000 second(s)
The 807 element was initialized in 0,000 second(s)
The 808 element was initialized in 0,013 second(s)
The 809 element was initialized in 0,000 second(s)
The 810 element was initialized in 0,000 second(s)
The 811 element was initialized in 1,138 second(s)
The 812 element was initialized in 0,000 second(s)
The 900 element was initialized in 0,002 second(s)
The 901 element was initialized in 0,000 second(s)
The 902 element was initialized in 0,000 second(s)
The 903 element was initialized in 0,000 second(s)
The 904 element was initialized in 0,000 second(s)
The 914 element was initialized in 0,078 second(s)
The 915 element was initialized in 0,012 second(s)
The 917 element was initialized in 0,000 second(s)
The 918 element was initialized in 0,000 second(s)
The 919 element was initialized in 0,000 second(s)
The 920 element was initialized in 1,193 second(s)
The 923 element was initialized in 0,000 second(s)
The 924 element was initialized in 0,000 second(s)
The 925 element was initialized in 0,000 second(s)
The 926 element was initialized in 0,003 second(s)
The 927 element was initialized in 0,002 second(s)
The 928 element was initialized in 0,000 second(s)

10.08.2022 18:34:40 Debug mode is: False
10.08.2022 18:34:40 Active view is: Loading
10.08.2022 18:34:40 Advanced settings is visible: False
10.08.2022 18:34:40 The "UWP for all users" switch state is: UNCHECKED
10.08.2022 18:34:40 The OS conditions checkings started
10.08.2022 18:34:40 OsVersionCondition run result: True
10.08.2022 18:34:40 The next condition to be run: OsBuildVersionCondition
10.08.2022 18:34:40 OsBuildVersionCondition run result: True
10.08.2022 18:34:40 The next condition to be run: OsFilesCorruptedCondition
10.08.2022 18:34:40 OsFilesCorruptedCondition run result: True
10.08.2022 18:34:40 The next condition to be run: RebootRequiredCondition
10.08.2022 18:34:40 RebootRequiredCondition run result: True
10.08.2022 18:34:40 The next condition to be run: SingleInstanceCondition
10.08.2022 18:34:40 SingleInstanceCondition run result: True
10.08.2022 18:34:40 The next condition to be run: SingleAdminSessionCondition
10.08.2022 18:34:40 SingleAdminSessionCondition run result: True
10.08.2022 18:34:40 The next condition to be run: Win10TweakerCondition
10.08.2022 18:34:40 Win10TweakerCondition run result: True
10.08.2022 18:34:40 The next condition to be run: SycnexScriptCondition
10.08.2022 18:34:40 SycnexScriptCondition run result: True
10.08.2022 18:34:40 The next condition to be run: DefenderCorruptedCondition
10.08.2022 18:34:40 DefenderCorruptedCondition run result: True
10.08.2022 18:34:40 The next condition to be run: NewVersionCondition
10.08.2022 18:34:40 NewVersionCondition run result: True
10.08.2022 18:34:40 This is last condition
10.08.2022 18:34:40 It took 1 second(s) to check the OS conditions
10.08.2022 18:34:40 Active view is: Loading
10.08.2022 18:34:41 Initialization of the elements started
10.08.2022 18:34:41 The 900 element changed status to: CHECKED
10.08.2022 18:34:41 The 600 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 300 element changed status to: CHECKED
10.08.2022 18:34:41 The 901 element changed status to: CHECKED
10.08.2022 18:34:41 The 200 element changed status to: CHECKED
10.08.2022 18:34:41 The 301 element changed status to: CHECKED
10.08.2022 18:34:41 The 902 element changed status to: CHECKED
10.08.2022 18:34:41 The 201 element changed status to: CHECKED
10.08.2022 18:34:41 The 302 element changed status to: CHECKED
10.08.2022 18:34:41 The 202 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 903 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 303 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 904 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 204 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 304 element changed status to: CHECKED
10.08.2022 18:34:41 The 205 element changed status to: CHECKED
10.08.2022 18:34:41 The 305 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 306 element changed status to: CHECKED
10.08.2022 18:34:41 The 307 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 206 element changed status to: CHECKED
10.08.2022 18:34:41 The 308 element changed status to: CHECKED
10.08.2022 18:34:41 The 207 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 208 element changed status to: CHECKED
10.08.2022 18:34:41 The 309 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 209 element changed status to: CHECKED
10.08.2022 18:34:41 The 210 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 310 element changed status to: CHECKED
10.08.2022 18:34:41 The 212 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 311 element changed status to: CHECKED
10.08.2022 18:34:41 The 213 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 312 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 214 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 215 element changed status to: CHECKED
10.08.2022 18:34:41 The 216 element changed status to: CHECKED
10.08.2022 18:34:41 The 217 element changed status to: CHECKED
10.08.2022 18:34:41 The 218 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 220 element changed status to: CHECKED
10.08.2022 18:34:41 The 222 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 223 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 224 element changed status to: CHECKED
10.08.2022 18:34:41 The 225 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 226 element changed status to: CHECKED
10.08.2022 18:34:41 The 227 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 229 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 268 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 313 element changed status to: DISABLED
10.08.2022 18:34:41 The 314 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 100 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 101 element changed status to: CHECKED
10.08.2022 18:34:41 The 102 element changed status to: CHECKED
10.08.2022 18:34:41 The 103 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 700 element changed status to: CHECKED
10.08.2022 18:34:41 The 701 element changed status to: CHECKED
10.08.2022 18:34:41 The 104 element changed status to: CHECKED
10.08.2022 18:34:41 The 105 element changed status to: CHECKED
10.08.2022 18:34:41 The 702 element changed status to: CHECKED
10.08.2022 18:34:41 The 106 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 107 element changed status to: CHECKED
10.08.2022 18:34:41 The 108 element changed status to: CHECKED
10.08.2022 18:34:41 The 109 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 110 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 111 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 112 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 113 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 114 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 115 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 116 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 117 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 118 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 119 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 120 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 121 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 122 element changed status to: CHECKED
10.08.2022 18:34:41 The 123 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 124 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 125 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 126 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 127 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 800 element changed status to: CHECKED
10.08.2022 18:34:41 The 801 element changed status to: CHECKED
10.08.2022 18:34:41 The 914 element changed status to: CHECKED
10.08.2022 18:34:41 The 502 element changed status to: DISABLED
10.08.2022 18:34:41 The 230 element changed status to: DISABLED
10.08.2022 18:34:41 The 241 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 242 element changed status to: CHECKED
10.08.2022 18:34:41 The 243 element changed status to: CHECKED
10.08.2022 18:34:41 The 244 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 245 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 246 element changed status to: CHECKED
10.08.2022 18:34:41 The 247 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 248 element changed status to: CHECKED
10.08.2022 18:34:41 The 249 element changed status to: CHECKED
10.08.2022 18:34:41 The 250 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 251 element changed status to: CHECKED
10.08.2022 18:34:41 The 253 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 254 element changed status to: CHECKED
10.08.2022 18:34:41 The 255 element changed status to: CHECKED
10.08.2022 18:34:41 The 256 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 257 element changed status to: CHECKED
10.08.2022 18:34:41 The 258 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 259 element changed status to: CHECKED
10.08.2022 18:34:41 The 260 element changed status to: CHECKED
10.08.2022 18:34:41 The 261 element changed status to: CHECKED
10.08.2022 18:34:41 The 262 element changed status to: CHECKED
10.08.2022 18:34:41 The 262 element changed status to: DISABLED
10.08.2022 18:34:41 The 264 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 265 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 915 element changed status to: CHECKED
10.08.2022 18:34:41 The 917 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 918 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 919 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 500 element changed status to: CHECKED
10.08.2022 18:34:41 The 601 element changed status to: DISABLED
10.08.2022 18:34:41 The 501 element changed status to: DISABLED
10.08.2022 18:34:41 The 503 element changed status to: CHECKED
10.08.2022 18:34:41 The 504 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 602 element changed status to: CHECKED
10.08.2022 18:34:41 The 315 element changed status to: CHECKED
10.08.2022 18:34:41 The 316 element changed status to: CHECKED
10.08.2022 18:34:41 The 317 element changed status to: CHECKED
10.08.2022 18:34:41 The 318 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 319 element changed status to: CHECKED
10.08.2022 18:34:41 The 803 element changed status to: CHECKED
10.08.2022 18:34:41 The 320 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 321 element changed status to: CHECKED
10.08.2022 18:34:41 The 322 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 323 element changed status to: CHECKED
10.08.2022 18:34:41 The 324 element changed status to: CHECKED
10.08.2022 18:34:41 The 325 element changed status to: CHECKED
10.08.2022 18:34:41 The 326 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 327 element changed status to: CHECKED
10.08.2022 18:34:41 The 328 element changed status to: CHECKED
10.08.2022 18:34:41 The 329 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 330 element changed status to: CHECKED
10.08.2022 18:34:41 The 331 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 332 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 333 element changed status to: CHECKED
10.08.2022 18:34:41 The 334 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 335 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 336 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 337 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 338 element changed status to: CHECKED
10.08.2022 18:34:41 The 339 element changed status to: CHECKED
10.08.2022 18:34:41 The 340 element changed status to: CHECKED
10.08.2022 18:34:41 The 341 element changed status to: CHECKED
10.08.2022 18:34:41 The 342 element changed status to: CHECKED
10.08.2022 18:34:41 The 804 element changed status to: CHECKED
10.08.2022 18:34:41 The 343 element changed status to: CHECKED
10.08.2022 18:34:41 The 344 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 347 element changed status to: CHECKED
10.08.2022 18:34:41 The 348 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 349 element changed status to: DISABLED
10.08.2022 18:34:41 The 350 element changed status to: CHECKED
10.08.2022 18:34:41 The 805 element changed status to: CHECKED
10.08.2022 18:34:41 The 806 element changed status to: CHECKED
10.08.2022 18:34:41 The 807 element changed status to: CHECKED
10.08.2022 18:34:41 The 808 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 809 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 810 element changed status to: CHECKED
10.08.2022 18:34:41 The 351 element changed status to: DISABLED
10.08.2022 18:34:41 The 352 element changed status to: UNCHECKED
10.08.2022 18:34:41 The 353 element changed status to: CHECKED
10.08.2022 18:34:42 The 920 element changed status to: CHECKED
10.08.2022 18:34:42 The 923 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 924 element changed status to: CHECKED
10.08.2022 18:34:42 The 925 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 926 element changed status to: CHECKED
10.08.2022 18:34:42 The 927 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 928 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 811 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 812 element changed status to: CHECKED
10.08.2022 18:34:42 The 813 element changed status to: UNCHECKED
10.08.2022 18:34:42 The 814 element changed status to: CHECKED
10.08.2022 18:34:43 The 354 element changed status to: DISABLED
10.08.2022 18:34:43 The 355 element changed status to: UNCHECKED
10.08.2022 18:34:43 The 356 element changed status to: CHECKED
10.08.2022 18:34:44 The 357 element changed status to: DISABLED
10.08.2022 18:34:44 The 358 element changed status to: UNCHECKED
10.08.2022 18:34:44 It took 3 second(s) to initialize elements
10.08.2022 18:34:44 Initialization of the UWP elements started
10.08.2022 18:34:44 It took 1 second(s) to initialize the UWP elements
10.08.2022 18:34:44 Active view is: Privacy

Без создания логов человеческих ресурсов не хватало для вычленения багов. А после добавления логирования можно было просто сказать: "Пришлите лог работы программы", — и идти спокойно спать (нет лога — нет фикса), предвкушая завтрашний дебаг. С логом жить стало лучше, жить стало веселее!

Нельзя обойти стороной и PVS-Studio, который помогает мне найти ошибки в логике работы. Да, их было немного (ведь и проект в сухих цифрах не самый большой), но то ли я стал писать со временем лучше, то ли что-то еще повлияло. Первый раз, когда я натравил PVS-Studio на проект, она нашла в сумме около 30 предупреждений, которые и были исправлены. Многие из этих предупреждений были совсем не очевидны для меня.

После того, как основные функции приложения были готовы и более-менее заработали, оказалось, что найти даже в одной категории нужный твик не так просто, если их много. Захотелось функции поиска. :) Были идеи сделать файлы индекса для каждой локализации и искать в них, динамически подгружая, но пока мы ограничились обычным перебором названий и описаний элементов, использовав функцию Contains.

private async void SearchClickedAsync(object arg)
{
    await Task.Run(() =>
    {
        var stopwatch = Stopwatch.StartNew();
        var searchString = arg as string;
        FoundTextedElement.Clear();
        Search = SearchState.Running;
        SetVisibleViewTag(Tags.ViewSearch);
        FoundTextedElement = TextedElements.Where(element => element.Status != ElementStatus.DISABLED
		&& element.ContainsText(searchString))
		.ToList();
        Search = SearchState.Stopped;
        stopwatch.Stop();
        DebugHelper.StopSearch(searchString, stopwatch.Elapsed.TotalSeconds, foundTextedElement.Count);
    });
}

С ростом функционала я стал понимать, что очень много действий строится на одной парадигме. Потому было решено написать парочку так называемых хелперов по автоматизации однотипных действий. Так появилась папка Helpers, да и не особо уже парочка хелперов там. Они записывают данные, начиная от реестра и заканчивая проверкой на здоровье Microsoft Defender.

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

В облаке хранится JSON-файл, который и парсится. Есть две ветки: стабильная и бета-версия. При компиляции через GitHub Actions считывается тип релиза (release или pre-release) и через скрипт в файл AppHelper.cs записывается свойство билда "private const bool IS_RELEASE" $true или $false. В зависимости от этого при запуске программа читает то или иное свойство файла в облаке, чтобы определить наличие новой версии.

internal class NewVersionCondition : IStartupCondition
    {
        public bool HasProblem { get; set; }
        public ConditionsTag Tag { get; set; } = ConditionsTag.NewVersion;

        public bool Invoke()
        {
            DebugHelper.IsOnline();

            try
            {
                if (HttpHelper.IsOnline)
                {
                    HttpWebRequest request = WebRequest.CreateHttp(AppHelper.SophiAppVersionsJson);
                    request.UserAgent = AppHelper.UserAgent;
                    var response = request.GetResponse();

                    using (Stream dataStream = response.GetResponseStream())
                    {
                        StreamReader reader = new StreamReader(dataStream);
                        var serverResponse = reader.ReadToEnd();
                        var release = JsonConvert.DeserializeObject<ReleaseDto>(serverResponse);
                        DebugHelper.HasUpdateRelease(release);
                        var releasedVersion = new Version(AppHelper.IsRelease ? release.SophiApp_release : release.SophiApp_pre_release);
                        var hasNewVersion = releasedVersion > AppHelper.Version;

                        if (hasNewVersion)
                        {
                            DebugHelper.IsNewRelease();
                            ToastHelper.ShowUpdateToast(currentVersion: $"{AppHelper.Version}", newVersion: $"{releasedVersion}");
                        }
                        else
                        {
                            DebugHelper.UpdateNotNecessary();
                        }

                        return HasProblem = hasNewVersion;
                    }
                }

                return HasProblem;
            }
            catch (WebException e)
            {
                DebugHelper.HasException("An error occurred while checking for an update", e);
                return HasProblem;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message.Replace(":", null));
            }
        }
    }

А при нахождении всплывет вот такой милый тост с уведомлением:

ЗАМЕНИТЬ
ЗАМЕНИТЬ

SophiApp поддерживает динамически-генерируемый список установленных UWP-приложений, подгружая их локализованные имена и беря соответствующую иконку приложения, — ничего не захардкожено. Такое умеют лишь SophiApp и O&O AppBuster от O&O Software. Но эту функцию надо будет тоже как-нибудь переписать, так как она далеко не быстро работает.

В интерфейсе есть тумблер для переключения, в какой области удалять пакеты: в области пользователя или для всей ОС. Тогда все новые создаваемые пользователи не получат удаленные пакеты.

Hidden text
using SophiApp.Dto;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using System.Threading;
using Windows.ApplicationModel;
using Windows.Foundation;
using Windows.Management.Deployment;

namespace SophiApp.Helpers
{
    internal class UwpHelper
    {
        internal static Package GetPackage(string packageName)
        {
            var sid = OsHelper.GetCurrentUserSid().Value;
            var packageManager = new PackageManager();
            return packageManager.FindPackagesForUser(sid)
                                 .First(package => package.Id.Name.Equals(packageName));
        }

        internal static IEnumerable<UwpElementDto> GetPackagesDto(bool forAllUsers = false)
        {
            var currentUserScript = @"# The following UWP apps will be excluded from the display
$ExcludedAppxPackages = @(
# Microsoft Desktop App Installer
'Microsoft.DesktopAppInstaller',

# Store Experience Host
'Microsoft.StorePurchaseApp',

# Notepad
'Microsoft.WindowsNotepad',

# Microsoft Store
'Microsoft.WindowsStore',

# Windows Terminal
'Microsoft.WindowsTerminal',
'Microsoft.WindowsTerminalPreview',

# Web Media Extensions
'Microsoft.WebMediaExtensions'
)

$AppxPackages = Get-AppxPackage -PackageTypeFilter Bundle | Where-Object -FilterScript {$_.Name -notin $ExcludedAppxPackages}

# The Bundle packages contains no Microsoft Teams
if (Get-AppxPackage -Name MicrosoftTeams -AllUsers:$false)
{
	# Temporarily hack: due to the fact that there are actually two Microsoft Teams packages, we need to choose the first one to display
	$AppxPackages += Get-AppxPackage -Name MicrosoftTeams -AllUsers:$false | Select-Object -Index 0
}

# The Bundle packages contains no Spotify
if (Get-AppxPackage -Name SpotifyAB.SpotifyMusic -AllUsers:$false)
{
	# Temporarily hack: due to the fact that there are actually two Microsoft Teams packages, we need to choose the first one to display
	$AppxPackages += Get-AppxPackage -Name SpotifyAB.SpotifyMusic -AllUsers:$false | Select-Object -Index 0
}

$PackagesIds = [Windows.Management.Deployment.PackageManager, Windows.Web, ContentType = WindowsRuntime]::new().FindPackages() | Select-Object -Property DisplayName, Logo -ExpandProperty Id | Select-Object -Property Name, DisplayName, Logo

foreach ($AppxPackage in $AppxPackages)
{
	$PackageId = $PackagesIds | Where-Object -FilterScript {$_.Name -eq $AppxPackage.Name}

	if (-not $PackageId)
	{
		continue
	}

	 [PSCustomObject]@{
		Name            = $AppxPackage.Name
		PackageFullName = $AppxPackage.PackageFullName
		Logo            = $PackageId.Logo
		DisplayName     = $PackageId.DisplayName
	}
}";
            var allUsersScript = @"# The following UWP apps will be excluded from the display
$ExcludedAppxPackages = @(
# Microsoft Desktop App Installer
'Microsoft.DesktopAppInstaller',

# Store Experience Host
'Microsoft.StorePurchaseApp',

# Notepad
'Microsoft.WindowsNotepad',

# Microsoft Store
'Microsoft.WindowsStore',

# Windows Terminal
'Microsoft.WindowsTerminal',
'Microsoft.WindowsTerminalPreview',

# Web Media Extensions
'Microsoft.WebMediaExtensions'
)

$AppxPackages = Get-AppxPackage -PackageTypeFilter Bundle -AllUsers | Where-Object -FilterScript {$_.Name -notin $ExcludedAppxPackages}

# The Bundle packages contains no Microsoft Teams
if (Get-AppxPackage -Name MicrosoftTeams -AllUsers:$true)
{
	# Temporarily hack: due to the fact that there are actually two Microsoft Teams packages, we need to choose the first one to display
	$AppxPackages += Get-AppxPackage -Name MicrosoftTeams -AllUsers:$true | Select-Object -Index 0
}

# The Bundle packages contains no Spotify
if (Get-AppxPackage -Name SpotifyAB.SpotifyMusic -AllUsers:$true)
{
	# Temporarily hack: due to the fact that there are actually two Microsoft Teams packages, we need to choose the first one to display
	$AppxPackages += Get-AppxPackage -Name SpotifyAB.SpotifyMusic -AllUsers:$true | Select-Object -Index 0
}

$PackagesIds = [Windows.Management.Deployment.PackageManager, Windows.Web, ContentType = WindowsRuntime]::new().FindPackages() | Select-Object -Property DisplayName, Logo -ExpandProperty Id | Select-Object -Property Name, DisplayName, Logo

foreach ($AppxPackage in $AppxPackages)
{
	$PackageId = $PackagesIds | Where-Object -FilterScript {$_.Name -eq $AppxPackage.Name}

	if (-not $PackageId)
	{
		continue
	}

	 [PSCustomObject]@{
		Name            = $AppxPackage.Name
		PackageFullName = $AppxPackage.PackageFullName
		Logo            = $PackageId.Logo
		DisplayName     = $PackageId.DisplayName
	}
}";

            return PowerShell.Create()
                             .AddScript(forAllUsers ? allUsersScript : currentUserScript)
                             .Invoke()
                             .Where(uwp => uwp.Properties["Logo"].Value != null)
                             .Select(uwp => new UwpElementDto()
                             {
                                 Name = uwp.Properties["Name"].Value as string,
                                 PackageFullName = uwp.Properties["PackageFullName"].Value as string,
                                 Logo = uwp.Properties["Logo"].Value.GetFirstValue<Uri>(),
                                 DisplayName = uwp.Properties["DisplayName"].Value.GetFirstValue<string>()
                             });
        }

        internal static void InstallPackage(string package)
        {
            var packageUri = new Uri(package);
            var packageManager = new PackageManager();
            var deploymentOperation = packageManager.AddPackageAsync(packageUri, null, DeploymentOptions.None);
            var opCompletedEvent = new ManualResetEvent(false);
            deploymentOperation.Completed = (depProgress, status) => { opCompletedEvent.Set(); };
            opCompletedEvent.WaitOne();
        }

        internal static bool PackageExist(string packageName)
        {
            var sid = OsHelper.GetCurrentUserSid().Value;
            var packageManager = new PackageManager();
            return packageManager.FindPackagesForUser(sid)
                                 .Where(package => package.Id.Name == packageName)
                                 .Count() > 0;
        }

        internal static void RemovePackage(string packageFullName, bool allUsers)
        {
            var stopwatch = Stopwatch.StartNew();
            var packageManager = new PackageManager();
            var deploymentOperation = packageManager.RemovePackageAsync(packageFullName, allUsers ? RemovalOptions.RemoveForAllUsers : RemovalOptions.None);
            var opCompletedEvent = new ManualResetEvent(false);
            deploymentOperation.Completed = (depProgress, status) => { opCompletedEvent.Set(); };
            opCompletedEvent.WaitOne();
            stopwatch.Stop();

            if (deploymentOperation.Status == AsyncStatus.Error)
            {
                var deploymentResult = deploymentOperation.GetResults();
                DebugHelper.UwpRemovedHasException(packageFullName, deploymentResult.ErrorText);
                return;
            }

            DebugHelper.UwpRemoved(packageFullName, stopwatch.Elapsed.TotalSeconds, deploymentOperation.Status);
        }
    }
}
private void GetUwpElements()
{
    DebugHelper.StartInitUwpApps();
    var stopwatch = Stopwatch.StartNew();
    UwpElementsCurrentUser = UwpHelper.GetPackagesDto(forAllUsers: false)
	.Select(dto => FabricHelper.CreateUwpElement(dto))
	.OrderBy(uwp => uwp.DisplayName)
	.ToList();

    UwpElementsAllUsers = UwpHelper.GetPackagesDto(forAllUsers: true)
	.Select(dto => FabricHelper.CreateUwpElement(dto))
	.OrderBy(uwp => uwp.DisplayName)
	.ToList();
    stopwatch.Stop();
    DebugHelper.StopInitUwpApps(stopwatch.Elapsed.TotalSeconds);
}

При генерации списка, чтобы случайно не сломать Windows, кроме системных пакетов, в исключение попадают следующие пакеты:

Microsoft.DesktopAppInstaller,
Microsoft.StorePurchaseApp,
Microsoft.WindowsNotepad,
Microsoft.WindowsStore,
Microsoft.WindowsTerminal,
Microsoft.WindowsTerminalPreview,
Microsoft.WebMediaExtensions,
Microsoft.AV1VideoExtension,
Microsoft.HEVCVideoExtension

У каждого элемента в приложении есть состояние: включен или выключен. При запуске все элементы проверяют свое состояние и отрисовывают его в UI, показывая реальное состояние каждой функции в Windows. Да, для этого пришлось написать и отладить 130 функций для проверки состояния каждого элемента. И это не всегда быстро, так как WMI еще живее всех живых.

После применения какой-либо функции идет повторное считывание всех функций (и проставление галочек с радиокнопками соответственно), мягкий перезапуск переменных, панели задач, меню "Пуск" и отправка команды по обновлению Рабочего стола (F5). Кто в комментариях напишет, для какой функции это надо, получит зачет автоматом. :D

private const int WM_SETTINGCHANGE = 0x1a;
private const int SMTO_ABORTIFHUNG = 0x0002;
private const string TRAY_SETTINGS = "TraySettings";
private static readonly IntPtr hWnd = new IntPtr(65535);
private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff);

// Virtual key ID of the F5 in File Explorer
private static readonly UIntPtr UIntPtr = new UIntPtr(41504);

public static void PostMessage() => PostMessageW(hWnd, Msg, UIntPtr, IntPtr.Zero);

public static void RefreshEnvironment()
{
	// Update Desktop Icons
	SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
	// Update Environment Variables
	SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, null, SMTO_ABORTIFHUNG, 100, IntPtr.Zero);
	// Update Taskbar
	SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, TRAY_SETTINGS);
	// Update Start Menu
	ProcessHelper.Stop(START_MENU_PROCESS);
}

Код логики элемента интерфейса:

internal class TextedElement : IElement
    {
        private string description;
        private string header;
        private ElementStatus status;

        public TextedElement((TextedElementDto Dto, Action<TextedElement, Exception> ErrorHandler,
                                EventHandler<TextedElement> StatusHandler, Func<bool> Customisation, UILanguage Language) parameters)
        {
            CustomisationStatus = parameters.Customisation;
            Descriptions = parameters.Dto.Description ?? parameters.Dto.ChildDescription;
            ErrorOccurred = parameters.ErrorHandler;
            Headers = parameters.Dto.Header ?? parameters.Dto.ChildHeader;
            Id = parameters.Dto.Id;
            Language = parameters.Language;
            StatusChanged = parameters.StatusHandler;
            Tag = parameters.Dto.Tag;
            ViewId = parameters.Dto.ViewId;
            Windows10Supported = parameters.Dto.Windows10Supported;
            Windows11Supported = parameters.Dto.Windows11Supported;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public event EventHandler<TextedElement> StatusChanged;

        protected Dictionary<UILanguage, string> Descriptions { get; set; }

        internal Func<bool> CustomisationStatus { get; set; }

        internal Action<TextedElement, Exception> ErrorOccurred { get; set; }
        internal UILanguage Language { get; set; }
        internal bool Windows10Supported { get; private set; }
        internal bool Windows11Supported { get; private set; }

        public string Description
        {
            get => description;
            set
            {
                description = value;
                OnPropertyChanged("Description");
            }
        }

        public string Header
        {
            get => header;
            set
            {
                header = value;
                OnPropertyChanged("Header");
            }
        }

        public Dictionary<UILanguage, string> Headers { get; set; }
        public uint Id { get; }

        public ElementStatus Status
        {
            get => status;
            set
            {
                status = value;
                OnPropertyChanged("Status");
                StatusChanged?.Invoke(null, this);
            }
        }

        public string Tag { get; }
        public uint ViewId { get; }

        private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        internal void ChangeStatus() => Status = Status == ElementStatus.UNCHECKED ? ElementStatus.CHECKED : ElementStatus.UNCHECKED;

        internal virtual bool ContainsText(string text)
        {
            var desiredText = text.ToLower();
            return Header.ToLower().Contains(desiredText) || Description.ToLower().Contains(desiredText);
        }

        internal virtual void GetCustomisationStatus()
        {
            try
            {
                Status = CustomisationStatus.Invoke() ? ElementStatus.CHECKED : ElementStatus.UNCHECKED;
            }
            catch (Exception e)
            {
                ErrorOccurred?.Invoke(this, e);
            }
        }

        internal virtual void Initialize()
        {
            var stopwatch = Stopwatch.StartNew();
            ChangeLanguage(Language);
            GetCustomisationStatus();
            stopwatch.Stop();
            DebugHelper.TextedElementInit(Id, stopwatch.Elapsed.TotalSeconds);
        }

        public virtual void ChangeLanguage(UILanguage language)
        {
            Header = Headers[language];
            Description = Descriptions[language];
        }
    }

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

internal const string _700_CLEANUP_TASK_ARGS = @"-WindowStyle Hidden -Command Get-Process -Name cleanmgr | Stop-Process -Force
Get-Process -Name Dism | Stop-Process -Force
Get-Process -Name DismHost | Stop-Process -Force
$ProcessInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$ProcessInfo.FileName = """"""$env:SystemRoot\system32\cleanmgr.exe""""""
$ProcessInfo.Arguments = """"""/sagerun:1337""""""
$ProcessInfo.UseShellExecute = $true
$ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Minimized
$Process = New-Object -TypeName System.Diagnostics.Process
$Process.StartInfo = $ProcessInfo
$Process.Start() | Out-Null
Start-Sleep -Seconds 3
[int]$SourceMainWindowHandle = (Get-Process -Name cleanmgr | Where-Object -FilterScript {$_.PriorityClass -eq """"""BelowNormal""""""}).MainWindowHandle
function MinimizeWindow
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        $Process
    )
    $ShowWindowAsync = @{
        Namespace = """"""WinAPI""""""
        Name = """"""Win32ShowWindowAsync""""""
        Language = """"""CSharp""""""
        MemberDefinition = @'
[DllImport(""""""user32.dll"""""")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow)
'@
    }

    if (-not(""""""WinAPI.Win32ShowWindowAsync"""""" -as [type]))
    {
        Add-Type @ShowWindowAsync
    }
	$MainWindowHandle = (Get-Process -Name $Process | Where-Object -FilterScript {$_.PriorityClass -eq """"""BelowNormal""""""}).MainWindowHandle
    [WinAPI.Win32ShowWindowAsync]::ShowWindowAsync($MainWindowHandle, 2)
}

while ($true)
{
    [int]$CurrentMainWindowHandle = (Get-Process -Name cleanmgr | Where-Object -FilterScript {$_.PriorityClass -eq """"""BelowNormal""""""}).MainWindowHandle
    if ($SourceMainWindowHandle -ne $CurrentMainWindowHandle)
    {
        MinimizeWindow -Process cleanmgr
        break
    }
    Start-Sleep -Milliseconds 5
}
$ProcessInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$ProcessInfo.FileName = """"""$env:SystemRoot\system32\dism.exe""""""
$ProcessInfo.Arguments = """"""/Online /English /Cleanup-Image /StartComponentCleanup /NoRestart""""""
$ProcessInfo.UseShellExecute = $true
$ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Minimized
$Process = New-Object -TypeName System.Diagnostics.Process
$Process.StartInfo = $ProcessInfo
$Process.Start() | Out-Null";

internal const string _700_CLEANUP_TOAST_TASK_ARGS = @"-WindowStyle Hidden -Command [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
[xml]$ToastTemplate = @""""""
<toast duration=""""""Long"""""" scenario=""""""reminder"""""">
	<visual>
		<binding template = """"""ToastGeneric"""""" >
            <text>*</text>
			<group>
				<subgroup>
					<text hint-style=""""""title"""""" hint-wrap=""""""true"""""">*</text>
				</subgroup>
			</group>
			<group>
				<subgroup>
					<text hint-style=""""""body"""""" hint-wrap=""""""true"""""">*</text>
				</subgroup>
			</group>
		</binding>
	</visual>
	<audio src=""""""ms-winsoundevent:notification.default""""""/>
    <actions>
        <input id=""""""SnoozeTimer"""""" type=""""""selection"""""" title=""""""*"""""" defaultInput=""""""1"""""">
			<selection id=""""""1"""""" content=""""""*"""""" />
			<selection id=""""""30"""""" content=""""""*"""""" />
			<selection id=""""""240"""""" content=""""""*"""""" />
		</input>
		<action activationType=""""""system"""""" arguments=""""""snooze"""""" hint-inputId=""""""SnoozeTimer"""""" content="""""""""""" id=""""""test-snooze""""""/>
		<action arguments=""""""WindowsCleanup:"""""" content=""""""*"""""" activationType=""""""protocol""""""/>
		<action arguments=""""""dismiss"""""" content="""""""""""" activationType=""""""system""""""/>
	</actions>
</toast>
""""""@
$ToastXml = [Windows.Data.Xml.Dom.XmlDocument]::New()
$ToastXml.LoadXml($ToastTemplate.OuterXml)
$ToastMessage = [Windows.UI.Notifications.ToastNotification]::New($ToastXML)
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(""""""windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"""""").Show($ToastMessage)";

При триггере, например, задания по очистке временных файлов пользователь увидит, как откроется специально написанное окно с предложением запустить очистку Windows, а также удалить неиспользованные обновления (замененные). В интерактивном тосте пользователь может отложить запуск на разные интервалы времени, отменить вовсе или запустить. При запуске начнет работать cleanmgr.exe, а затем dism.exe /Online /English /Cleanup-Image /StartComponentCleanup /NoRestart, что позволяет автоматизировать столь рутинное занятие. Аналогичные тосты всплывают при очистке папки временных файлов и папки %SystemRoot%\SoftwareDistribution\Download с той лишь разницей, что задание для очистки последней папки будет ждать, когда остановится служба Центра обновлений Windows, чтобы случайно не удалить уже скачанные пакеты обновления, предназначенные для установки.

Всплывающие тосты

Некоторые функции рассчитаны на настоящих любителей экзотики, потому было принято решение скрыть их по умолчанию. К таким, например, относится функция по переносу папки %TEMP% официальным образом в корень диска C:\.

Вишенкой креатива Дмитрия стала идея сделать несколько функций, но не показывать их в интерфейсе. Мол, это потенциально опасные для пользователя функции, и ему не нужно про них знать. На мой резонный вопрос: "Зачем?", — он парировал: "Программировай!". К счастью, WPF очень гибкий, да и я к тому времени уже не впадал в шок от его идей, хотя часто не представлял, как это сделать.

public bool AdvancedSettingsVisibility
{
	get => advancedSettingsVisibility;
	set
	{
		advancedSettingsVisibility = value;
		DebugHelper.AdvancedSettinsVisibility(value);
		OnPropertyChanged(AdvancedSettingsVisibilityPropertyName);
	}
}

private void AdvancedSettingsClicked(object args) => AdvancedSettingsVisibility = AdvancedSettingsVisibility.Invert();

В 2020 году встал вопрос об автоматизации компиляции. Легче сказать, чем сделать. В это же время GitHub аккурат выкатил свой Actions, и после множества сломанных копий при релизе триггерится Action, беря версию тэга релиза и записывает перед компиляцией в файл AssemblyInfo.cs, чтобы в заголовке программы была видна версия. Дальше идет непосредственно компиляция с выкачиванием последних версий зависимостей из своих репозиториев, архивация в ZIP-архив и автоматическая загрузка готового архива на страницу релиза. На опенсорсе, конечно, свет не сошелся, но для успокоения пользователей мы решили добавить в сборку шаг получения хэш-суммы собранного архива.

cloc творит чудеса!
cloc творит чудеса!

Также через Actions идет подсчет количества строк кода в репозитории. При пуше или пулл реквесте триггерится Action, который выкачивает cloc. Дальше через GitHub-секрет в gist Дмитрия записывается JSON-файл с данными по количеству строк кода в репозитории. В данном случае записалось "message":"25.1k". В дальнейшем этот JSON отдается бэйджику от shields.io, который и рендерит уже красивую зеленую плашку.

Ну, и пару слов о логотипе. Перед новогодним релизом, когда было уже не так стыдно показать работающий билд SophiApp, Владимир посоветовал Наталью как фриланс-дизайнера, которая и нарисовала текущий логотип, за что ей и спасибо.

Такую разработку не дай бог каждому!
Такую разработку не дай бог каждому!

Выводы и планы на будущее

Выводы напрашиваются, собственно, сами собой:

  • Надо здраво рассчитывать свои силы, если берешься за такой проект; 

  • Четко прорабатывать техническое задание, чтобы знать объем работ;

  • Уметь терпеть и не бросать все на полпути;

  • И вообще счастье — в преодолении. :-)

Отдельное спасибо Дмитрию за то, что он прошел этот путь со мной от начала и до конца. Ну, и еще кое-что: мечтайте осторожно — мечты сбываются, ведь Дмитрий после 20 лет работы системным администратором решился сменить профессию, подавшись в разработчики. Сейчас он работает в крупной российской девелоперской компании. Если бы не мой комментарий, ничто бы в нашей жизни не изменилось.

Если вам интересны новости ИТ и технологий из первоисточников на английском, можете подписаться на мой новостной канал Sophia News, обсудить их в чате Sophia Chat, где можно задать вопросы по SophiApp, Sophia Script, ПК, ОС и прочим темам про ИТ.

Все баги и пожелания можете оставлять здесь, в Discord, в чате Telegram-группы или создайте Issue на GitHub.

Спасибо, что пережили с нами еще раз разработку SophiApp!

P.S. Спасибо за правки и редактуру текста DoubleSharp и Инне Пристягиной из PVS-Studio.

Жизнь с SophiApp
Жизнь с SophiApp

Комментарии (83)


  1. Sanctuary_s Автор
    20.08.2022 12:32
    -2

    То есть ты хранишь Mimikatz в открытую и удивляешься, что Defender ее удалил? У меня нет слов. У вас безопасности же нет, как я понял? И админы знают, что у них на серваке такая прога лежит?

    легитимную информацию

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

    И вообще добавлять в исключение эксплоиты — для слабаков. Надо же дождаться, чтобы работающий АВ удалил эксплоит, а потом гневно ругаться на то, что "Windows Defender это вредоносное ПО ". Я просто в ужасе сижу.

    Читатели, я один удивлен этому, или все считают, судя по заплюсованному комменатрию, что так и надо: на серваке хранить в открытую эксплоит, на который триггерится любой АВ, и он не должен на него реагировать никак, а если реагирует, то это плохой АВ?


  1. Lelant0s
    19.08.2022 16:10
    +6

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



    1. RAX7
      19.08.2022 21:34

      Похоже следующая мажорная версия станет платной

      Hi. Sorry for the late reply. I was in flames at work. :) The described feature was already discussed by me and my colleague. It will be implemented, but only in SophiApp 2.0 and only in a paid version. Somewhere in Autumn, 2022. We have a list what to change in the next big release. Now we're fully focused on near 1.0.0.26 stable release. Let's not make premature plans, how things will be. Better to live up until this Autumn first. :) n

      https://github.com/Sophia-Community/SophiApp/issues/37#issuecomment-1060044562

      так что ваше желание скоро исполнится для всех пользователей.


      1. Sanctuary_s Автор
        19.08.2022 21:42

        Сперва надо дожить. А во-вторых, бесплатная будет всегда опенсорсной и бесплатной, поддерживающейся на GitHub. Так что тень на плетень не надо наводить.


        1. RAX7
          19.08.2022 21:52

          бесплатная будет всегда опенсорсной

          это радует. Но на сколько я понял разделение на платную и бесплатную версию все же будет?


          1. Sanctuary_s Автор
            19.08.2022 21:57

            Для начала надо переписать все опять (какой, 7 раз уже?) с учетом того опыта, заложив возможность интегрировать тот функционал, который хотим. Оплатить разработку нового UI (это около 30 000 примерно). Да и платная версия все-таки рассчитана больше на понимающих людей, чем на тех, кто просто настроил и забыл. Обычные пользователи ничего не ощутят. Пока такие планы. :)


            1. RAX7
              19.08.2022 22:22
              +2

              Отличные планы и хорошо что вы ищите силы и средства на дальнейшую разработку софтины, но вариант с разделение на платную/бесплатную версию, как по мне неудачен.

              1) если для себя, то покупать софтину которой я буду пользоваться раз в 2-5 лет при очередной переустановке винды я вряд ли буду. А если меня позовут в гости как "тыжпрограмиста" я не буду на чужом компе заниматься активацией платных функций.

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

              В любом случае надеются, что если сделаете платную версию, то не урежете функционал бесплатной.

              P.S. спасибо за прекрасную софтину.


              1. Sanctuary_s Автор
                19.08.2022 22:31

                Да, тут все не так просто, как может показаться. Как показала практика, продается все хорошо. :) Я ни разу не видел, чтобы в какой-то твикер вшивали малварь. Лишь активатор на KMS-основе подвержен такой истории. Надо лишь знать официальные форумы. :)

                > если сделаете платную версию, то не урежете функционал бесплатной.

                Урезать никто и никогда не будет. Речь идет лишь о расширении функционала. Например, экспорт и импорт пресетов. Сам список функций не будет трогаться, а лишь расширяться.


                1. Lelant0s
                  19.08.2022 22:56

                  Я сам не программист, а Prod.Man., поэтому у меня такое, скорее профессиональное, любопытство насчет ценовой модели: насколько жизнеспособно (с целью максимизации вашей прибыли, есессно) разделение на условно Standard и Pro версию в разрезе "далекоидучести" твика (Pro, понятное дело, платная).

                  Что я имею в виду: условно, поменять форму курсора это фан без далеко идущих планов, не более. Вырезать что-то лишнее - это где-то посередине, и может быть отнесено в обе стороны (т.е. надо думать - что уходит в Pro, а что остаётся в Standard), а вот твики с уровнями доступа юзера, протоколами и проч. - это явно уже админовские заморочки (ну либо для себя, но человек явно погруженный в тему и вы экономите ему время), а значит - можно разумную сумму за это брать.

                  Имеет смысл?

                  ПыСы. Там ниже народ пишет, что вы недостаточно даете право выбора решать что и как твикать. Я не берусь судить, т.к. пока сам не пробовал, но тоже кстати вариант - бесплатная вся на пресетах, платная - полная свобода выбора. Обе аудитории получают ровно то, что хотят.


                  1. Sanctuary_s Автор
                    19.08.2022 23:10

                    Там ниже народ пишет, что вы недостаточно даете право выбора решать что и как твикать.

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


                    1. Sanctuary_s Автор
                      19.08.2022 23:17

                      Вообще многое в подходе к проверки в том числе поменяется: при возникновении ошибки не будет exit с занесением в лог. Все-таки надо ошибки потом все обрабатывать, ничего страшного не случилось. Надеюсь, у нас получится оправдать возложенные на нас надежды. :)
                      P.S. макет UWP меня тоже бесит с этими обрубленными названиями пакетов. :D


  1. xFFFF
    19.08.2022 17:35
    +1

    Проделана огромная работа) Я думаю, что можно было бы существенно уменьшить количество труда, если бы были использованы сторонние наработки. Например, для MVVM я бы использовал библиотеку CommunityToolkit.Mvvm. Элементы управления так же можно взять готовые.


  1. Mingun
    19.08.2022 18:39
    +6

    Да… Вот так читаешь про метания про трудности с «UI с нормальным, проработанным UX». А потом тебя внезапно как обухом по голове


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


    1. dartraiden
      19.08.2022 18:42
      +3

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


      1. uhf
        19.08.2022 20:55
        +5

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


        1. Diamos
          19.08.2022 22:14
          +5

          К сожалению, идеология данного автора прямо противоположна Вашему (и моему) взгляду, исходя из предыдущей дискуссии с ним на эту тему)).
          История точь-в-точь как и с замечательным opensource редактором FSNotes — скверный характер автора убивает всю перспективность замечательного проекта. К примеру, на вопрос о расширении возможностей работы с таблицами, ответ в духе «этого не будет, потому что я так хочу и клал я на ваши пожелания». Самое любопытное, что люди готовы платить деньги за продукт и всего лишь хотят нормальную офлайновую замену notion, evernote и т.д.

          Здесь, на мой взгляд, аналогично — продукт весьма перспективен, но с треском разбивается об авторитарную идеологию «пользователь не может быть умным, должен знать своё место и делать что я скажу». С другой стороны, для каждого рыбака найдётся своя рыбка, так что восторженные возгласы домохозяек точно будут.


          1. Sanctuary_s Автор
            19.08.2022 22:25
            -1

            Да нет, на самом деле я все понимаю, но боязнь навредить привела к такой политики. Со временем многие ограничения спадут. А к добавлению нового функционала, если он не вредит ОС, я всегда только рад. Так что не надо так))


            1. Diamos
              19.08.2022 22:30
              +1

              Если вы с командой всё-таки прийдете к тому, чтобы добавить в приложение pro-режим, в котором можно извращаться над системой как душе угодно, уверяю Вас, что масса продвинутых пользователей получит оргазм. Лично я буду в первых рядах на покупку продукта)


              1. Sanctuary_s Автор
                19.08.2022 22:33

                Я полностью согласен. Возможно, какой-то нибудь hardcore mode. Но у меня даже знаний таких нет, что можно сделать с ОС, извратив ее суть. Что, удалить MS Store? Edge? У меня фантазий больше нет. Ха-ха.

                Но я понимаю, что, если не можешь победить, то возглавь. :)


                1. klounader
                  19.08.2022 23:56
                  +1

                  MS Store? Edge?

                  Разве ж это хардкор? Первым делом обычно сносится за ненадобностью.
                  Хардкор, это когда венда представляет из себя чисто операционную систему, потребляет пол гига оперативы, занимает гигов пять на диске и летает на уровне XP.


                  1. Sanctuary_s Автор
                    20.08.2022 00:01

                    Первым делом обычно сносится за ненадобностью.

                    А чем не угодил Store, если через него обновляются многие приложухи полезные? Та же локализация через него прилетает. Terminal. Edge чем так-то мешает? Ну, есть он и есть. IE же есть тоже есть. Ну, есть и есть.

                    потребляет пол гига оперативы

                    Прошли времена... Сейчас по 32 ГБ дома у людей. нет уже смысла гнаться за таким потреблением...

                    на уровне XP.

                    11 не летает, что ли? Как ракета на 16 потоках и SSD с NMVe-проколом. В разы быстрее, чем старушка XP на дерганном HDD. :)

                    Другие примеры жду.))


                    1. klounader
                      20.08.2022 15:04
                      +4

                      Иногда от операционной системы требуется лишь только операционная система без навязанного вагона бесполезного барахла.
                      А вы запустите ХР на SSD — это же кайф неописуемый.
                      А потом 11ю на дёрганном HDD — вот уж где полный изврат.
                      Ну и опять же, не у всех людей 32 гига с нвме и не везде нужны такие мощности. Если брать всякие нанокомпы, терминалы, да и прочие старые компы тоже, то там уж как нигде требуется ось, занимающая минимально возможное количество ресурсов для максимально возможного быстродействия возложенной на неё задачи.
                      Ибо там любой шорох автообновлятора и прочей очень умной шушеры просто поставит железку на кол, а поставленная задача перестанет выполняться.
                      Так что не просто так народ выпиливает все эти дефендеры и прочие свистопляски всеми возможными способами, до которых могут дотянуться. Они реально очень много жрут и постоянно компостируют мозги своим присутствием.
                      Потом помножим всё это на мобильное устройство, в котором дефолтная ось батарею пожирает быстрее, а бесполезного тепла генерирует больше на радость Грете Тунберг.
                      Если таких твикеров много и они пользуются активным спросом, то очевидно, что проблема не в полутора нищебродах на 8 млрд человек, которые до сих пор сидят на коре два дуба, а куда шире.
                      Просто посмотрите на эту ситуацию не с топового компа в крутом офисе, а с приближенного к земной реальности недорогого нетбука, к примеру.
                      Для остроты ощущений, воткните в него ноутбучный HDD и оптимизируйте до тех пор, пока не пропадёт эпилептический приступ. Заодно поймёте, что и как действительно влияет на работу оси в замедленной съёмке, а не через призму эмулятора эфемерного вакуума.


            1. mixsture
              20.08.2022 15:39
              +1

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


              1. Sanctuary_s Автор
                20.08.2022 15:45
                +1

                Потому в 81 билде это канет в Лету. надеюсь, уже в понедельник-вторник. :)


        1. dartraiden
          21.08.2022 10:19

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

          Пользователь хочет, чтобы твик сделал то, что написано в его описании, а не сломал систему.

          Вот похожий пример из нашего проекта. Пользователь ставит компонент от версии X на версию X-1 вовсе не потому, что он хочет именно этого несовпадения версий. Он его ставит, потому что ему нужен компонент.


          1. uhf
            21.08.2022 10:35
            +1

            Тут в первую очередь надо определиться, что понимать под поломкой системы.
            Вы выше написали: «сломает из-за того, что теперь определенную службу отключать нельзя». А что значит «нельзя»? Программно нельзя, или потому что какой-то человек так решил? По мнению автора твикера, Defender отключать нельзя, так как это «сломает» систему. Будем считать его мнение единственно правильным?


            1. dartraiden
              21.08.2022 10:37

              А что значит «нельзя»?
              Например, нарушится корректная работа системы, потому что в очередном выпуске Windows на эту службу завязано что-то критически важное. Грубо говоря «нельзя» означает «нельзя стрелять себе в ногу — будет мясо, кровь, больно и инвалидность». Но, конечно, найдётся человек, который будет свято отстаивать своё право изувечить свою ногу.

              Будем считать его мнение единственно правильным?
              Вот в том и беда, что пользователи считают себя умнее даже инженеров производителя ОС, не то что производителя твикера…


              1. uhf
                21.08.2022 11:02
                +1

                что-то критически важное
                — что? Очередная телеметрия? Очень нужная служба оптимизации кэша сервиса доставки файлов предзапуска системы инсталляции магазина обновлений?
                нельзя стрелять себе в ногу — будет мясо, кровь, больно и
                инвалидность
                «Нельзя, потому что нельзя» — объяснение на уровне некоторых недалеких родителей, которые своих детей пугают бабайкой. Нормальные объясняют причинно-следственную связь. Я уж не говорю про технический ресурс.
                пользователи считают себя умнее даже инженеров производителя ОС
                Вы считаете их по дефолту тупее? Ну зря.
                Ну допустим инженеры производителя всегда умнее. А вы не задумывались, что принимают решения они не самостоятельно, задачи им ставят другие люди, и далеко не всегда в интересах пользователя?


              1. klounader
                21.08.2022 23:43
                -1

                Можно согласиться с тем, что производитель видит дизайн своего продукта таким, каким он хочет его видеть и/или каким хочет, чтобы он был у пользователей. Но предоставив пользователю какие-то политики безопасности и при этом их анально огородив от вмешательства — попахивает вахтёрством.
                Не спорю, есть в некоторых местах и правильные решения. А в некоторых местах есть и неправильные с точки зрения пользователя. Вот не хочет он UAC в том виде, в котором он существует и хоть ты трести, а пока этим дерьмом будет пользоваться неудобно, то от него будут избавляться. Как и с дефендером, который под лозунгом «топим за безопасность» отжирает все ресурсы компа и стучит папочке не только на 25 рандомных символов, честно стыренных из Хогвартса. Автоматические обновления также поставляют на территорию клиента Яндекс.Зонды и уничтожают будку с Весёлым Роджером. Тут уже не до вопросов кто умнее, когда продают намеренно дырявые кастрюли, а в соседней палатке можно воспользоваться сварочным аппаратом, чтобы кастрюлю можно было заварить обратно.

                Мне вот интересно, Билл Гейтс, Яндекс и прочая шушера сами вот пользуются своими продуктами и их ничего в них не смущает? Или может у них есть какие-то свои специальные секреты и патчи?


                1. dartraiden
                  22.08.2022 00:09
                  +1

                  Но предоставив пользователю какие-то политики безопасности и при этом их анально огородив от вмешательства — попахивает вахтёрством.

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

                  Эти анальные меры безопасности, не дают вам же отчекрыжить себе анус. Если же человек очень хочет отчекрыжить себе анус, на производстве существует ответственный за ТБ, задача которого — бить такого обезьяна палкой по голове.

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

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

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

                  Автоматические обновления также поставляют на территорию клиента Яндекс.Зонды
                  У меня ничего не поставляют. Возможно, потому что я потратил 5 минут времени и отключил автоматическую установку приложений. И опять ровно та же ситуация — это поддерживаемый сценарий, в отличие от отключения обновлений.

                  и уничтожают будку с Весёлым Роджером.
                  Это та древняя история с рукожопым активатором «семёрки»? Если да, то про борьбу с активатором — это миф. Реальная причина была гораздо проще и прозаичнее. Там чудовищный в своей костыльности активатор Odin делал копию ядра, патчил её и прописывал в загрузчик. Поскольку это не просто неподдерживаемый сценарий, а чудовищный костыль, то рано или поздно он обязан был сломаться. Что и произошло, когда случилось крупное обновление ядра и системного окружения, которое было рассчитано на работу с новым ядром. У юзеров получилась нерабочая конфигурация «старое ядро + новое окружение». Вместо того, чтобы бить себя по голове со словами «зачем же я, дебил, использовал такие костыли вместо нормального активатора» юзеры начали выдумывать мифы про какую-то борьбу с активаторами. Сами себе создали конфигурацию, которая производителем не поддерживается, а виноват у них кто-то другой.

                  Причем, эти бедолаги до сих пор встречаются в дикой природе, для них simplix-у пришлось даже добавлять обходные пути в UpdatePackR7. Он добрый, а, скажем, на MDL обладателей таких активаторов давно уже посылают куда подальше (сносить это говно и ставить нормальный активатор).


                  1. Komei
                    22.08.2022 00:34

                    Ну ок, техника безопасности на производстве — вахтёрство.

                    Совсем другая ситуация. Станок — собственность завода, а не ваша. Его повреждение проблема завода, а не ваша. Даже ваша травма проблема не только ваша, т.к. есть обязательства по выплате компенсаций + уголовное дело в отношении организовавших производство. А вот в случае с виндой ничего такого нет. Т.е. все шишки будут только пользователя.

                    В линуксе вам придётся очень сильно поплясать с бубном, чтобы всегда сидеть под рутом.

                    Одна строчка в конфиге. Вообще никаких проблем. Лично я работаю в CentOS и Fedora и ни разу не имел проблем с тем что мне нужен логин под root. Возможно в каких-то дистрибутивах это не так, за все говорить не возьмусь.


                  1. klounader
                    22.08.2022 01:14

                    Да нет. Я, к примеру, не хочу иметь автозапуск файлов при подключении внешних устройств и не хочу светить в сети административными ресурсами.
                    Хотя по умолчанию оно всё включено. То бишь, производитель позволяет разгильдяйствовать на рабочем месте и кому угодно тыкать в мой ничем не защищённый телнет — ужос.

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

                    не дают мне же отчекрыжить себе не защищённый телнет. бедняжки. у них там полна америка голых жоп нараспашку, пускай туда любуются.

                    даёт вам возможность установить уровень UAC на минимальный

                    толку тогда от него вообще? проще вообще вырубить этот внезапный кринж. он всё равно не работает.

                    В линуксе вам придётся очень сильно поплясать с бубном, чтобы всегда сидеть под рутом.

                    давненько я за линуксом не сидел, похоже.

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

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

                    Во-первых, это не правда

                    с топового компа этого и правда не видно.

                    (варварски, вырезая Защитник), лучше потратить время на изучение политик

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

                    У меня ничего не поставляют. Возможно, потому что я потратил 5 минут времени и отключил автоматическую установку приложений.

                    Яндекс.Музыка больше не прилетает сразу же после установки, да? Здорово. А может вместо неё что-нибудь другое прилетает, но уже не такое заметное, как жёлтая иконка на рабочем столе. Вы проверяли?

                    Это та древняя история с рукожопым активатором «семёрки»?

                    Нет, это история посвежее, про выпиливание uTorrent-ов:
                    habr.com/ru/company/itsumma/news/t/563028


    1. Sanctuary_s Автор
      19.08.2022 18:46

      В принципе комментарий ниже объясняет суть подхода: лишь ближе к концу разработки ушли баги. Или лучше позволишь пользователю разрешить запускать продукт, где есть уже исправленный баг? Патчи не прилетают автоматом, значит, остаётся пока запрещать запуск старых билдов. Да и в чем проблема скачать новый, если он лучше во всем?


      1. Mingun
        19.08.2022 19:28

        Кто сказал, что лучше? А может у вас там опять баг, который всё ломает ещё похлёще прежнего? Или я не хочу заморачиваться с обновлением вот прям сейчас, а просто вернуть эту долбанную настройку на место, потому что вчера был пьян и сдуру отключил очень нужную фичу :) А низзя!


  1. dartraiden
    19.08.2022 19:07
    +13

    Неудачное решение дизайнера — отделять полоской настройку от её вариантов. Полоска однозначно читается как разделитель между совершенно разными секциями.

    image


    1. Sanctuary_s Автор
      19.08.2022 19:11

      Как уж есть. В новой версии все учтем. :)


      1. Mike-M
        20.08.2022 15:41
        +1

        Хорошо бы еще выделять фокусом выбранный в данный момент элемент списка на средней панели, чтобы было понятно, к какому элементу относится описание справа.


        1. Sanctuary_s Автор
          20.08.2022 15:44

          Думаю, после перехода UI как в Windows 11 такое просто отпадет. Как показала жизнь, наведение курсора на элемент лишь усложняет код и добавляет ограничения.


    1. CoolCmd
      19.08.2022 20:22
      +4

      объяснение таких очевидных ошибок обычно простое: авторы не пользуются своим детищем.


      еще доставляет скриншот списока приложений, в котором имена такие короткие, что подряд идут XBOX + две буквы. мини игра "угадай приложение".


      К тому же дизайн программы не подразумевал стандартных элементов UI — все должно быть нестандартно, стильно и вызывать wow-эффект.

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


    1. HemulGM
      21.08.2022 08:27

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


  1. kiff
    19.08.2022 19:49
    +1

    Скачал, посмотрел. Добротно. Интерфейсы нынче на любой вкус и цвет. Тренды меняются быстрее чем заканчивается туалетная бумага в торговых центрах.

    Что касается дизайна, мне ближе по удобству и "красивости" такое исполнение как у total network inventory. Прям очень зашел их UI. Либо в последнее время очень неплох Microsoft To Do, наверно по разбиению основных экранов он бы и к вашему приложению зашел.

    И хотел узнать про обновления вашей программы, вы написали, что блокируете старую версию предлагая обновиться. Хороших подход, есть свои плюсы. Но, вы это обосновали тем, что некоторые, к примеру, службы более отключать нельзя. Но апдейты windows могут прийти раньше ваших. И может быть ситуация, когда служба была отключена, а новая версия Windows этого не потерпит. Я не знаток Windows, и не могу знать, можно ли как-то проверить совместимость перед обновлением.. Тема интересная конечно.

    Одно могу сказать точно - у вас хорошие мысли по поводу хороших программ. Удачи вам!


    1. Sanctuary_s Автор
      19.08.2022 19:52

      При запуске проверяет лишь наличие (не тип запуска) служб, относящихся к Defender: "Windefend", "SecurityHealthService", "wscsvc", так как их отсутствие будет означать, что Windows полностью убили. Сторонние антивирусы просто отключают их, соответственно, функции, относящиеся к работе будет просто выключены в UI (серые).

      А про другие службы я не понял. В любом случае все остальное не важно, что там делает Windows и как обновляется.


      1. kiff
        19.08.2022 20:27
        +1

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


        1. Sanctuary_s Автор
          19.08.2022 20:30

          https://habr.com/ru/post/683452/comments/#comment_24647528

          Исправленные баги в новых билдах. Отвечал уже. :) Да и в чем проблема выкачать 3,5 МБ опять, если есть новая версия? так будет, пока не будем уверены, что новых найденных багов не будет. Думаю, через пару билдов уже.


  1. Crimento
    19.08.2022 22:18
    +1

    Выглядит интересно, ознакомлюсь.

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

    А вот UI у вас явно поприятнее выглядит.


    1. Sanctuary_s Автор
      19.08.2022 22:28

      Видел. Запускал. Сразу в глаза бросается "Speed up your system ", что наводит на мысль о том, что я писал в начале статьи. " Disable Windows 10 automatic updates " вот это вообще победа: собственноручно позволить ломать пользователю ОС, сделав ее менее стабильной и безопасной. Но как писали выше, на каждого рыбака найдется своя рыбка. Пока пользователи сами перестанут игнорировать такие продукты, им будут пускать пыль в глаза такими поделками.


      1. Komei
        20.08.2022 04:34
        +7

        Вот только обновления сами по себе зачастую всё ломают. Моральное право на автоматическое обновление несмотря ни на что будет у вас (причём в широком смысле слова «вас», M$ это тоже касается) только тогда, когда не будет никаких «AS IS» и вы будете нести всю полноту ответственности за ЛЮБЫЕ возможные последствия обновлений. Пока это не так, решение о том что делать должен принимать тот, кто дефакто и несёт ответственность за все последствия — т.е. пользователь.

        Тотже Windows Defender по-умолчанию сам по себе ведёт себя как вирус (слава богу он теперь удаляется штатными средствами и именно удаляется, т.к. выключение оказалось не достаточно, он сам себя включал — ну я же говорю: вирус).

        У меня был с ним такой случай. Новая машина, винду только поставили. Захожу в сеть (SMB) чтобы открыть текстовый файл с описанием порядка настройки/установки моих программ. Через пару минут выскакивает сообщение дефендера о том, что он что-то удалил с УДАЛЁННОГО сервера. Мало того, я даже не заходил в те папки откуда он удалил. И то что он удалил, было как раз легитимной информацией, хранящайся на этом сервере (сервер кстати, не винда). Да, я понимаю суть его агра, удалил он Mimikatz. Вот только он ничем не угрожал винде. Он не хранился в её ФС, не запускался, ничего. Он просто существовал на сервере, на которые у пользователя винды был доступ.

        Из чего следует что Windows Defender это вредоносное ПО, которое используя авторизационные данные пользователя проникает на удалённые сервера и уничтожает (не српашивает или помещает в карантин, а сразу удаляет) хранящуюся там легитимную информацию.

        Я думаю теперь у вас не возникает вопроса почему эта дрянь удаляется со всем моих ПК?


        1. Sanctuary_s Автор
          20.08.2022 12:32
          -2

          То есть ты хранишь Mimikatz в открытую и удивляешься, что Defender ее удалил? У меня нет слов. У вас безопасности же нет, как я понял? И админы знают, что у них на серваке такая прога лежит?

          легитимную информацию

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

          И вообще добавлять в исключение эксплоиты — для слабаков. Надо же дождаться, чтобы работающий АВ удалил эксплоит, а потом гневно ругаться на то, что "Windows Defender это вредоносное ПО ". Я просто в ужасе сижу.

          Читатели, я один удивлен этому, или все считают, судя по заплюсованному комменатрию, что так и надо: на серваке хранить в открытую эксплоит, на который триггерится любой АВ, и он не должен на него реагировать никак, а если реагирует, то это плохой АВ?


          1. klounader
            20.08.2022 16:06
            +1

            Я считаю, что Komei прав. Потому что антивирусы сами выбрали себе стратегию вести себя как вирус, шарясь где не попадя и удаляя без спроса, а то и втихую пользовательские файлы, да ещё и на удалённых устройствах. Это уже ни в какие ворота, если честно. Причём чешут не столько откровенные вирусы, сколько кейгены и другие безобидные для системы файлы. Потом бегаешь ищешь, куда всё подевалось и от осознания такого бытия пытаешься вспомнить, сколько тормозухи вчера ты выпил. Неправильно это.
            Вот есть вирус. Лежит он на сервере. Это компетенция сервера. Увидел — уведоми пользователя. Предложи варианты, что с этим делать: Удалить навсегда, Отправить в карантин, Напомнить позже, Оставить и забыть… А политикой рубить с плеча и молча мести всё подряд — уж лучше как-нибудь без них.


            1. TimsTims
              21.08.2022 01:04

              У вас с Komei один очень похожий аргумент — антивирус сделал мне (админу) неудобно, поэтому я его не люблю. Причем вы даже не спорите с тем, что антивирус удалил что-то лишнее (похожее на вирус, но не вирус, а важный документ). То есть антивирус по сути сделал свою работу.

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

              Если аргумент в стиле «антивирусу туда было нельзя», то тут тоже имхо вы давно забыли зоны ответственности. Антивирусу везде можно. Всё что касается вашего компа, и вне вашего компа — везде где он может дотянуться. Современные антивирусы анализируют, на какие вы ходите сайты (тоже, казалось бы, какое их дело?), чтобы пытаться хоть иногда предупредить вас о фишинге. Они анализируют трафик (тоже, заметьте, с другими хостами), и блокируют нехорошие соединения (опять-же как повезет).
              А ещё антивирусы могут отправить (!) к себе домой ваши(!) личные файлы на анализ. Это может быть ваше домашнее видео, а может быть секретный файл…
              Где-то эта защита срабатывает, и пользователь действительно защищается от атаки. Но я лишь хотел вам показать суть — что антивирусы давно ходят далеко за пределы вашего жесткого диска и ОЗУ.


              1. Komei
                21.08.2022 02:17
                +4

                антивирус сделал мне (админу) неудобно

                Это называется не так. Антивирус безспроса уничтожил данные.

                такой вредный сервер с вируснёй, который заражает все сетевые папки компании

                Как это? Во-первых Mimikatz это не вирус и он ничего не заражает. Будучи вызванным пользователем он только дампит учётные данные из lsa.exe что-ли (имя файла подзабыл). Во-вторых, для того чтобы что-то там заражать вирус должен не хранится на сервере, а исполняться там. А как он может там исполняться, если является программой для винды, а на сервере не она?

                Если их не лечить сразу при обнаружении, то очень скоро он расползётся на все остальные компы.

                Вот только заниматься этим должен антивирус сервера или антивирус уровня сети.

                Не могу представить ситуацию, когда вам это надо.

                Потому что ситуация на самом то деле другая. Ничего не расползается, ничего не заражается, а сам «вирус» используется для тестирования настроек того, какие данные дефакто хранятся в памяти винды на некотором хосте С. Т.е. по факту у пользователя всё тип топ и ему ничего не угрожает.

                Антивирусу везде можно.

                Нет, только там, куда его допустили. У вас же получается что администратором моей сети являюсь не я, а фирма M$. И именно она определяет что и как должно быть в моей сети.

                А ещё антивирусы могут отправить (!) к себе домой ваши(!) личные файлы на анализ.

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

                Где-то эта защита срабатывает, и пользователь действительно защищается от атаки.

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

                И вот решение о том, что допустимо, а что нет — это прерогатива владельца информационной системы. А не производителей софта.

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

                В ситуации когда я принял осознанное решение что такие услуги защиты мне не нужны, попытка их навязывания и является тем, что описывает понятие «вредоносное ПО». Именно это навязывание заставляет думать, что истинная цель разработчиков таких антивирусов это получить мои конфиденциальные данные.

                P.S. В нормальных корпоративных решениях вы увидите скорее что-то вроде этого: наш продукт поддерживает отправку подозрительных файлов в наше облако, однако эта опция отключена по-умолчанию и системный интегратор не имеет право включать её не получив на то письменное согласие клиента. И вот в таком виде, меня совершенно устраивает эта возможность, т.к. она не выглядит как попытка воровства моих данных, а лишь как ещё одна возможность улучшения защиты со своими плюсами и минусами. И решения о её использовании я приму исходя именно из своей ситуации, да и ответственность за это решение буду нести тоже я.


                1. TimsTims
                  21.08.2022 14:48

                  Давайте посудим логически: если бы антивирусы и правда были зловредными, значит любой мог бы подать на них в суд, и неплохо так заработать.

                  Вопрос: почему вы так не делаете?

                  Hidden text

                  Я даже не буду говорить что вы и сами все прекрасно понимаете и знаете ответ, но все же.


                  1. Komei
                    22.08.2022 00:42

                    Давайте посудим логически

                    Давайте.

                    если бы антивирусы и правда были зловредными, значит любой мог бы подать на них в суд

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

                    Я почти на 100% уверен что где-то в дебрях лицензионного соглашения, размером с небольшую книгу, написано что всё это непотребство разрешено и т.п.

                    Вопрос: почему вы так не делаете?

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

                    К сожалению я не имею доступа к редактированию законов, особенно законов США и ЕС.


              1. klounader
                21.08.2022 22:34
                +1

                Первая и основная причина моего недовольства антивирусами в том, что они тупые как пробки и при этом считают себя самыми умными. Основная претензия к тому, что антивирус удаляет файлы и молчит как партизан, как будто ничего и не было, как будто так и надо. Вместо удаления можно их например расширения лишить или разрешений каких в зависимости от ситуации, утащить в карантин, но в тихую херить их, я считаю, это паскудством. И научиться, сука, разговаривать: «Я нашёл такой-то файл у тебя в папке БДСМ, он является кейгеном к серверу майкрософта и он временно обезврежен, что с ним сделать?», «Я тут на компе у твоего коллеги сверху нашёл какой-то подозрительный видеофайл и их тут прям много таких. Может стоит вызвать ему ГИБДД?». Всегда должна быть возможность оперировать своими подозрительными файлами, не имея навыков работы детектива, археолога и проктолога. А антивирус должен защищать в первую очередь внутренние владения от внешних угроз. Что там творится у соседа сверху я не знаю и знать не хочу. А также не хочу, чтобы мои вычислительные мощности без моего ведома тратились на лечение триппера безответственного соседа и всей остальной богодельни оператора связи.

                Если в сети затесался вредный сервер с активной вируснёй, то долбиться нужно в оперативную память сервера, раз уж на то пошло. А пока антивирусу только и остаётся, что работать в качестве мухобойки по всему серверу, пока на нём не кончится файловое хранилище.

                Опять же, должна же эвристика догонять в каком виде файл валяется (может он в архив запечатан) и антивирус весь этот архив грохнет. Ачотакова? Насколько он свежий и какую на самом деле представляет угрозу. Я думаю, что отличить кейген для фотошопа от сетевого червя они должны быть в состоянии. И в зависимости от потенциального вреда каждого из них уже выбирать, махать секирой сразу же или пока не горит.

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

                А ещё антивирусы могут отправить (!) к себе домой ваши(!) личные файлы на анализ. Это может быть ваше домашнее видео, а может быть секретный файл…

                И вызвать тащ майора с бутылкой швабры. Киберпанк, откуда не ждали.

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


          1. Komei
            21.08.2022 00:36
            +3

            То есть ты хранишь Mimikatz в открытую и удивляешься, что Defender ее удалил?

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

            Дальше. Давайте не минутку представим, что файл был прямо на компе с виндой. Удалять то зачем? Ведь не просто так во всех нормальных антивирусах есть карантин, правда? Потому что окончательное решение о том, что делать с информацией должен принимать её владелец, а не антивирус. Задача антивируса только защищать систему от урона. Пока вирус просто лежит в файле он не представляет опасности. А уж если он изолирован и его запуск невозможен, то и подавно. А ещё у антивирусов бывают и ложные срабатывания. Ещё раз: такое поведение это признак безнаказанности M$, а не какая-либо необходимость.

            Это не легитимная информация

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

            а отсутствие ИБ у вас в конторе

            Во, пошли уже ничем не подкреплённые выводы. С чего вы взяли, что дело было в конторе? Дело-то было у меня дома…

            У меня бы уже спустили аудит из Америки в офис за такое.

            А это называется «политика информационной безопасности». Если у вас она такая, что на сервере Х хранить такое нельзя, то туда ставится соотв. антивирус и он настраивается соотв. образом. Да и то, удаление без вопросов делать нельзя, т.к. бывают ложноположительные срабатывания. В том же солярисе ФС даже имеет специальные флаги для того, чтобы антивирусы могли помечать опасные файлы.

            И вообще добавлять в исключение эксплоиты — для слабаков.

            А у меня вообще было время для настроики системы? Почему не сделать действием по-умолчанию — карантин? Тогда у меня была бы возможность указать что это исключение.

            Надо же дождаться, чтобы работающий АВ удалил эксплоит, а потом гневно ругаться на то, что «Windows Defender это вредоносное ПО „

            Вы упорно игнорируете тот факт, что это самое ПО было чёрте где?

            и он не должен на него реагировать никак

            Вот тут вы просто лжёте. Я нигде не говорил что никак. Я говорил что:
            а) У антивируса есть зона ответственности и мы говорим про антивирус конечной точки (это такой, который отвечает за хост на котором он установлен)
            б) Удаление как действие по-умолчанию — недопустимо да и вообще не желательно, т.к. существует риск ошибки.


      1. Crimento
        21.08.2022 00:29
        +1

        Ну так это инструмент, если бездумно пользоваться - можно все сломать. Я ж не призываю всех им пользоваться, но для специфичных случаев бывает нужно отключить дефендер. На вин11 и свежих билдах вин10, например, он перезаписывает даже локальные политики, что в принципе уже очень интересный прецедент.


        1. Sanctuary_s Автор
          21.08.2022 00:31

          Пруфы?


          1. Crimento
            21.08.2022 11:25

            1. Sanctuary_s Автор
              21.08.2022 12:52

              В ответе вообще написано, что это для сервака и by design.


              1. Komei
                22.08.2022 00:50

                А весь прикол заключается в том, что до этого эта политика прекрасно работала весь Win10 для любых систем. И зачем же её было менять то, а? Не новую добавили, а именно поведение старой изменили? Неужели не наводит на определённые размышления.


  1. Eddy71
    20.08.2022 10:20
    +1

    Насладиться испытанием этой замечательной программы не удалось - отругала старой версией винды. 19044 ей не понравилась, ей надо 1904х..


    1. Sanctuary_s Автор
      20.08.2022 10:31

      Минорная версия устрале, значит. Не писать же 19041, 19042, 19043, 19044, 19045 (и так до 48). Потом последняя цифра заменяется на x.


      1. gvg1
        20.08.2022 13:02

        Присмотритесь к тому, что написал Eddy71


        1. Sanctuary_s Автор
          20.08.2022 13:03

          Да, в 81 билде поменяется проверка. Станет все проще и понятнее, и многие ограничения уйдут. :) Наверное, просто оставлю 19044.1706 и выше, чтобы не вводить пользователей в заблуждение.


          1. Timofeuz
            21.08.2022 14:00

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


            1. Sanctuary_s Автор
              21.08.2022 14:45

              Будет уточнено, установив порога 19044.1706. :)


  1. roboter
    20.08.2022 14:27

    Экран UWP приложений, вообще не понятно что происходит, какие изменения сейчас произойдут?.

    При применении изменений, весь UI блокируется.

    MSI installer первое что я ищу, распространять софт в ZIP было модно в 2000x.

    Feedback frequency - Заголовок секции сливается с предыдущим пунктом.

    При развёртывании окна на другом мониторе перепрыгивает на основной.


    1. Sanctuary_s Автор
      20.08.2022 14:29

      MSI installer первое что я ищу, распространять софт в ZIP б

      Весь GitHub так не думает. Как и весь интернет. Но блокировку уберем в 81.


  1. Mike-M
    20.08.2022 15:52
    +1

    Интересная история получилась. Спасибо, прочитал с удовольствием.
    Ошибки правописания отправил в личку.

    Есть одно замечание-вопрос по функционалу:

    Создать задание «SoftwareDistribution» по очистке папок %SystemRoot% \SoftwareDistribution\Download
    Надеюсь, пользователь каким-то образом уведомляется, что после очистки папок он не сможет посмотреть историю обновлений Windows?


    1. Sanctuary_s Автор
      20.08.2022 15:53
      +1

      Очищаются лишь установочные файлы. История и прочие файлы не трогаются!

      У меня у самого вот так: https://i.imgur.com/YB3MLEb.png


    1. Sanctuary_s Автор
      20.08.2022 15:55

      Удаляются лишь файлы

      Содержимое этой папки не трогается никогда


  1. mixsture
    20.08.2022 15:57
    +3

    а ОС никогда не обновлять, тем больше ошибок возникает в работе нашей программы.

    С учетом этих вводных, волевым усилием было принято решение где-то раз в полгода повышать требование к минорной версии билда Windows. На текущий момент это 1904x.1766+ для Windows 10 и 22000.739+, 22509+ для Windows 11 и Windows 11 Insider Preview соответственно.


    Кажется, в мире софта что-то пошло не так…
    Поправьте меня, если я неправ, но «1904x.1766+» на понятном человеческом языке звучит как «примерно не старше 3х месяцев». И эти требования тащатся за программой таким 3х-месячным окном. Добавим сюда принуждение обновлять программу и получаем неявное принуждение обновлять ОС чаще, чем раз в 3 месяца. БОльшие интервалы обновления ОС выдадут неработоспособность программы, даже купленной.
    Думаю, причина этого в архитектуре. Сейчас она — некоторый монолит под актуальную версию ОС. А можно ее поделить на ядро, модули настроек и апи между ними. Модули и отвечают за конкретные настройки ОС, причем они еще и зависят от номеров обновлений (каждая версия модуля поддерживает такой-то интервал обновлений).
    Тогда появятся такие фичи:
    • под любую версию ОС можно подобрать и выкачать модули с вашего сервера и появится поддержка старых версий (ибо сейчас по сути вы выкидываете в мусорку рабочий на прошлых версиях код).
    • модули становятся слабозависимыми и их можно параллельно разрабатывать, а еще можно принимать фиксы или новые модули от сообщества


    1. Sanctuary_s Автор
      20.08.2022 16:01

      Поправьте меня, если я неправ, но «1904x.1766+» на понятном человеческом языке звучит как «примерно не старше 3х месяцев».

      Есть такое. :) Чтобы не перечислять 41, 42, 43, 44, 45 и т. д. В 81 билде будет просто 19044.1766 и выше.

      Тогда появятся такие фичи:

      Обдумаем...


  1. BigDflz
    20.08.2022 19:14
    -1

    не вздумайте эту x.... использовать. Эти черти не всё проверили. при двух мониторах - один гасится и хер знает как его включить.


    1. klounader
      20.08.2022 21:19

      Бэкапы придумали трусы? Штош, с 31 марта вас. :))


      1. Sanctuary_s Автор
        20.08.2022 21:21

        Да ему там явно плохо и очень давно.


        1. klounader
          20.08.2022 22:20

          В программе есть опция «Отменить последние/все изменения»? Нет? Не порядок. Делайте. :))
          Нельзя исключать вариант, что изменения, применённые с помощью программы, действительно могли нарушить работу его мониторной конфигурации. Тут уж надо разбираться. Так то может у него просто физически монитор сдох или кабель отвалился. Вот бывают такие совпадения и ничего с этим не поделаешь. Ну или правда прога чото поломала.
          А на эмоциях чего не побурчать то и не обвинить первого попавшегося под руку, мы же не роботы. Авось расскажет потом, в чём на самом деле была проблема.


          1. Sanctuary_s Автор
            20.08.2022 22:22

            Советуешь записывать совершенные изменения только в текущей сессии же?

            Как оно могло нарушить работу, если настраивается проводник по сути и безопасность? ;)


            1. klounader
              20.08.2022 23:21
              +1

              Почему бы и да. Да даже, наверное, можно все сессии записывать. А по ним потом вертать настройки взад, например, по типу как это сделано у мракософта с точками восстановления. Да и не только у мракософта.
              Ace Utilities, к примеру, тоже каждые применённые изменения сохраняет и позволяет откатиться к любой точке. Причём, как через виндовую реализацию, так и собственными файлами бэкапит. Это важно, т.к. виндовый бэкапер может не работать по разным причинам, по которым и тому злополучному дефендеру досталось, так и этому монитору у товарища выше. :)
              При первом же запуске не мешает записать текущую конфигурацию компьютера под имеющийся функционал программы, чтобы можно было нажать кнопочку «Раньше было лучше», попутно предложив создать точку восстановления шиндовс. Не думаю, что это архисложная задача. Но мне кажется, что всякого рода твикеры просто обязаны уметь работать на откат собственных изменений так, чтобы это было понятно, а не вспоминать «как там чего было включено или выключено и как это всё теперь починить аааааааааа format c:».

              Мракософту же удаётся как-то ломать принтеры и всё такое своими обновлениями.
              У линуксоидов там так и вообще трэш, угар и содомия творится в этом плане.
              Никогда не знаешь, что и откуда может выстрелить. Они могут. Все остальные могут. Почему вы не можете? Можете. Просто, видимо, ещё не сталкивались с подобным инцидентом. Ну или вот оно у вас, возможно, первое. :))


    1. flickster
      20.08.2022 21:23

      А ваши черти всякий софт заставляют без оглядки тыкать?)


      1. Sanctuary_s Автор
        20.08.2022 21:23

        Да не причем тут это: тупо бочку катит потому, что может. У программы даже такого функционала нет.


        1. flickster
          20.08.2022 21:27

          Точнее потому что не может ничего другого)


  1. flickster
    21.08.2022 08:41

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