首页
/ PowerToys Bug Report Tool 深度解析:诊断报告的生成流程、数据清单与隐私脱敏机制

PowerToys Bug Report Tool 深度解析:诊断报告的生成流程、数据清单与隐私脱敏机制

2026-09-06 15:50:59作者:晏闻田Solitary

本文围绕 PowerToys 仓库中的 Bug Report Tool(问题报告工具)展开:它是随安装包内置的日志与系统信息采集器,可将 PowerToys 运行日志、注册表状态、组策略(GPO)配置、事件查看器记录、安装目录文件指纹等打包成一个可直接提交给开发者的 zip 报告。读完本文,你能完整理解该报告的触发方式、报告内每个关键文件的来源、隐私脱敏的具体实现逻辑,以及如何基于源码扩展采集项与独立构建该工具。

工具定位与触发方式

Bug Report Tool 用于在用户遇到 PowerToys 问题时收集诊断信息。它从 PowerToys 的应用目录复制日志、采集与功能相关的系统信息、对敏感信息进行脱敏,最终把所有内容压缩为桌面上的 PowerToys_Report_[日期]_[时间].zip 文件,供用户在问题反馈中分享给开发者。

用户有两种触发路径:

  • 右键点击 PowerToys 托盘图标 → Report Bug
  • 左键点击托盘图标 → 打开设置 → Bug Report Tool

从 Runner(主程序)侧的实现看,src/runner/bug_report.cpp 中的 BugReportManager::launch_bug_report() 会定位安装目录下的 Tools\PowerToys.BugReportTool.exe 并在新线程中启动它,同时通过观察者回调向 UI 通知“运行中/已结束”状态(见 bug_report.cpp#L42-L68)。也就是说,用户看到的“生成报告”进度窗口,本质上是 Runner 托管的一次外部进程调用。

工具源码位于 tools/BugReportTool/,独立于主解决方案构建(详见文末“构建流程”一节)。

报告生成流程(wmain 主链路)

工具的主入口是 Main.cpp#L294-L406 中的 wmain。它支持可选的命令行参数:第一个参数为 zip 输出目录,缺省时通过 SHGetSpecialFolderPath 获取桌面路径。整体流程如下:

  1. 准备临时目录:在系统临时目录下使用 PowerToys\ 子目录,先清理旧内容;
  2. 复制配置与日志
    • 通过 PTSettingsHelper::get_root_save_folder_location() 获取 %LOCALAPPDATA%\Microsoft\PowerToys 并整体递归复制(随后删除其中的 Updates 子目录,避免把完整的更新下载包打进报告);
    • 复制 %USERPROFILE%\AppData\LocalLow\Microsoft\PowerToys\logs(低权限日志);
  3. 采集安装目录结构:非 _DEBUG 编译下调用 InstallationFolder::ReportStructure(reportDir)
  4. 脱敏HideUserPrivateInfo(reportDir) 替换敏感字段并删除含隐私的文件/目录;
  5. 采集系统信息:Windows 设置、显示器信息、Windows 版本、.NET 安装信息、注册表、GPO、兼容性标签页信息、事件查看器日志、AppX 部署日志、安装器日志、Win11 上下文菜单包信息;
  6. 打包ZipFolder(zipPath, reportDir) 将临时目录压缩为 zip,最后清理临时目录。

值得注意的是,各采集步骤均带 try/catch 兜底并打印错误提示——单个采集项失败不会中断整个报告生成,这与“诊断工具必须尽量出结果”的设计目标一致。

从源码结构看,虽然开发文档中将其描述为“console application”,当前实现是 C++ 编写的 Windows 控制台程序(BugReportTool.vcxproj + wmain 入口),并调用 C++/WinRT 接口(如 winrt::Windows::Data::JsonWindows::Management::Deployment)。

报告收集的数据清单

日志

  • %LOCALAPPDATA%\Microsoft\PowerToys\Logs:常规日志,随配置目录整体复制;
  • %USERPROFILE%\AppData\LocalLow\Microsoft\PowerToys\logs:低权限(LocalLow)日志,对应以受限令牌运行的模块。

系统信息

项目 采集实现 输出文件
Windows 版本与 Build ReportWindowsVersion 通过 ntdll!RtlGetVersion 获取主/次版本号与 Build 号 windows-version.txt
语言与区域 ReportWindowsSettings 读取 WinRT GlobalizationPreferences 首选语言与线程 Locale windows-settings.txt
显示器信息 ReportMonitorInfo(由 Monitor Report Tool 的逻辑实现),对 FancyZones 与多显示器场景排障至关重要 monitor-report-info.txt
.NET 安装详情 ReportDotNetInstallationInfo 执行 dotnet --list-runtimes 并落盘 dotnet-installation-info.txt
PowerToys 相关注册表 ReportRegistry,见下文“注册表采集” registry-report-info.txt
GPO 策略值 ReportGPOValues,见下文“GPO 采集” gpo-configuration-info.txt
应用兼容性模式 ReportCompatibilityTab,检查用户与系统两级 AppCompatFlags\Layers compatibility-tab-info.txt
事件查看器日志 EventViewer::ReportEventViewerInfo / ReportAppXDeploymentLogs EventViewer-*.xmlEventViewer-Microsoft-Windows-AppXDeploymentServer/Operational.xml
安装器日志 ReportInstallerLogs 复制临时目录中前缀为 powertoys-bootstrapper-msi-PowerToysMSIInstaller_ 的日志 原样复制
Windows 11 新上下文菜单包 ReportInstalledContextMenuPackages context-menu-packages.txt

PowerToys 配置与安装完整性

配置目录中的 settings.json、各模块配置、last_version_run.jsonlog_settings.jsonoobe_settings.jsonsettings_placement.jsonsettings-telemetry.jsonUpdateState.json 等文件随目录整体进入报告;同时 installationFolderStructure.txt 提供安装目录的文件结构完整性校验信息(详见下文)。

报告关键文件说明

  • compatibility-tab-info.txt:PowerToys 各可执行文件在用户级与系统级设置的两份“兼容性”选项卡(以旧版 Windows 模式运行、DPI 覆盖等)配置,这类设置曾导致过 UI 缩放/渲染类问题;
  • context-menu-packages.txt:注册到 Windows 11 新上下文菜单的 MSIX 包(ImageResizer、PowerRename)的显示名、完整包名、版本、发行者与 VerifyIsOK 校验状态;
  • dotnet-installation-info.txt:已安装的 .NET 运行时版本清单;
  • EventViewer-*.xml:以文件名为可执行文件名的事件查看器日志(XSL 渲染后的 XML 事件),是定位崩溃/异常的首选材料;
  • EventViewer-Microsoft-Windows-AppXDeploymentServer/Operational.xml:AppXDeployment-Server 通道事件,用于诊断 MSIX 安装/注册失败(例如上下文菜单包、Peek 等打包应用无法注册);
  • gpo-configuration-info.txt:当前生效的 GPO 策略值;
  • installationFolderStructure.txt:安装目录树,每个文件行格式为 FileName Version MD5Hash,可快速比对文件是否被篡改、丢失或版本错乱;
  • last_version_run.json:上一次实际运行的 PowerToys 版本;
  • log_settings.json:日志级别设置(排查“日志太少”类问题);
  • monitor-report-info.txt:连接的显示器信息,由 Monitor Info Report Tool 的逻辑生成;
  • oobe_settings.json:OOBE(开箱体验)设置;
  • registry-report-info.txt:PowerToys 使用的注册表键值;
  • settings_placement.json:设置窗口的位置/大小记忆;
  • settings-telemetry.json:上次发送遥测数据的时间;
  • UpdateState.json:上次更新检查结果与下载状态;
  • windows-settings.txtwindows-version.txt:语言设置与 Windows 版本。

注册表与 GPO 采集的源码细节

注册表采集

RegistryUtils.cpp 维护了两份清单:

  • 整键递归导出清单L11-L41):覆盖 HKEY_CLASSES_ROOT 下的 PowerToys 相关 powertoys 键、一组 CLSID/AppID(文件资源管理器扩展、预览/缩略图提供程序注册),以及 AllFileSystemObjects\ShellEx\ContextMenuHandlers\PowerRenameExt 与各文件类型(.svg.md.pdf.qoi.gcode.bgcode.stl)的 shellex 注册项——这些正是 File Explorer 扩展/预览功能依赖的注册位置;
  • 精确值清单L43-L52):PreviewHandlers 下的各预览处理器 GUID 值,以及 FEATURE_BROWSER_EMULATIONprevhost.exedllhost.exe 的键值(Monaco 预览依赖 WebView2 的浏览器模式)。

QueryKey 使用 RegQueryInfoKeyW + RegEnumValueW/RegEnumKeyExW 递归读取并缩进输出,打开失败时写入 ERROR <code> 而不是抛异常——报告中出现的 ERROR 2(找不到键)本身就是一种有用的状态信号(例如某扩展未被安装或注册失败)。

GPO 采集

ReportGPOValues.cpp#L39-L111 逐项调用 common/utils/gpo.h 提供的 powertoys_gpo::getConfigured*EnabledValue() 等接口,把策略状态映射为文本:enabled / disabled / not_configured / can't_access / wrong_value。清单覆盖各模块的启用策略、自动更新控制(如 getDisableAutomaticUpdateDownloadValuegetSuspendNewUpdateToastValue)、Mouse Without Borders 网络策略(如 getConfiguredMwbSameSubnetOnlyValue、策略定义的 IP 映射规则)以及 NewPlus、QuickAccent、TextExtractor 等模块开关。在企业环境中,模块“看起来没启用”往往正是 GPO 覆盖所致,这份文件是排障第一现场。

安装目录结构与文件指纹

InstallationFolder.cpp 从可执行文件位置推导安装根目录(GetModuleFileName → 上一级并 canonical 化),递归遍历输出目录树:

  • 每个文件通过 GetFileVersionInfo/VerQueryValue 读取 PE 版本资源,输出四段式文件版本号(GetVersionL12-L47);
  • 每个文件通过 CNG 接口(CryptAcquireContextCryptCreateHash(CALG_MD5)CryptHashData)流式计算 MD5(GetChecksumL63-L155)。

二者组合成 FileName Version MD5Hash 行,使维护者能离线比对“用户机器上到底是哪一版二进制、文件是否被第三方替换”,而不需要用户逐文件上传。

事件查看器采集:XQL 查询与进程清单

EventViewer.cpp 封装了 EventViewerReporter

  • 时间窗口为最近 10 天(PERIOD = 10 * 24 * 3600 * 1000 100ns 单位);
  • 按进程查询使用 XQuery 模板 QUERY_BY_PROCESS,从 Application 通道筛选 EventData 中包含目标进程名的近 10 天事件;按通道查询(QUERY_BY_CHANNEL)则用于 Microsoft-Windows-AppXDeploymentServer/Operational,且仅在事件内容包含 PowerToysCommandPalette 时保留,控制体积;
  • 结果以 BATCH_SIZE = 50 的事件批量经 EvtNext 拉取,逐条用 EvtRender(EvtRenderEventXml) 渲染为格式化 XML 写入 EventViewer-<进程名>.xml

要覆盖哪些进程由 ProcessesList.cpp#L4-L56processes 向量集中定义——从 PowerToys.exePowerToys.Settings.exePowerToys.PowerLauncher.exe,到 FancyZones、KeyboardManager、FileLocksmith、MouseWithoutBorders 的 Helper/Service、各预览/缩略图提供程序(PowerToys.GcodePreviewHandler.exe 等)以及 Microsoft.CmdPal.UI.exe,共 50 余项。该清单同时驱动事件日志采集与兼容性标志检查,是扩展 Bug Report Tool 时最常被修改的“注册表”。

另外,Package.cpp#L50-L78 通过 WinRT PackageManager.FindPackagesForUser 检索 ImageResizerContextMenuPowerRenameContextMenu 两个包,输出版本与 VerifyIsOK 状态,用于判断 Win11 新右键菜单中 PowerToys 条目缺失是否为包注册/损坏问题。

隐私脱敏(Redaction)机制

这是 Bug Report Tool 最值得学习的部分:报告包含用户本地配置目录的完整副本,若不脱敏会泄露连接密钥、路径习惯等隐私。实现集中在 Main.cpp#L28-L61

1. 字段级脱敏 escapeInfo(JSON XPath 映射表)

文件 脱敏字段(XPath) 目的
FancyZones\app-zone-history.json app-zone-history/app-path 隐藏用户打开过的应用路径
FancyZones\settings.json properties/fancyzones_excluded_apps 隐藏被排除应用列表
MouseWithoutBorders\settings.json properties/SecurityKey 防止泄露多机连接密钥
Keyboard Manager\default.json remapKeysToTextremapShortcutsToText、各 runProgramFilePath/Args/StartInDiropenUri 防止泄露键盘重映射中的文本、程序与 URI
Workspaces/workspaces.json workspaces/applications/command-line-arguments 隐藏工作区应用的命令行参数
AdvancedPaste/settings.json properties/custom-actions/value/name.../prompt 隐藏自定义动作的名称与提示词

执行逻辑是:HideForFile 读取对应 JSON → GetXpathArray 把 XPath 按 / 切分为路径段 → HideByXPath 递归下钻(数组节点会遍历所有元素),命中叶子键后把值替换为 <private_data> 字符串 → 写回文件(Main.cpp#L87-L149)。

2. 整体删除 filesToDelete

AdvancedPaste\lastQuery.jsonAdvancedPaste\kernelQueryCache.jsonPowerToys Run\CachePowerRename\replace-mru.jsonPowerRename\search-mru.jsonPowerToys Run\Settings\UserSelectedRecord.jsonPowerToys Run\Settings\QueryHistory.jsonNewPlus\Templatesetw 这些文件/目录在打包前被直接删除——搜索历史、最近替换记录、用户模板等属于“对排障价值低、隐私风险高”的数据。

结合文档的概述:工具会隐藏 Mouse Without Borders 安全密钥、FancyZones 应用区域历史、用户相关路径与机器名。开发者在新增含敏感配置的模块时,应同步在这两张清单中登记。

扩展 Bug Report Tool 的切入点

新增 PowerToys 功能时,文档建议从以下五处评估是否需要同步更新该工具,结合源码可进一步落实:

  1. 新的日志位置:确认日志落在 %LOCALAPPDATA%\Microsoft\PowerToys\Logs 或 LocalLow,否则需在 wmain 中增加复制逻辑;
  2. 新增注册表键:加入 RegistryUtils.cppregistryKeysregistryValues 清单;
  3. 新增 GPO 值:在 ReportGPOValues.cpp 中增加对应 powertoys_gpo 取值行;
  4. 新进程名:加入 ProcessesList.cppprocesses 向量(同时影响事件日志与兼容性检查);
  5. 新配置文件/新隐私字段:若配置含敏感值,登记到 escapeInfofilesToDelete

构建流程

Bug Report Tool 独立于主 PowerToys 解决方案构建,且必须在构建安装器之前完成——构建产物随 PowerToys 安装包分发(安装后位于 Runner 查找的 Tools\PowerToys.BugReportTool.exe)。

命令行构建(在仓库根目录,需 VS 开发环境):

nuget restore .\tools\BugReportTool\BugReportTool.sln
msbuild -p:Platform=x64 -p:Configuration=Release .\tools\BugReportTool\BugReportTool.sln

Visual Studio 构建:打开 BugReportTool.sln,将解决方案配置设为 Release 后生成即可。

小结

Bug Report Tool 用“一次性快照”的思路把分散在应用配置目录、注册表、组策略、事件日志和安装目录中的诊断面收敛为单一 zip 包;其工程价值不仅在于采集清单的完整,更在于字段级 XPath 脱敏、文件级整体删除、MD5+文件版本指纹、按进程 XQuery 事件采集等机制,为“用户可自助提交、开发者可离线复现”的问题排查流程提供了可复制的参考实现。若你负责维护 PowerToys 的某个模块,把本模块的进程名、注册表键、日志位置与隐私字段纳入上述各清单,就是让 Bug Report Tool 持续有效的正确做法。

登录后查看全文
热门项目推荐
相关项目推荐