尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Microsoft PowerToys 更新机制全解:从 GitHub 版本检测到两阶段自动安装的完整链路

Microsoft PowerToys 更新机制全解:从 GitHub 版本检测到两阶段自动安装的完整链路 Microsoft PowerToys 更新机制全解从 GitHub 版本检测到两阶段自动安装的完整链路【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys本文以 PowerToys 官方开发文档doc/devdocs/processes/update-process.md为主线系统讲解 PowerToys 的自动更新体系如何检测新版本、如何持久化更新状态、如何在 GPO 与用户设置约束下下载和安装更新。读完后你将能够完整理解从检查更新按钮点击到 WiX 安装包静默替换的整条调用链并掌握官方推荐的调试与故障排查手段。一、机制总览与关键文件PowerToys 的更新机制由检测 状态持久化 独立更新器三部分组成Runner 进程周期性或手动触发版本检测检测结果写入本地状态文件当用户接受更新时由随安装包分发的PowerToys.Update.exe完成下载、签名校验与静默安装。文档在 update-process.md 中给出的关键文件如下本文按这些文件展开源码级分析文件职责仓库路径updating.h/updating.cpp版本检测、安装器下载、清理等核心更新逻辑src/common/updating/updating.cppupdateState.h/updateState.cpp更新状态的读取与保存含文件锁src/common/updating/updateState.cppupdateLifecycle.h更新器两阶段的命令行参数构建与安全校验src/common/updating/updateLifecycle.hPowerToys.Update.cpp独立更新器主程序Stage 1 / Stage 2src/Update/PowerToys.Update.cppUpdateUtils.cppRunner 内的周期性更新工作线程与 Toast 通知src/runner/UpdateUtils.cpp二、版本检测GitHub API 与安装器资产匹配文档Version Detection一节指出更新检测使用 GitHub API 获取最新版本信息API 返回包含版本号与资产列表assets的 JSON客户端按**架构ARM64 或 X64和安装范围user 或 machine**匹配正确的安装器资产。源码证实了具体实现1. 两个 API 端点。在 updating.cpp 中定义了两个非本地化常量https://api.github.com/repos/microsoft/PowerToys/releases/latest—— 常规检查获取最新稳定版https://api.github.com/repos/microsoft/PowerToys/releases?per_page100—— 仅当用户开启包含预览版更新时才使用遍历前 100 条 release 记录从中挑选高于当前版本的最大版本号代码注释指出 GitHub 不保证 release 的返回顺序因此不能提前 break。2. 本地构建跳过检查。若版本主、次号均为 00.0.*即构建农场的本地构建get_github_version_info_async直接返回Local build cannot be updated不会发起网络请求。3. 资产匹配规则。extract_installer_asset_download_info 的匹配逻辑是扩展名 × 架构 × 文件名模式三条件同时命中且扩展名按优先级降序尝试.exe优先于.msi。文件名模式常量定义在 updating.h// non-localized constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN Lpowertoyssetup; constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN_USER Lpowertoysusersetup;当前安装范围为 per-user 时使用powertoysusersetup模式per-machine 时使用powertoyssetup模式——这与下文安装器工程的 GUID/MSI 命名一一对应。4. 版本比较与结果类型。检测结果用std::variantnew_version_download_info, version_up_to_date表达GitHub 版本号不高于当前版本时返回version_up_to_date否则返回包含 release 页面 URL、版本号、安装器下载 URL、安装器文件名、是否预览版的new_version_download_info。任何异常网络失败等统一收敛为Network error字符串避免调用方崩溃——PowerToys.Update.cpp 中特意加了对错误值解引用的防御注释说明这是曾经出过问题的位置。5. 下载重试。download_new_version_async通过http::HttpClient下载安装器到挂起更新目录MAX_DOWNLOAD_ATTEMPTS固定为 3 次updating.cpp全部失败才返回空值。三、安装范围Installation Scope与升级码文档指出 PowerToys 区分 user 安装器与 machine 安装器两者有各自不同的文件名模式与升级码upgrade code且这些码必须保持一致才能正确升级。这一点在 WiX 安装器工程中可以得到直接印证。installer/PowerToysSetupVNext/Common.wxi 根据构建变量PerUser切换两套定义维度Per-UserPer-MachineMSI 文件名PowerToysUserSetup-版本-平台.msiPowerToysSetup-版本-平台.msi默认安装目录LocalAppDataFolderProgramFiles64Folder注册表作用域HKCUHKLM权限limited非提权elevated提权UpgradeCodeD8B559DB-4C98-487A-A33F-50A8EEE4272642B84BF7-5FBF-473B-9C8B-049DC16F7708MSI 文件名中的PowerToysUserSetup/PowerToysSetup正是第二节日下载匹配所用的powertoysusersetup/powertoyssetup模式不区分大小写MSI 名中包含架构字符串满足architecture_matched条件。WiX 安装器还会在注册表中记录BundleUpgradeCode供自定义操作判断同范围旧版本的存在见 CustomAction.cpp 中的升级码比对逻辑从而保证同作用域的新版本能作为升级覆盖安装而不是并排共存。四、更新状态文件UpdateState 的结构与持久化文档Update State一节列出了状态文件包含的信息当前更新状态、release 页面 URL、上次检查时间、是否有新版本可用、安装器是否已下载。源码中对应 UpdateState 结构体struct UpdateState { enum State { upToDate 0, errorDownloading 1, readyToDownload 2, readyToInstall 3, networkError 4 } state upToDate; std::wstring releasePageUrl; std::optionalstd::time_t githubUpdateLastCheckedDate; std::wstring downloadedInstallerFilename; bool isPrerelease false; static void store(std::functionvoid(UpdateState) stateModifier); static UpdateState read(); };各状态值的含义upToDate无新版本、readyToDownload发现新版本但尚未下载、readyToInstall安装器已下载完成等待用户触发安装、errorDownloading下载失败、networkError获取版本信息失败。存储路径与命名。文档给出的路径为%LOCALAPPDATA%\Microsoft\PowerToys\update_state.json。当前代码中文件名常量是UpdateState.jsonupdateState.cpp拼在 Runner 的根保存目录下而带下划线的旧文件名update_state.json仍保留在设置 UI 的向后兼容测试资产中例如 V0.21.1 测试状态文件可见项目专门用历史版本的状态文件回归验证过格式兼容性。并发与迁移策略。从源码结构看有两个值得注意的工程细节read与store都会先获取命名互斥量Local\PowerToysRunnerUpdateStateMutex防止 Runner 与更新器并发改写状态文件序列化时会写入updateStateFileVersion字段。读取时若该字段版本与当前可执行文件版本不一致IsOldFileVersion则删除旧文件并写回默认状态——即状态文件随版本演进自动重置避免旧格式状态污染新版本逻辑。状态迁移即更新流程本身。结合 UpdateUtils.cpp 的ProcessNewVersionInfo可以看出状态机如何流转版本最新 → 置回upToDate并清空安装器文件名允许自动下载且尚未下载 → 先执行cleanup_updates()清理旧安装器然后下载成功置readyToInstall并记录文件名失败置errorDownloading不允许自动下载 → 置readyToDownload仅提示用户去设置页手动触发。cleanup_updates()updating.cpp除了删除挂起更新目录中残留的.exe/.msi还会顺带清理根保存目录下不含当前版本号的旧.log日志文件。五、更新检查自动与手动两条触发路径文档Update Checking区分了手动检查设置页点击 Check for Updates与自动检查周期性 update worker。src/runner/UpdateUtils.cpp 给出了自动检查的关键常量constexpr int64_t UPDATE_CHECK_INTERVAL_MINUTES 60 * 24; // 常规检查间隔24 小时 constexpr int64_t UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES 60 * 2; // 失败后重试间隔2 小时 const int UPDATE_NOTIFICATION_TOAST_SUSPEND_MINOR_VERSION_COUNT 2; // Toast 挂起覆盖的次版本号数两条路径的实现要点自动检查PeriodicUpdateWorker()UpdateUtils.cpp是 Runner 启动时的常驻循环根据状态文件里的githubUpdateLastCheckedDate计算距离 24 小时检查点的剩余睡眠时间醒来后拉取 GitHub 版本信息并处理结果若本次获取失败则缩短为 2 小时后重试。手动检查CheckForUpdatesCallback()由设置页的检查更新动作触发逻辑与自动检查相同但传show_notifications false——手动检查不会主动弹 Toast避免用户反复点击反复弹窗仅在确有可用更新且状态变化时通过托盘图标提示。是否自动下载由三处条件共同决定!IsMeteredConnection() get_general_settings().downloadUpdatesAutomatically再叠加 GPOdisable automatic update download的强制关闭见第六节。预览版开关effective_include_prerelease_updates()会先查DisablePreviewUpdatesGPO策略启用时无条件关闭预览更新检查否则才读取用户设置include_prerelease_updates。IsMeteredConnection()UpdateUtils.cpp基于 WinRTWindows.Networking.Connectivity判断蜂窝WWAN连接或漫游、超出流量上限、连接成本为 Fixed/Variable均视为计费等效连接从而抑制自动下载。六、GPO 更新策略组策略文档User Settings / GPO Update Settings一节列出了三个与更新相关的组策略并在 doc/devdocs/processes/gpo.md 的 Update-Related GPO Settings 中再次确认组策略作用disable automatic update download阻止自动下载安装器仍可手动检查与手动下载disable new update toast控制是否显示有新版本的 Toast 通知suspend new update toast在 2 个次版本minor release范围内挂起 Toast 通知源码中三者分别对应 gpo.h 的getDisableAutomaticUpdateDownloadValue/getSuspendNewUpdateToastValue/getDisableNewUpdateToastValue。挂起逻辑的实际算法在 ProcessNewVersionInfo当挂起策略启用且新版本的minor - 已安装 minor 2时且主版本号不大于当前抑制通知一旦落后超过 2 个次版本例如 0.60.0 已装、0.63.* 发布则恢复通知。代码注释特别提醒修改挂起阈值时必须同步更新 ADML 文档保证组策略描述与实际行为一致。此外还有第四个更新相关策略DisablePreviewUpdates启用后强制关闭预览prerelease更新检查稳定版更新不受影响UpdateUtils.cpp。按照 gpo.md 的说明策略值保存在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PowerToys机器级优先于用户级或HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\PowerToys用户级下DWORD 值 1 表示启用、0 表示禁用、键值不存在表示未配置本地测试时可用regedit直接创建这些值后重启 PowerToys 验证。七、PowerToys.Update.exe两阶段安装流程文档PowerToys Updater小节描述了PowerToysUpdate.exe的职责随安装分发、负责下载安装器并在用户点击更新通知时被调用。当前实现是 src/Update/PowerToys.Update.cpp输出名PowerToys.Update.exeWinMain依据第一个参数分派两个阶段Stage 标志常量定义在其资源头文件中整体流程如下Stage 1普通权限由更新器本体执行见 WinMain配置备份先调用updating::BackupConfigFilesconfigBackup.h备份根保存目录下的配置文件防止安装过程损坏用户数据获取安装器ObtainInstaller读取 UpdateState——若状态已是readyToInstall则直接用IsSafeDownloadedInstallerFilenameupdateLifecycle.h严格校验缓存的文件名拒绝任何路径分隔符、..、盘符与目录分量再用weakly_canonical规范化后确认解析结果仍位于 Updates 目录内随后免网络安装否则根据是否包含预览版重新调用get_github_version_info_async对readyToDownload/errorDownloading状态先cleanup_updates()再重新下载关闭 PowerToys 并派生 Stage 2InstallNewVersionStage1PowerToys.Update.cpp先把自身复制为%TEMP%\PowerToys.Update.PID.exe避免安装替换目录中正在运行的文件并顺手清理上一次遗留的孤儿更新器副本然后查找托盘图标窗口先打开进程句柄再发送WM_CLOSE防止 PowerToys 在 WM_CLOSE 处理中退出后 PID 被复用WaitForSingleObject等待其真正退出最后通过ShellExecuteExW以BuildStage2Arguments构建的参数Stage 2 标志 安装器路径 安装目录启动临时的自身副本。Stage 2提权执行安装见 InstallNewVersionStage2签名信任校验安装器存放在用户可写的%LOCALAPPDATA%\Microsoft\PowerToys\Updates目录而 Stage 2 又是提权运行的。为防止本地非提权用户在下载后、执行前窗口期内替换安装器TOCTOU 提权代码注释标注为 MSRC 112000 缓解措施Stage 2 以FILE_SHARE_READ拒绝写/删除共享打开安装器文件调用updating::verify_installer_trust验证其为 Microsoft Authenticode 签名并保持句柄开放直到启动完成确保校验过的字节 执行的字节执行安装.msi走MsiInstallProductW其余视为 WiX 引导程序以/passive /norestart参数启动并等待进程结束退出码为 0 才算成功收尾成功则把 UpdateState 重置为upToDate并刷新检查时间戳之后无论成败都会执行RestoreCorruptedConfigs检查并修复可能损坏的配置文件若提供了安装目录参数CanRelaunchAfterUpdate要求至少 4 个参数则从安装目录重新启动PowerToys.exe并传入更新成功的报告参数。八、更新通知Toast文档Update Notification描述了通知闭环新更新可用时 Toast 出现在 Windows 操作中心点击后启动更新流程更新器必要时下载安装器安装器以相应命令行参数运行。ShowNewVersionAvailable 的实现补充了通知的细节通知正文显示当前版本 → 新版本预览版会在版本号后附加-preview并使用独立的资源字符串带两个操作按钮Update now深链powertoys://update_now/最终拉起PowerToys.Update.exe的 Stage 1与More info深链powertoys://open_overview/打开设置概览页同一UPDATING_PROCESS_TOAST_TAG标签会先移除旧通知保证屏幕上至多一条更新提示自动下载模式下若被 GPO/设置抑制了 Toast见第六节通知不弹但托盘图标仍通过set_tray_icon_update_available(true)保持有可用更新提示。九、版本编号与安装包细节文档Version Numbering / Installer Details给出的规则采用语义化版本MAJOR.MINOR.PATCH常规发布递增 MINOR如 0.89.0热修复递增 PATCH如 0.87.0 → 0.87.1安装包使用 WiX 引导程序bootstrapper并为 per-user 与 per-machine 分别定义升级码升级码必须保持稳定才能正确完成升级覆盖。从源码可以补充两点佐证VersionHelpersrc/common/version/helper.h统一负责tag_name的解析与比较更新状态文件的updateStateFileVersion也用它做版本迁移判断第四节WiX 工程的 Common.wxi 中两个升级码以?define UpgradeCodeGUID...形式硬编码正是必须保持一致承诺的落点——修改任一 GUID 都会使该作用域的旧安装无法被识别为同一产品的升级。十、调试技巧与常见问题继承文档Debugging Tips一节并对应到源码行为强制触发更新检查。修改状态文件中的时间戳字段githubUpdateLastCheckedDate为一个更早的日期然后退出 PowerToys、修改文件、再启动 PowerToys。原理是PeriodicUpdateWorker依据该字段计算距 24 小时检查点的剩余睡眠第五节时间戳越早、唤醒越早。注意该文件读写受Local\PowerToysRunnerUpdateStateMutex互斥量保护编辑前请先退出 PowerToys。常见故障文档列举的四类及源码印证权限问题导致无法下载per-machine 安装器的更新器需要提权运行Stage 2 提权失败或目标目录不可写会直接失败并写日志网络中断下载下载有 3 次重试全部失败后状态置errorDownloading下次手动检查或 2 小时后重试时重新拉取组策略阻止更新disable automatic update download只禁自动下载、不禁检测disable new update toast只禁通知、更新仍会静默准备好。排查时优先查注册表策略值应用运行中导致安装失败Stage 1 已针对该场景设计了关窗口 → 等进程退出 → 再启动安装器的时序第七节若 PowerToys 拒绝退出WaitForSingleObject10 秒超时文件锁仍可能导致安装器报错。查看更新日志。文档给出的日志路径为%LOCALAPPDATA%\Microsoft\PowerToys\Logs\PowerToys-*.log其中查找 update 相关的 trace/error 条目。另外更新器自身也单独初始化了一个以LogSettings::updateLogPath为路径的日志PowerToys.Update.cpp排查点击更新后无反应时应同时查看两份日志。关键日志锚点包括Discovered new version、Downloading installer for a new version、Automatic download of updates is disabled by GPO、Aborting update: downloaded installer failed trust verification。十一、发布与回滚考虑Rollout Considerations文档Rollout Considerations明确了当前的发布策略更新对所有用户同时可用目前没有灰度staged rollout机制发布后发现严重问题只能靠热修复hotfix解决热修复的制作流程见 release-process.md。这一设计也解释了为何状态文件采用按版本号重置第四节、以及为何补丁版本只递增 PATCH 位——在无灰度的前提下快速、可回退的小版本修复是唯一的止损手段。十二、小结PowerToys 的更新机制可以用一句话概括Runner 负责发现状态文件负责记住独立的 PowerToys.Update.exe 负责安装。三个关注点解耦带来的直接好处是更新器可以独立于 Runner 崩溃或退出而完成收尾两阶段设计与 Authenticode 校验把用户可写目录 提权执行这一安全难点显式处理掉了。对于维护者或排障者而言记住四个锚点即可定位绝大多数问题GitHub API 端点第二节的两个 URL、状态文件UpdateState.json的五个状态值、WiX 的两个升级码以及 Stage 1/Stage 2 的边界。【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表