diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5203e6..d0a2f6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,10 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: 8.0.x diff --git a/README.md b/README.md index 307bcef..3720e20 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,10 @@ | 12 | **Принтер по умолчанию** | Default printer | Сброс реестра + `SetDefaultPrinter` | ### Плюс: -- 🌐 **Удалённые машины** — применяй фиксеры через WinRM/PowerShell Remoting +- 🌐 **Удалённые машины** — системные шаги через WinRM, а пользовательские настройки принтера — в реальном интерактивном сеансе - 🔍 **Обнаружение принтеров** — CIM / WMI / Get-Printer (тройной fallback) - 📋 **Полный лог** — каждый шаг фиксера отображается в реальном времени +- ↩️ **Точечный откат** — снимок изменяемых ключей реестра и кнопка восстановления после фикса - 🗂 **Active Directory** — обзор принтеров в домене --- @@ -99,6 +100,8 @@ W-Fix/ │ │ ├── Services/ │ │ │ ├── WmiService.cs # Обнаружение принтеров (CIM/WMI/PS) │ │ │ ├── PowerShellEngine.cs # Встроенный PS SDK + внешний fallback +│ │ │ ├── InteractiveUserPowerShellService.cs # Remote-команды в сеансе пользователя +│ │ │ ├── SystemStateBackupService.cs # Снимок реестра + restore.ps1 │ │ │ └── FixerRegistry.cs # Регистрация и поиск фиксеров │ │ └── Models/ # PrinterInfo, FixResult, LogEntry ... │ └── W-Fix.App/ # WPF UI (MVVM + CommunityToolkit) diff --git a/docs/architecture.md b/docs/architecture.md index faaa7f2..e02f1cf 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -30,11 +30,49 @@ UI не должен самостоятельно определять успе - Не сообщать `Success`, если обязательный шаг завершился неуспешно. - Не скрывать ошибку обязательного шага за успешным перезапуском Spooler. - Ограничивать удаление файлов только ресурсами подсистемы печати. -- Для потенциально опасных изменений реестра сохранять исходное состояние перед будущей реализацией rollback. +- Каждый фиксер, изменяющий реестр, реализует `ISystemStateChangingFixer` и точно объявляет затрагиваемые + значения, разделы и ACL. Новое изменение реестра без дополнения плана снимка считается дефектом. - Любой новый сценарий должен иметь тестируемую логику определения результата. +## Снимок и откат системного состояния + +Перед запуском изменяющего реестр фиксера `SystemStateBackupService` сохраняет только затрагиваемое состояние, +а не экспортирует реестр целиком. Снимок содержит `state.clixml`, `manifest.json`, при необходимости `.reg`-файлы +и автономный `restore.ps1`. Каталог создаётся на целевой машине в +`%ProgramData%\W-Fix\Backups\<время>_<фиксер>_`. + +Ошибка снимка выводится как заметное предупреждение, но не превращает W-Fix в режим «только диагностики»: +пользовательский ремонт продолжается. После успешного снимка команда **«Откат»** восстанавливает значения, +удаляет значения, которых до исправления не существовало, возвращает ACL и импортирует сохранённые разделы. +После восстановления перезапускается Print Spooler. + +UI сохраняет выбранные фиксер, принтер и компьютер в начале операции. Поэтому смена выбора во время исполнения +не может направить часть одного сценария на другую цель. Повторные загрузки принтеров, очередей и компьютеров +отменяют устаревшие запросы, чтобы поздний ответ не перезаписывал более актуальное состояние. + +## Удалённый пользовательский контекст + +PowerShell Remoting выполняет команды в сервисном контексте учётной записи администратора. Поэтому обычный +`HKCU:` и вызовы `WScript.Network` из WinRM не относятся к рабочему столу пользователя, вошедшего на целевой ПК. +Фиксеры принтера по умолчанию используют отдельный `InteractiveUserPowerShellService`: + +1. Через WinRM определяется интерактивный пользователь и его SID. +2. Администратор сохраняет HKCU этого пользователя через `Registry::HKEY_USERS\`; restore-скрипт хранит + уже разрешённый путь, поэтому откат возвращается в тот же профиль. +3. На целевом ПК создаётся временная задача `W-Fix-Interactive-` с типом входа `Interactive` и уровнем + `Limited`. Пароль пользователя не запрашивается и не сохраняется; используется существующий токен сеанса. +4. Пользовательская часть выполняет `WScript.Network`/PrintUI, пишет UTF-8 журнал и подтверждает фактически + назначенный принтер. Код задачи и маркеры `[ERROR]` проверяются независимо. +5. Административные действия — исправление ACL и перезапуск Spooler — остаются в WinRM-контексте. +6. Временная задача и файлы удаляются в `finally`; при отмене выполняется дополнительная best-effort очистка. + `ExecutionTimeLimit` ограничивает процесс даже при обрыве управляющего соединения. + +Удалённый пользовательский фикс требует включённых WinRM и Task Scheduler, административного доступа и +уже вошедшего пользователя. Если интерактивного сеанса нет, операция завершается явной ошибкой — W-Fix не +сообщает ложный успех для HKCU администратора. + ## Следующий архитектурный этап Вынести создание сервисов из `MainWindowViewModel` в composition root и ввести интерфейсы исполнителя, обнаружения принтеров и диалогов. Это позволит тестировать фиксеры без реального изменения Windows и добавить -режим предварительного просмотра/отката системных настроек. +интеграционные проверки снимка/отката на изолированной тестовой машине. diff --git a/src/W-Fix.App/MainWindow.xaml b/src/W-Fix.App/MainWindow.xaml index 39c3b3a..11325f4 100644 --- a/src/W-Fix.App/MainWindow.xaml +++ b/src/W-Fix.App/MainWindow.xaml @@ -54,6 +54,9 @@