ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PowerToys 新模块开发端到端指南:从模块模板到设置集成、调试与打包

PowerToys 新模块开发端到端指南:从模块模板到设置集成、调试与打包 PowerToys 新模块开发端到端指南从模块模板到设置集成、调试与打包【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys本文基于 PowerToys 官方开发者文档 Creating a new PowerToy: end-to-end developer guide 整理扩写完整覆盖从零构建一个 PowerToys 工具模块的全流程模块类型选型、模块接口Module Interface的关键方法、服务工程搭建、设置系统集成、调试技巧、WiX 安装包集成以及测试与 OOBE 收尾。读完后你可以独立完成一个新模块从模板初始化、runner 注册、设置页面接线到可打包交付的全部工作并理解 runner 与模块 DLL 之间的加载机制。1. 概览与前置条件PowerToy 模块是一个自包含self-contained的工具单元集成在 PowerToys 生态内可以是纯 UI 型、纯后台服务型或者两者兼有。1.1 环境要求先按照 Getting Started 指南配置好开发环境然后参照 调试文档 验证自己能够构建并运行PowerToys.slnx。可选WiX v5 工具集用于制作安装包。1.2 标准目录结构原文档约定的模块目录布局如下所有模块逻辑都应隔离在src/modules/YourModule之下src/ modules/ your_module/ YourModule.sln YourModuleInterface/ YourModuleUI/ (if needed) YourModuleService/ (if needed)从源码结构看ModuleInterface工程产出的是ModuleInterface.dll供 runner 在运行时加载并调用UI 与 Service 工程则按需拆分。2. 设计与规划先确定模块类型再写接口2.1 选择模块类型写代码前先想清楚三件事需要什么样的 UI、生命周期如何、它是常驻服务还是事件驱动。文档给出了四类典型场景并推荐对照相似的现有模块来研究UI-only纯界面例如 ColorPicker可参考 src/modules/colorPickerBackground service后台服务例如 LightSwitch、Awake可参考 src/modules/LightSwitch 与 src/modules/awakeHybridUI 后台逻辑混合例如 ShortcutGuide可参考 src/modules/ShortcutGuideC/C# 互操作例如 PowerRename可参考 src/modules/powerrename。模块主体既可以用 C 编写也可以用 C# 编写。2.2 模块接口的关键成员模块入口是ModuleInterface模板中为dllmain.cpp核心类继承自PowertoyModuleIface。当前仓库中的接口定义位于 powertoy_module_interface.h其中声明了get_name、get_key、get_config、set_config、enable、disable、is_enabled、destroy等纯虚函数以及带默认实现的get_hotkeys、on_hotkey、is_enabled_by_default、gpo_policy_enabled_configuration等方法见该文件约 89–156 行。文档要求你在模板中理解并改造以下九组关键成员1设置结构体ModuleSettings这是模块设置项的存放处值类型可以是字符串、bool、int甚至自定义枚举struct ModuleSettings {};2接口类声明完整类定义继承PowertoyModuleIface私有成员通常包含启用状态、事件处理逻辑或热键相关字段公有部分包含构造函数与初始化逻辑class ModuleInterface : public PowertoyModuleIface { private: // the private members of the class // Can include the enabled variable, logic for event handlers, or hotkeys. public: // the public members of the class // Will include the constructor and initialization logic. }注意类中许多函数是样板代码只需把模块名做简单的字符串替换下面列出的其余函数才需要较大改动。3GPO 组策略支持GPOGroup Policy Object允许管理员在一组机器上统一下发策略。你的模块必须出现在 GPO 的设置列表中并实现gpo_policy_enabled_configuration返回对应模块的策略值。实现上可以右键powertoys_gpo对象跳转到定义为模块配置getConfiguredModuleEnabledValuevirtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override { return powertoys_gpo::getConfiguredModuleEnabledValue(); }4init_settings()初始化设置从已存在的settings.json读取配置文件不存在时保留默认值void ModuleInterface::init_settings()5get_config向设置面板描述配置Runner 调用它获取settings.json中该模块的配置描述序列化为 JSON 写入缓冲virtual bool get_config(wchar_t* buffer, int* buffer_size) override6set_config接收新设置设置面板提交的新值会以序列化 JSON 传入模块负责解析并持久化virtual void set_config(const wchar_t* config) override7call_custom_action自定义动作当模块使用custom_action类型的设置项时设置面板点击按钮会触发该方法void call_custom_action(const wchar_t* action) override8生命周期函数控制模块的启用/禁用状态以及默认是否启用virtual void enable() // starts the module virtual void disable() // terminates the module and performs any cleanup virtual bool is_enabled() // returns if the module is currently enabled virtual bool is_enabled_by_default() const override // allows the module to dictate whether it should be enabled by default in the PowerToys app.9热键函数负责热键的解析、上报与响应// takes the hotkey from settings into a format that the interface can understand void parse_hotkey(PowerToysSettings::PowerToyValues settings) // returns the hotkeys from settings virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override // performs logic when the hotkey event is fired virtual bool on_hotkey(size_t hotkeyId) override2.3 设计原则模块逻辑隔离在/modules/YourModule下禁止跨模块直接依赖优先复用 src/common 中的共享工具库如 DPI 辅助dpi_aware.h、显示器枚举monitors.h等init/set/get config 都通过预设函数访问设置核心实现在src/common/SettingsAPI的 settings_helpers.h 与 settings_objects.h 中PowerToysSettings::Settings支持add_bool_toggle、add_int_spinner、add_string、add_color_picker、add_custom_action等控件类型PowerToyValues提供load_from_settings_file/save_to_settings_file持久化方法。3. 模块脚手架Bootstrapping使用 PowerToy 模块模板 生成模块接口的起始代码。模板安装方式见 tools/project_template/README.md把ModuleTemplate.zip放入%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates\VS 2026 对应Visual Studio 18目录之后在 Visual Studio 新建工程时于 Visual C 选项卡下即可看到。把全部工程与命名空间替换为你的模块名。模板文件 dllmain.cpp 中使用$projectname$/$safeprojectname$占位符例如const static wchar_t* MODULE_NAME L$projectname$;。更新.vcxproj与解决方案文件中的 GUID。用你自己的逻辑实现第 2 节提到的各函数。注册模块——这是模块能被 runner 检测到的必要步骤。原文档列出的注册清单包括src/runner/modules.hsrc/runner/modules.cppsrc/runner/resource.hsrc/runner/settings_window.hsrc/runner/settings_window.cppsrc/runner/main.cppsrc/common/logger.h日志小技巧在 runner 代码中搜索已有模块名如LightSwitch可以快速定位这些清单。从当前仓库源码可以确认runner 在 src/runner/main.cpp 中维护了knownModules列表逐项形如LPowerToys.LightSwitchModuleInterface.dll随后在循环中调用load_powertoy(moduleSubdir)加载并以pt_module-get_key()为键存入modules()设置页映射在 src/runner/settings_window.h 的ESettingsWindowNames枚举与 src/runner/settings_window.cpp 中成对出现模板 README 还补充说明模块 DLL 名需加入 src/runner/main.cpp 的known_dlls映射才能在运行时被加载。ModuleInterface工程必须产出ModuleInterface.dll这样 runner 才能与服务交互。经验提示模块 ID 不一致manifest、注册表、服务之间是最常见的加载失败原因之一务必保持一致。4. 编写服务Service每个 PowerToy 的服务形态都不同。建议先在独立工程中开发应用主体再接入 PowerToys 的设置逻辑但服务必须先于 runner 接线完成。要点服务是与 Module Interface 相互独立的项目可用 C# 或 C 编写服务图标通过.rc文件设置服务名在.vcxproj中通过TargetName设置例如PropertyGroup OutDir..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)\/OutDir TargetNamePowerToys.LightSwitchService/TargetName /PropertyGroup需要查看.vcxproj内容时右键工程选择Unload project服务内读取设置的推荐写法ModuleSettings单例随服务代码提供可参考 src/modules/LightSwitch 中的实现并按需裁剪ModuleSettings::instance().InitFileWatcher(); ModuleSettings::instance().LoadSettings(); auto settings ModuleSettings::instance().settings();如果模块带用户界面使用WinUI Blank App模板建工程遵循 Windows 设计最佳实践借助 WinUI 3 Gallery 应用辅助 UI 编码。5. 设置系统集成PowerToys 的设置按模块以 JSON 形式存放在%LOCALAPPDATA%\Microsoft\PowerToys\module\settings.json5.1 C# 侧实现步骤在src\settings-ui\Settings.UI.Library\下创建moduleProperties.cs与moduleSettings.cs。Properties中定义所有设置的默认值需与 Module Interface 中声明的设置项一一对应moduleSettings.cs负责构建settings.json对象结构应匹配public ModuleSettings() { Name ModuleName; Version Assembly.GetExecutingAssembly().GetName().Version.ToString(); Properties new ModuleProperties(); // settings properties you set above. }在src\settings-ui\Settings.UI\ViewModels下创建moduleViewModel.cs——它是 PowerToys 应用内设置页与磁盘设置文件之间的交互层。此处的变更会通过NotifyPropertyChanged事件触发设置监听器在src\settings-ui\Settings.UI\SettingsXAML\Views创建SettingsPage.xaml即用户与模块设置交互的页面面向用户的字符串必须走资源串以便本地化x:Uid关联 Resources.resw// LightSwitch.xaml ComboBoxItem x:UidLightSwitch_ModeOff AutomationProperties.AutomationIdOffCBItem_LightSwitch TagOff / // Resources.resw data nameLightSwitch_ModeOff.Content xml:spacepreserve valueOff/value /data重要上面示例用.Content定位 ComboBox 的内容这个后缀会随控件类型变化例如.Text、.Header等。提醒通过外部编辑器VS Code、记事本的手工修改不会触发设置监听器只有通过 PowerToys 写入的变更才会触发重载。这一行为与 C 侧的文件监听机制src/common/SettingsAPI中的 FileWatcher.h相对应。5.2 常见坑只使用 WinUI 3 框架不要使用 UWP从非 UI 线程更新 UI 时必须使用DispatcherQueue。6. 构建与调试6.1 调试步骤首次调试 PowerToys 的开发者请先完成 调试文档 中的预调试准备将runner设为启动项目并确认构建配置与系统架构ARM64/x64一致按F5或点击Local Windows Debugger按钮开始调试runner 会随之启动若要为服务设断点按 CtrlAltP 搜索你的服务进程并附加到 runner用日志记录改动。日志位置Runner 日志%LOCALAPPDATA%\Microsoft\PowerToys\RunnerLogs模块日志%LOCALAPPDATA%\Microsoft\PowerToys\Module\Service\version提示PowerToys 会激进缓存.nuget产物构建行为异常时可用git clean -xfd清理。runner 侧的加载行为可以在 src/runner/main.cpp 中看到佐证Debug 模式下某个模块加载失败只会Logger::warn记录并继续执行便于开发者快速迭代不必为调试单个模块而构建全部模块。7. 安装包与打包WiX7.1 把模块加入安装器通过 NuGet 为 WiX5 安装WixToolset.Heat在installer\PowerToysInstallerVNext目录为你的模块新增一个Module.wxs文件例如 installer/LightSwitch.wxs 可作为参照格式拷贝其他模块如 Light Switch的 wxs 格式替换字符串与 GUID 值关键占位符是!--ModuleNameFiles_Component_Def--——它会被generateFileComponents.ps1生成的组件代码替换当前仓库中对应的生成脚本为 installer/generateAllFileComponents.ps1在 installer/Product.wxs 的Feature IdCoreFeature ... 段落中加入一行ComponentGroupRef IdModuleComponentGroup /在文件组件生成脚本末尾按如下格式为新模块追加条目-fileListName ModuleFiles需与Module.wxs中设置的字符串一致ModuleServiceName需与服务 exe 名一致# Module Name Generate-FileList -fileDepsJson -fileListName ModuleFiles -wxsFilePath $PSScriptRoot\Module.wxs -depsPath $PSScriptRoot..\..\..\$platform\Release\ModuleServiceName Generate-FileComponents -fileListName ModuleFiles -wxsFilePath $PSScriptRoot\Module.wxs -regroot $registryroot8. 测试与验证8.1 UI 测试测试工程放在/modules/YourModule/Tests新建 WinUI Unit Test App参照现有模块如 Light Switch的测试写法可测试独立的 UI如 Color Picker 类模块也可以验证 PowerToys 应用内的设置 UI 是否真正控制到了你的服务。8.2 手动验证清单在 PowerToys Settings 中启用/禁用模块检查日志中的初始化记录确认图标、工具提示tooltips与 OOBE 页面正确显示。8.3 实用技巧验证睡眠/唤醒与提权elevation状态。后台模块若在事件句柄未在恢复后重建唤醒后往往静默失效用 Windows Sandbox 模拟干净安装环境想模拟“新用户”可删除%LOCALAPPDATA%\Microsoft下的 PowerToys 文件夹。8.4 快捷键冲突检测如果模块带快捷键必须按设置实现文档中 Shortcut conflict detection 章节的步骤正确注册以获得冲突检测能力。runner 侧的冲突检测实现可参考 src/runner/hotkey_conflict_detector.cpp 与集中式热键管理 src/runner/centralized_hotkeys.cpp。9. 收尾工作9.1 OOBEOut-of-Box Experience页面OOBE 页面是一个自定义设置页在新用户首次使用以及更新之后、正式设置应用打开之前显示让用户一眼了解各模块用途。需要在src\settings-ui\Settings.UI\SettingsXAML\OOBE\Views创建OOBEModuleName.xaml把模块名加入src\settings-ui\Settings.UI\OOBE\Enums\PowerToysModules.cs中的枚举。9.2 模块资源Assets模块功能完成后需要规划对外展示的资源Module Icon显示在 OOBE 页面、README、PowerToys 主页、模块设置页等多处Module Image各模块设置页顶部的图片OOBE ImageOOBE 页面上每个模块的头部图。说明图标与截图的具体设计由设计团队在应用内部保证一致性。如果你有关图标或截图的构想可以写在 PR 的 Additional Comments 部分供团队参考。9.3 文档提交新 PowerToy 需要两类文档开发者文档放在仓库/doc/devdocs/modules/如 doc/devdocs/modules/readme.md 目录面向开发者说明如何接手维护你的模块应涵盖架构、关键文件、测试与调试技巧Microsoft Learn 文档当模块准备合入 PowerToys 仓库时由内部团队成员编写面向用户的 Learn 文档。开发者在此步骤工作量不大但需留意 PR 动态及时补充团队索要的信息。10. 小结新模块接入检查单阶段关键动作验证方式设计确定 UI-only / 服务 / 混合 / 互操作类型找到最相似的现有模块作参照脚手架模板生成、替换占位符与 GUIDModuleTemplate.dll工程可编译注册加入 runner 的模块 DLL 清单与设置窗口映射在 src/runner/main.cpp 的knownModules与 settings_window.h 中搜到模块名服务独立工程先行TargetName命名ModuleSettings读设置服务可独立运行并读写%LOCALAPPDATA%\Microsoft\PowerToys\module\settings.json设置集成Properties/Settings/ViewModel/XAML/resw 五件套设置面板改动触发文件监听并写回 JSON调试runner 为启动项目CtrlAltP 附加服务RunnerLogs与模块Module\Service日志正常打包Module.wxsProduct.wxs引用 生成脚本条目安装包生成成功干净环境Windows Sandbox验证测试WinUI 单元测试 手动验证 OOBE 检查启用/禁用、唤醒恢复、新用户场景均通过如果你在使用过程中需要帮助按文档建议提一个带Needs-Team-Response标签的 issue 以获得团队关注。【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表