PowerToys Bug Report Tool 深度解析:诊断报告的生成流程、数据清单与隐私脱敏机制
本文围绕 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 获取桌面路径。整体流程如下:
- 准备临时目录:在系统临时目录下使用
PowerToys\子目录,先清理旧内容; - 复制配置与日志:
- 通过
PTSettingsHelper::get_root_save_folder_location()获取%LOCALAPPDATA%\Microsoft\PowerToys并整体递归复制(随后删除其中的Updates子目录,避免把完整的更新下载包打进报告); - 复制
%USERPROFILE%\AppData\LocalLow\Microsoft\PowerToys\logs(低权限日志);
- 通过
- 采集安装目录结构:非
_DEBUG编译下调用InstallationFolder::ReportStructure(reportDir); - 脱敏:
HideUserPrivateInfo(reportDir)替换敏感字段并删除含隐私的文件/目录; - 采集系统信息:Windows 设置、显示器信息、Windows 版本、.NET 安装信息、注册表、GPO、兼容性标签页信息、事件查看器日志、AppX 部署日志、安装器日志、Win11 上下文菜单包信息;
- 打包:
ZipFolder(zipPath, reportDir)将临时目录压缩为 zip,最后清理临时目录。
值得注意的是,各采集步骤均带 try/catch 兜底并打印错误提示——单个采集项失败不会中断整个报告生成,这与“诊断工具必须尽量出结果”的设计目标一致。
从源码结构看,虽然开发文档中将其描述为“console application”,当前实现是 C++ 编写的 Windows 控制台程序(BugReportTool.vcxproj + wmain 入口),并调用 C++/WinRT 接口(如 winrt::Windows::Data::Json、Windows::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-*.xml、EventViewer-Microsoft-Windows-AppXDeploymentServer/Operational.xml |
| 安装器日志 | ReportInstallerLogs 复制临时目录中前缀为 powertoys-bootstrapper-msi- 与 PowerToysMSIInstaller_ 的日志 |
原样复制 |
| Windows 11 新上下文菜单包 | ReportInstalledContextMenuPackages |
context-menu-packages.txt |
PowerToys 配置与安装完整性
配置目录中的 settings.json、各模块配置、last_version_run.json、log_settings.json、oobe_settings.json、settings_placement.json、settings-telemetry.json、UpdateState.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.txt、windows-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_EMULATION中prevhost.exe、dllhost.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。清单覆盖各模块的启用策略、自动更新控制(如 getDisableAutomaticUpdateDownloadValue、getSuspendNewUpdateToastValue)、Mouse Without Borders 网络策略(如 getConfiguredMwbSameSubnetOnlyValue、策略定义的 IP 映射规则)以及 NewPlus、QuickAccent、TextExtractor 等模块开关。在企业环境中,模块“看起来没启用”往往正是 GPO 覆盖所致,这份文件是排障第一现场。
安装目录结构与文件指纹
InstallationFolder.cpp 从可执行文件位置推导安装根目录(GetModuleFileName → 上一级并 canonical 化),递归遍历输出目录树:
- 每个文件通过
GetFileVersionInfo/VerQueryValue读取 PE 版本资源,输出四段式文件版本号(GetVersion,L12-L47); - 每个文件通过 CNG 接口(
CryptAcquireContext→CryptCreateHash(CALG_MD5)→CryptHashData)流式计算 MD5(GetChecksum,L63-L155)。
二者组合成 FileName Version MD5Hash 行,使维护者能离线比对“用户机器上到底是哪一版二进制、文件是否被第三方替换”,而不需要用户逐文件上传。
事件查看器采集:XQL 查询与进程清单
EventViewer.cpp 封装了 EventViewerReporter:
- 时间窗口为最近 10 天(
PERIOD = 10 * 24 * 3600 * 1000100ns 单位); - 按进程查询使用 XQuery 模板
QUERY_BY_PROCESS,从Application通道筛选EventData中包含目标进程名的近 10 天事件;按通道查询(QUERY_BY_CHANNEL)则用于Microsoft-Windows-AppXDeploymentServer/Operational,且仅在事件内容包含PowerToys或CommandPalette时保留,控制体积; - 结果以
BATCH_SIZE = 50的事件批量经EvtNext拉取,逐条用EvtRender(EvtRenderEventXml)渲染为格式化 XML 写入EventViewer-<进程名>.xml。
要覆盖哪些进程由 ProcessesList.cpp#L4-L56 的 processes 向量集中定义——从 PowerToys.exe、PowerToys.Settings.exe、PowerToys.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 检索 ImageResizerContextMenu、PowerRenameContextMenu 两个包,输出版本与 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 |
remapKeysToText、remapShortcutsToText、各 runProgramFilePath/Args/StartInDir、openUri 等 |
防止泄露键盘重映射中的文本、程序与 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.json、AdvancedPaste\kernelQueryCache.json、PowerToys Run\Cache、PowerRename\replace-mru.json、PowerRename\search-mru.json、PowerToys Run\Settings\UserSelectedRecord.json、PowerToys Run\Settings\QueryHistory.json、NewPlus\Templates、etw 这些文件/目录在打包前被直接删除——搜索历史、最近替换记录、用户模板等属于“对排障价值低、隐私风险高”的数据。
结合文档的概述:工具会隐藏 Mouse Without Borders 安全密钥、FancyZones 应用区域历史、用户相关路径与机器名。开发者在新增含敏感配置的模块时,应同步在这两张清单中登记。
扩展 Bug Report Tool 的切入点
新增 PowerToys 功能时,文档建议从以下五处评估是否需要同步更新该工具,结合源码可进一步落实:
- 新的日志位置:确认日志落在
%LOCALAPPDATA%\Microsoft\PowerToys\Logs或 LocalLow,否则需在wmain中增加复制逻辑; - 新增注册表键:加入 RegistryUtils.cpp 的
registryKeys或registryValues清单; - 新增 GPO 值:在 ReportGPOValues.cpp 中增加对应
powertoys_gpo取值行; - 新进程名:加入 ProcessesList.cpp 的
processes向量(同时影响事件日志与兼容性检查); - 新配置文件/新隐私字段:若配置含敏感值,登记到
escapeInfo或filesToDelete。
构建流程
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 持续有效的正确做法。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00