GTweak CommandExecutor 命令执行器详解:180 行代码如何驱动上百项系统优化
【免费下载链接】GTweakPortable Tool for an Ideal Windows Setup项目地址: https://gitcode.com/GitHub_Trending/gt/GTweak
GTweak 是一款免安装的 Windows 系统优化工具,上百个功能按钮背后都靠一个 CommandExecutor.cs 静默执行命令。本文带你一次性看懂这个"命令执行器":一次点击如何走到 CMD,6 个入口方法各管什么,以及 3 处能直接搬走的实现。
🖱️ 点击"清理临时文件"后,你的屏幕前发生了什么
在维护页点一下"清理临时文件",界面没冒出一个黑框,文件却真的被删了。你以为它只是个快捷方式,其实 MemoryCleaner.cs 干的事要狠得多:删Windows\Temp、清%localappdata%\Temp、连 WER 崩溃日志都不放过——这些目录的属主是 TrustedInstaller,连普通管理员都动不了。可用户全程只看到进度提示,没见到一次 UAC 弹窗、一个闪烁的窗口。
这条"看不见"的链路,靠的就是 CommandExecutor.cs 这个internal static类。它像前台接线员:所有业务模块打一个电话,至于找谁干活、要不要喊管理员、窗口藏哪儿,接线员自己安排——这就是门面模式(Facade)。
带走的话:功能模块永远不直接碰 Process,只跟执行器说话。
🧭 一次点击的完整执行链路:谁发起、谁执行、结果流向哪
以"激活 Windows"为例,完整链路是一条四段接力:
- 谁发起:ViewModel 或业务模块(如 WinLicenseHandler.cs)决定"要跑什么",只挑方法、不写进程代码。
- 谁路由:
CommandExecutor按两个维度选方法——要不要看输出、要不要提权/可见,全类就 6 个入口。 - 谁执行:解释器接管。
cmd.exe或powershell.exe带-NoProfile -ExecutionPolicy Bypass参数启动,WindowStyle.Hidden+CreateNoWindow把黑框藏进墙里。 - 结果流向哪:要输出的走
StandardOutput回到调用方;失败一律交给 ErrorLogger.cs 落日志,UI 不感知。
| 角色 | 职责 | 典型调用方 |
|---|---|---|
| ViewModel / 业务模块 | 声明"跑什么",挑对入口方法 | AddonsViewModel.cs |
| CommandExecutor | 路由:静默、提权、等待、编码 | 全项目约 20 个文件 |
| cmd / PowerShell | 实际执行,窗口被隐藏 | — |
| TrustedInstaller | 提权到系统最高身份 | TrustedInstaller.cs |
| ErrorLogger | 承接所有异常,保证 UI 不崩 | — |
带走的话:把"跑什么"和"怎么跑"劈成两半,排障时永远先问是哪一半出了问题。
🔧 关键实现拆解:三处最值得抄的代码
1. 老 API 异步化:WaitForExitAsync 扩展方法
它做了什么:.NET 4.x 的Process没有原生异步等待,作者用TaskCompletionSource+Exited事件补了个约 20 行的扩展方法:
var tcs = new TaskCompletionSource<object>(); process.Exited += (s, e) => tcs.TrySetResult(null); tcs.Task.ContinueWith(_ => process.Exited -= Handler, TaskScheduler.Default); return tcs.Task;为什么这么写:进程一退出就TrySetResult,GetCommandOutput/InvokeRunCommand因此能await而不卡死 WPF 界面线程;完成后自动解绑事件,防内存泄漏。
不这么写会怎样:WaitForExit()会同步冻结 UI,点一次激活整个窗口假死;不解绑事件则每次调用都泄漏一个订阅。
2. 多行命令压平:CleanCommand
它做了什么:自定义脚本参数常带换行和乱空格,直接拼接必挂。CleanCommand按行拆分、去空、压缩连续空白,再根据内容里是否已含&&或&自动选分隔符,把多行命令安全地压回单行。
为什么这么写:&&前一条成功才继续,&无条件续跑;清理类命令往往希望"删失败也接着删",选&更稳。
不这么写会怎样:cmd /c遇到裸换行直接截断或报错,用户脚本在第一行之后就悄悄失效。
3. 两种提权、一个 PID:runas 与 TrustedInstaller 双通道
它做了什么:普通提权只有一行——RunCommand里设Verb = "runas",UAC 弹一下就完事。
FileName = ..., Arguments = ..., Verb = "runas", CreateNoWindow = true为什么这么写:受保护文件要更高身份,RunCommandAsTrustedInstaller就接管了:伪装成 TrustedInstaller 的子进程执行命令(TrustedInstaller.cs 里用PROC_THREAD_ATTRIBUTE_PARENT_PROCESS完成"借位")。类里那个volatile int PID记录被借位的进程 ID,让识别链路闭环。
不这么写会怎样:rd /s /q C:\Windows\Temp这种命令普通管理员会收到"拒绝访问",清理功能等于摆设。
带走的话:静默、提权、等待三样能力收口在一个文件里,新增功能只需挑方法,不用重造进程代码。
⚖️ 取舍与替代:作者放弃了什么
第一,为什么 Base64 而不转义引号?RunEncodedPowerShell把命令Convert.ToBase64String(Encoding.Unicode.GetBytes(command))后传给-EncodedCommand。逐层转义引号、$、中文是出了名的坑,而且 PowerShell 的-Command收 ASCII 控制台代码页,中文直接变乱码;Base64 一刀切掉两类问题。代价是日志里读不懂原始命令——作者认为调试体验换稳定性,划算。
第二,为什么"发完即忘"而不是等待一切?RunCommand不await、不取结果,属于 fire-and-forget。像清临时文件这种长链条命令,等待只会拖住界面;失败也没人关心,交给ErrorLogger就行。作者放弃的是执行结果的即时反馈,换回的是 UI 永远流畅。
带走的话:不是所有命令都值得等待——按"结果是否被后续依赖"决定同步还是异步。
📦 可直接搬走的清单
- 给旧 API 补异步,用 TCS + 事件 + 自动解绑三件套:
TaskCompletionSource配合事件订阅,ContinueWith里记得-= Handler,直接抄进任何 .NET 老项目。 - 把进程启动参数模板化:
-NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass这套 PowerShell 组合 +CreateNoWindow,是你写任何后台执行器的起点。 - 写一个命令压平器:按行拆分、
Trim、Regex压空白、按内容选&&/&,多行输入变单行可执行。 - 用退出码做"静默失败"契约:
ExitCode != 0就返回空字符串而不是抛异常,上层逻辑天然容错。 - 入口收敛成 6 个以内:按"静默/可见 × 同步/异步 × 普通/提权"组合方法,超过 6 个就该重新切分了。
从一次点击删临时文件,到 slmgr 激活 Windows,GTweak 的全部功能按钮最终都汇进这个不到 180 行的文件。理解了 CommandExecutor,你就拿到了整款工具背后所有按钮的"总钥匙"——而它前台接线员式的门面设计,正是你下一个项目里最该学的一课。
【免费下载链接】GTweakPortable Tool for an Ideal Windows Setup项目地址: https://gitcode.com/GitHub_Trending/gt/GTweak
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考