commit 89c48131b258c68bc2c1e834f0afb564a7fac8b5 Author: leefer Date: Sun Aug 2 18:08:19 2026 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd2a271 --- /dev/null +++ b/.gitignore @@ -0,0 +1,494 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp + +# Repository-local SDK and release artifacts +.tools/ +artifacts/ + +# The macOS *.app rule above also matches this WPF project on Windows. +!src/WindowsToolbox.App/ +!src/WindowsToolbox.App/*.cs +!src/WindowsToolbox.App/*.xaml +!src/WindowsToolbox.App/*.csproj diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..96140df --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,10 @@ + + + latest + enable + enable + false + true + $(MSBuildThisFileDirectory)assets\WindowsToolbox.ico + + diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..a3f3aea --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0e9071 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# Windows 工具箱 + +面向 Windows 10/11 的中文 WPF 系统工具。主程序以普通权限运行;修改系统设置时才启动独立管理员执行器。所有可恢复功能会在第一次修改前保存基线快照,重复执行不会覆盖原始状态。 + +## 已实现功能 + +- Windows 11 经典右键菜单:启用、恢复;修改后自动重启 Explorer,在 Build 26200 上立即生效。 +- 快捷方式箭头:隐藏、恢复,并生成本地透明图标资源。 +- Windows 更新:暂停 1—35 天、每日自动续期、强力关闭、每日关闭守护、恢复原始状态。 +- Windows 安全:检测防篡改保护、关闭可控制的 Defender 防护、可选关闭防火墙、恢复原始设置。 +- 自动登录:恢复 `netplwiz` 中可能隐藏的密码复选框并打开 Windows 自带账户控制;工具不读取密码。 +- 桌面归档:使用 Known Folder API 识别真实桌面,按 `yyyy-MM-dd` 归档普通文件和普通文件夹;排除快捷方式、系统项及重解析目录,自动处理同名文件/文件夹,支持撤销和每月计划任务。 +- KMS:内置微软公开的 Windows 10/11 GVLK,使用用户填写的本地 KMS 地址,连接成功后才安装密钥,支持恢复原 KMS 主机。 +- 系统状态、逐步执行结果及最近 100 次操作历史。 +- 多尺寸应用图标,同时用于窗口标题栏、任务栏和 EXE 文件。 + +启动时只运行一个有 5 秒硬超时的可选状态查询进程,主窗口不会等待它才显示;普通功能完成后只刷新注册表基础状态,不再重复等待 Defender 与许可证查询。 + +## 运行 + +开发构建输出位于: + +```text +src\WindowsToolbox.App\bin\Debug\net8.0-windows\WindowsToolbox.App.exe +``` + +发布目录生成后运行: + +```text +artifacts\WindowsToolbox\WindowsToolbox.App.exe +``` + +运行环境需要 Windows 10/11 x64 和 .NET 8 Desktop Runtime。涉及系统修改的按钮会出现 UAC 提示。 + +## 构建 + +仓库已经配置本地 .NET 8 SDK 位置。执行: + +```powershell +.\scripts\build-release.ps1 +``` + +生成便携发布目录: + +```text +artifacts\WindowsToolbox\ +``` + +项目没有第三方 NuGet 运行依赖。 + +## 数据位置 + +```text +%LOCALAPPDATA%\WindowsToolbox\ +├─ ArchiveManifests\ 桌面归档恢复清单 +├─ Logs\ 操作历史 +├─ Pending\ 临时提权命令及结果 +└─ Profiles\ 桌面归档配置 + +%PROGRAMDATA%\WindowsToolbox\ +├─ Assets\ 透明快捷方式图标 +└─ Snapshots\ 系统设置基线快照 +``` + +## 重要边界 + +- “强力关闭更新”会关闭可控制的策略、服务和任务,并每日重新检查;Windows 就地修复或重大升级后仍可能恢复受保护组件。 +- 工具不会绕过 Defender 防篡改保护。关闭 Defender 前必须由用户在 Windows 安全中心手动关闭防篡改保护。 +- 家庭版不支持 KMS 客户端激活。Windows 客户端的正规 KMS 服务还需要满足微软规定的客户端数量门槛。 +- KMS 恢复功能可以恢复主机和端口,但 Windows 不提供旧完整产品密钥,无法自动恢复未知旧密钥。 +- 跨磁盘文件移动不是文件系统原子操作;移动失败会保留在恢复清单和操作日志中。 + +更多内部设计见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) 和 [docs/OPERATIONS.md](docs/OPERATIONS.md)。 diff --git a/WindowsToolbox.sln b/WindowsToolbox.sln new file mode 100644 index 0000000..f83aed0 --- /dev/null +++ b/WindowsToolbox.sln @@ -0,0 +1,64 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E54A5670-8E0E-463E-8FBF-430054BF2736}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.Core", "src\WindowsToolbox.Core\WindowsToolbox.Core.csproj", "{404532FF-0F8C-4A80-A182-3FEBAB789DC3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.Infrastructure", "src\WindowsToolbox.Infrastructure\WindowsToolbox.Infrastructure.csproj", "{32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.App", "src\WindowsToolbox.App\WindowsToolbox.App.csproj", "{44AEEFE7-C6CA-482D-BDDF-54F5CF570295}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.ElevatedHost", "src\WindowsToolbox.ElevatedHost\WindowsToolbox.ElevatedHost.csproj", "{61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.Cli", "src\WindowsToolbox.Cli\WindowsToolbox.Cli.csproj", "{BC9B81D5-0862-4465-9357-738BB0B2E4E0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A98D294C-F010-49CE-968F-A85E2F921692}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsToolbox.Verification", "tests\WindowsToolbox.Verification\WindowsToolbox.Verification.csproj", "{8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {404532FF-0F8C-4A80-A182-3FEBAB789DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {404532FF-0F8C-4A80-A182-3FEBAB789DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {404532FF-0F8C-4A80-A182-3FEBAB789DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {404532FF-0F8C-4A80-A182-3FEBAB789DC3}.Release|Any CPU.Build.0 = Release|Any CPU + {32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4}.Release|Any CPU.Build.0 = Release|Any CPU + {44AEEFE7-C6CA-482D-BDDF-54F5CF570295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44AEEFE7-C6CA-482D-BDDF-54F5CF570295}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44AEEFE7-C6CA-482D-BDDF-54F5CF570295}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44AEEFE7-C6CA-482D-BDDF-54F5CF570295}.Release|Any CPU.Build.0 = Release|Any CPU + {61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22}.Release|Any CPU.Build.0 = Release|Any CPU + {BC9B81D5-0862-4465-9357-738BB0B2E4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC9B81D5-0862-4465-9357-738BB0B2E4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC9B81D5-0862-4465-9357-738BB0B2E4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC9B81D5-0862-4465-9357-738BB0B2E4E0}.Release|Any CPU.Build.0 = Release|Any CPU + {8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {404532FF-0F8C-4A80-A182-3FEBAB789DC3} = {E54A5670-8E0E-463E-8FBF-430054BF2736} + {32D8C232-E015-4DC5-8DF6-D9B6C70D2BC4} = {E54A5670-8E0E-463E-8FBF-430054BF2736} + {44AEEFE7-C6CA-482D-BDDF-54F5CF570295} = {E54A5670-8E0E-463E-8FBF-430054BF2736} + {61D6B7B9-FB16-4D3C-B0FA-1EE6B5124E22} = {E54A5670-8E0E-463E-8FBF-430054BF2736} + {BC9B81D5-0862-4465-9357-738BB0B2E4E0} = {E54A5670-8E0E-463E-8FBF-430054BF2736} + {8CCFF617-CDF1-4EFD-812E-AE92CD75F8CA} = {A98D294C-F010-49CE-968F-A85E2F921692} + EndGlobalSection +EndGlobal diff --git a/assets/WindowsToolbox.ico b/assets/WindowsToolbox.ico new file mode 100644 index 0000000..f668d81 Binary files /dev/null and b/assets/WindowsToolbox.ico differ diff --git a/assets/WindowsToolbox.png b/assets/WindowsToolbox.png new file mode 100644 index 0000000..84fa931 Binary files /dev/null and b/assets/WindowsToolbox.png differ diff --git a/assets/WindowsToolbox.svg b/assets/WindowsToolbox.svg new file mode 100644 index 0000000..a540d7a --- /dev/null +++ b/assets/WindowsToolbox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/data/kms-keys.json b/data/kms-keys.json new file mode 100644 index 0000000..98ea972 --- /dev/null +++ b/data/kms-keys.json @@ -0,0 +1,93 @@ +[ + { + "editionId": "Professional", + "displayName": "Windows 10/11 专业版", + "productKey": "W269N-WFGWX-YVC9B-4J6C9-T83GX", + "appliesTo": "Windows 10/11 Pro" + }, + { + "editionId": "ProfessionalN", + "displayName": "Windows 10/11 专业版 N", + "productKey": "MH37W-N47XK-V7XM9-C7227-GCQG9", + "appliesTo": "Windows 10/11 Pro N" + }, + { + "editionId": "ProfessionalWorkstation", + "displayName": "Windows 10/11 专业工作站版", + "productKey": "NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J", + "appliesTo": "Windows 10/11 Pro for Workstations" + }, + { + "editionId": "ProfessionalWorkstationN", + "displayName": "Windows 10/11 专业工作站版 N", + "productKey": "9FNHH-K3HBT-3W4TD-6383H-6XYWF", + "appliesTo": "Windows 10/11 Pro for Workstations N" + }, + { + "editionId": "ProfessionalEducation", + "displayName": "Windows 10/11 专业教育版", + "productKey": "6TP4R-GNPTD-KYYHQ-7B7DP-J447Y", + "appliesTo": "Windows 10/11 Pro Education" + }, + { + "editionId": "ProfessionalEducationN", + "displayName": "Windows 10/11 专业教育版 N", + "productKey": "YVWGF-BXNMC-HTQYQ-CPQ99-66QFC", + "appliesTo": "Windows 10/11 Pro Education N" + }, + { + "editionId": "Education", + "displayName": "Windows 10/11 教育版", + "productKey": "NW6C2-QMPVW-D7KKK-3GKT6-VCFB2", + "appliesTo": "Windows 10/11 Education" + }, + { + "editionId": "EducationN", + "displayName": "Windows 10/11 教育版 N", + "productKey": "2WH4N-8QGBV-H22JP-CT43Q-MDWWJ", + "appliesTo": "Windows 10/11 Education N" + }, + { + "editionId": "Enterprise", + "displayName": "Windows 10/11 企业版", + "productKey": "NPPR9-FWDCX-D2C8J-H872K-2YT43", + "appliesTo": "Windows 10/11 Enterprise" + }, + { + "editionId": "EnterpriseN", + "displayName": "Windows 10/11 企业版 N", + "productKey": "DPH2V-TTNVB-4X9Q3-TJR4H-KHJW4", + "appliesTo": "Windows 10/11 Enterprise N" + }, + { + "editionId": "EnterpriseG", + "displayName": "Windows 10/11 企业版 G", + "productKey": "YYVX9-NTFWV-6MDM3-9PT4T-4M68B", + "appliesTo": "Windows 10/11 Enterprise G" + }, + { + "editionId": "EnterpriseGN", + "displayName": "Windows 10/11 企业版 G N", + "productKey": "44RPN-FTY23-9VTTB-MP9BX-T84FV", + "appliesTo": "Windows 10/11 Enterprise G N" + }, + { + "editionId": "EnterpriseS", + "displayName": "Windows 10/11 企业版 LTSC", + "productKey": "M7XTQ-FN8P6-TTKYV-9D4CC-J462D", + "appliesTo": "Enterprise LTSC 2019/2021/2024" + }, + { + "editionId": "EnterpriseSN", + "displayName": "Windows 10/11 企业版 LTSC N", + "productKey": "92NFX-8DJQP-P6BBQ-THF9C-7CG2H", + "appliesTo": "Enterprise LTSC N 2019/2021/2024" + }, + { + "editionId": "Core", + "displayName": "Windows 家庭版(不支持 KMS 客户端激活)", + "productKey": "", + "appliesTo": "Windows 10/11 Home", + "supported": false + } +] diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..37fcf4c --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,53 @@ +# 架构说明 + +## 项目组成 + +```text +WindowsToolbox.App + WPF 界面、MVVM、UAC 客户端 + +WindowsToolbox.ElevatedHost + 固定命令白名单、管理员权限系统修改 + +WindowsToolbox.Cli + 当前用户桌面归档计划任务入口 + +WindowsToolbox.Infrastructure + 注册表、进程、任务计划、快照、各功能实现 + +WindowsToolbox.Core + 命令、状态、结果、快照和配置模型 + +WindowsToolbox.Verification + 不修改系统的自动验证程序 +``` + +## 执行流程 + +```text +用户点击功能 + → 主程序生成强类型 ToolboxCommand + → 普通文件操作直接执行 + → 系统操作写入 Pending 目录并请求 UAC + → ElevatedHost 验证路径、时效和命令类型 + → 首次操作保存基线快照 + → 执行固定实现 + → 读取实际状态进行验证 + → 写入结果和操作历史 + → 主程序刷新系统状态 +``` + +管理员执行器只接受枚举中的命令以及经过各模块验证的参数,不提供任意 CMD、PowerShell 或脚本执行入口。 + +## 扩展新功能 + +新增功能时应按以下顺序: + +1. 在 `ToolboxCommandKind` 中添加固定命令。 +2. 在基础设施项目中实现模块服务。 +3. 确定原始状态的完整快照范围。 +4. 在 `ToolboxCommandDispatcher` 注册执行分支和提权要求。 +5. 在 WPF ViewModel 暴露命令,在页面添加状态、风险说明、应用和恢复入口。 +6. 向 Verification 项目增加非破坏性验证。 + +每个修改操作必须满足幂等、可验证和尽可能可恢复;不能只根据进程退出码显示成功。 diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md new file mode 100644 index 0000000..86c296d --- /dev/null +++ b/docs/OPERATIONS.md @@ -0,0 +1,46 @@ +# 系统操作边界 + +## 右键菜单与快捷方式箭头 + +经典菜单使用当前用户 Shell 类注册项,并在修改后由普通权限主程序自动重启 Explorer。检测到 Atlas OS 时,同时维护 Atlas Toolbox 使用的状态镜像,实际状态仍以 Explorer 读取的 CLSID 项为准。快捷方式箭头使用系统 `Shell Icons` 覆盖值和工具生成的透明 ICO。 + +## Windows 更新 + +强力关闭模式管理: + +- Windows Update 自动更新策略。 +- `wuauserv`、`UsoSvc` 和 `WaaSMedicSvc` 的可控制启动状态。 +- Update Orchestrator 与 Windows Update 中存在且允许修改的计划任务。 +- `WindowsToolbox\UpdateGuard` 每日守护任务。 + +部分服务和任务在不同 Windows 构建中受系统保护。模块会保留每一步结果,不把拒绝访问伪装成成功。 + +暂停模式写入开始和结束时间,最长限制为 35 天。自动续期任务每天重新请求所选暂停长度并再次验证。 + +## Windows 安全 + +模块使用 Defender 和防火墙的 Windows PowerShell Cmdlet。它不会停止 Security Center 核心服务,不会修改系统文件所有权,也不会绕过防篡改保护。恢复时按基线快照逐项恢复 Defender 偏好、三个防火墙配置和通知策略。 + +## 桌面归档 + +枚举桌面根目录中的文件和文件夹。默认排除: + +- `desktop.ini` +- `.lnk` 和 `.url` +- 带 System 属性的文件或文件夹 +- 重解析点、目录联接及其他可能指向桌面外部的特殊目录 + +普通隐藏文件会归档。虚拟的“此电脑”“回收站”等系统图标不属于文件系统枚举范围,因此不会移动。同名目标采用 `名称 (1).扩展名` 或 `文件夹 (1)` 递增。每次执行都生成恢复清单;撤销只移动仍然存在于归档目录中的项目。 + +## KMS + +密钥目录位于 `data\kms-keys.json`,可以手动维护。激活顺序为: + +1. 验证版本、密钥格式、主机和端口。 +2. TCP 测试本地 KMS 主机。 +3. 保存原 KMS 主机和端口。 +4. 安装所选 GVLK。 +5. 设置 KMS 主机。 +6. 请求 Windows 激活。 + +如果连接测试失败,不修改产品密钥。 diff --git a/global.json b/global.json new file mode 100644 index 0000000..4a8c056 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.423", + "rollForward": "latestPatch" + } +} diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 new file mode 100644 index 0000000..c8e5001 --- /dev/null +++ b/scripts/build-release.ps1 @@ -0,0 +1,51 @@ +param( + [ValidateSet('Debug', 'Release')] + [string]$Configuration = 'Release', + [switch]$SkipRestore +) + +$ErrorActionPreference = 'Stop' +$projectRoot = Split-Path -Parent $PSScriptRoot +$dotnetExe = Join-Path $projectRoot '.tools\dotnet\dotnet.exe' +$nugetConfig = Join-Path $projectRoot 'NuGet.Config' +$outputRoot = Join-Path $projectRoot 'artifacts\WindowsToolbox' + +if (-not (Test-Path -LiteralPath $dotnetExe)) { + throw 'Local .NET SDK was not found under .tools\dotnet.' +} + +$env:DOTNET_CLI_HOME = Join-Path $projectRoot '.tools\dotnet-home' +$env:NUGET_PACKAGES = Join-Path $projectRoot '.tools\nuget' +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' +$env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' + +New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null + +if (-not $SkipRestore) { + & $dotnetExe restore (Join-Path $projectRoot 'WindowsToolbox.sln') --configfile $nugetConfig + if ($LASTEXITCODE -ne 0) { throw 'Restore failed.' } +} + +$commonArguments = @( + '--configuration', $Configuration, + '--framework', 'net8.0-windows', + '--self-contained', 'false', + '--no-restore', + '--output', $outputRoot, + '-p:DebugType=None', + '-p:DebugSymbols=false' +) + +& $dotnetExe publish (Join-Path $projectRoot 'src\WindowsToolbox.ElevatedHost\WindowsToolbox.ElevatedHost.csproj') @commonArguments +if ($LASTEXITCODE -ne 0) { throw 'Elevated host publish failed.' } + +& $dotnetExe publish (Join-Path $projectRoot 'src\WindowsToolbox.Cli\WindowsToolbox.Cli.csproj') @commonArguments +if ($LASTEXITCODE -ne 0) { throw 'CLI publish failed.' } + +& $dotnetExe publish (Join-Path $projectRoot 'src\WindowsToolbox.App\WindowsToolbox.App.csproj') @commonArguments +if ($LASTEXITCODE -ne 0) { throw 'WPF app publish failed.' } + +Copy-Item -LiteralPath (Join-Path $projectRoot 'README.md') -Destination (Join-Path $outputRoot 'README.md') -Force + +Write-Host "Published to: $outputRoot" -ForegroundColor Green +Write-Host 'Run WindowsToolbox.App.exe to start.' -ForegroundColor Green diff --git a/scripts/generate-icon.py b/scripts/generate-icon.py new file mode 100644 index 0000000..b34216e --- /dev/null +++ b/scripts/generate-icon.py @@ -0,0 +1,62 @@ +from pathlib import Path + +from PIL import Image, ImageDraw + + +ROOT = Path(__file__).resolve().parents[1] +ASSETS = ROOT / "assets" +SCALE = 4 +SIZE = 256 +CANVAS = SIZE * SCALE + + +def scaled_points(points): + return [(x * SCALE, y * SCALE) for x, y in points] + + +mask = Image.new("L", (CANVAS, CANVAS), 0) +mask_draw = ImageDraw.Draw(mask) +mask_draw.rounded_rectangle( + (8 * SCALE, 8 * SCALE, 248 * SCALE, 248 * SCALE), + radius=54 * SCALE, + fill=255, +) + +background = Image.new("RGBA", (CANVAS, CANVAS), (0, 0, 0, 0)) +pixels = background.load() +for y in range(CANVAS): + ratio = y / max(1, CANVAS - 1) + red = round(59 + (29 - 59) * ratio) + green = round(130 + (78 - 130) * ratio) + blue = round(246 + (216 - 246) * ratio) + for x in range(CANVAS): + pixels[x, y] = (red, green, blue, mask.getpixel((x, y))) + +draw = ImageDraw.Draw(background, "RGBA") +highlight = Image.new("RGBA", (CANVAS, CANVAS), (0, 0, 0, 0)) +highlight_draw = ImageDraw.Draw(highlight, "RGBA") +highlight_draw.ellipse( + (-60 * SCALE, -110 * SCALE, 250 * SCALE, 150 * SCALE), + fill=(255, 255, 255, 22), +) +background.alpha_composite(highlight) + +w_shape = [ + (44, 66), (78, 66), (105, 157), (128, 98), + (129, 98), (152, 157), (179, 66), (212, 66), + (174, 192), (139, 192), (128, 161), (116, 192), + (82, 192), +] +shadow_shape = [(x, y + 4) for x, y in w_shape] +draw.polygon(scaled_points(shadow_shape), fill=(10, 35, 90, 45)) +draw.polygon(scaled_points(w_shape), fill=(255, 255, 255, 255)) + +icon = background.resize((SIZE, SIZE), Image.Resampling.LANCZOS) +ASSETS.mkdir(parents=True, exist_ok=True) +icon.save(ASSETS / "WindowsToolbox.png", "PNG", optimize=True) +icon.save( + ASSETS / "WindowsToolbox.ico", + "ICO", + sizes=[(16, 16), (20, 20), (24, 24), (32, 32), (40, 40), (48, 48), (64, 64), (128, 128), (256, 256)], +) +print(ASSETS / "WindowsToolbox.ico") diff --git a/src/WindowsToolbox.App/App.xaml b/src/WindowsToolbox.App/App.xaml new file mode 100644 index 0000000..a6e8557 --- /dev/null +++ b/src/WindowsToolbox.App/App.xaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/WindowsToolbox.App/App.xaml.cs b/src/WindowsToolbox.App/App.xaml.cs new file mode 100644 index 0000000..9e5c2c2 --- /dev/null +++ b/src/WindowsToolbox.App/App.xaml.cs @@ -0,0 +1,12 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace WindowsToolbox.App; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} diff --git a/src/WindowsToolbox.App/AssemblyInfo.cs b/src/WindowsToolbox.App/AssemblyInfo.cs new file mode 100644 index 0000000..cc29e7f --- /dev/null +++ b/src/WindowsToolbox.App/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/src/WindowsToolbox.App/Converters.cs b/src/WindowsToolbox.App/Converters.cs new file mode 100644 index 0000000..b50e311 --- /dev/null +++ b/src/WindowsToolbox.App/Converters.cs @@ -0,0 +1,16 @@ +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace WindowsToolbox.App; + +public sealed class PageVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => + string.Equals(value?.ToString(), parameter?.ToString(), StringComparison.OrdinalIgnoreCase) + ? Visibility.Visible + : Visibility.Collapsed; + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => + Binding.DoNothing; +} diff --git a/src/WindowsToolbox.App/MainWindow.xaml b/src/WindowsToolbox.App/MainWindow.xaml new file mode 100644 index 0000000..5c32741 --- /dev/null +++ b/src/WindowsToolbox.App/MainWindow.xaml @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +