doc/devdocs/development/new-powertoy.md
First of all, thank you for wanting to contribute to PowerToys. The work we do would not be possible without the support of community supporters like you.
This guide documents the process of building a new PowerToys utility from scratch, including architecture decisions, integration steps, and common pitfalls.
A PowerToy module is a self-contained utility integrated into the PowerToys ecosystem. It can be UI-based, service-based, or both.
Follow the Getting Started guide to set up your development environment, then validate that you are able to build and run PowerToys.slnx.
Optional:
src/
modules/
your_module/
YourModule.sln
YourModuleInterface/
YourModuleUI/ (if needed)
YourModuleService/ (if needed)
Think about how your module works and which existing modules behave similarly. You are going to want to think about the UI needed for the application, the lifecycle, whether it is a service that is always running or event based. Below are some basic scenarios with some modules to explore. You can write your application in C++ or C#.
Begin by setting up the PowerToy module template project. This will generate boilerplate for you to begin your new module. Below are the key headers in the Module Interface (dllmain.cpp) and an explanation of their purpose:
struct ModuleSettings {};
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.
}
[!NOTE] Many of the class functions are boilerplate and need simple string replacements with your module name. The rest of the functions below will require bigger changes.
powertoys_gpo object to go to the definition and set up the getConfiguredModuleEnabledValue for your module.virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredModuleEnabledValue();
}
init_settings() initializes the settings for the interface. Will either pull from existing settings.json or use defaults.void ModuleInterface::init_settings()
get_config retrieves the settings from the settings.json file.virtual bool get_config(wchar_t* buffer, int* buffer_size) override
set_config sets the new settings to the settings.json file.virtual void set_config(const wchar_t* config) override
call_custom_action allows custom actions to be called based on signals from the settings app.void call_custom_action(const wchar_t* action) override
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.
// 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) override
/modules/<YourModule>common instead of cross-module dependenciessettings_objects.h in src\common\SettingsAPI.vcxproj and solution files.src/runner/modules.hsrc/runner/modules.cppsrc/runner/resource.hsrc/runner/settings_window.hsrc/runner/settings_window.cppsrc/runner/main.cppsrc/common/logger.h (for logging)ModuleInterface.dll. This will allow the runner to interact with your service.[!TIP] Mismatched module IDs are one of the most common causes of load failures. Keep your ID consistent across manifest, registry, and service.
This is going to look different for every PowerToy. It may be easier to develop the application independently, and then link in the PowerToys settings logic later. But you have to write the service first, before connecting it to the runner.
.rc file..vcxproj by setting the <TargetName><PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
<TargetName>PowerToys.LightSwitchService</TargetName>
</PropertyGroup>
.vcxproj, right click the item and select Unload projectModuleSettings::instance().InitFileWatcher();
ModuleSettings::instance().LoadSettings();
auto& settings = ModuleSettings::instance().settings();
These come from the ModuleSettings.h file that lives with the Service. You can copy this from another module (e.g., Light Switch) and adjust to fit your needs.
If your module has a user interface:
PowerToys settings are stored per-module as JSON under:
%LOCALAPPDATA%\Microsoft\PowerToys\<module>\settings.json
src\settings-ui\Settings.UI.Library\ create <module>Properties.cs and <module>Settings.cs<module>Properties.cs is where you will define your defaults. Every setting needs to be represented here. This should match what was set in the Module Interface.<module>Settings.csis where your settings.json will be built from. The structure should match the followingpublic ModuleSettings()
{
Name = ModuleName;
Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Properties = new ModuleProperties(); // settings properties you set above.
}
src\settings-ui\Settings.UI\ViewModels create <module>ViewModel.cs this is where the interaction happens between your settings page in the PowerToys app and the settings file that is stored on the device. Changes here will trigger the settings watcher via a NotifyPropertyChanged event.SettingsPage.xaml at src\settings-ui\Settings.UI\SettingsXAML\Views. This will be the page where the user interacts with the settings of your module.x:Uid connects to Resources.resw)// LightSwitch.xaml
<ComboBoxItem
x:Uid="LightSwitch_ModeOff"
AutomationProperties.AutomationId="OffCBItem_LightSwitch"
Tag="Off" />
// Resources.resw
<data name="LightSwitch_ModeOff.Content" xml:space="preserve">
<value>Off</value>
</data>
[!IMPORTANT] In the above example we use
.Contentto target the content of the Combobox. This can change per UI element (e.g.,.Text,.Header, etc.)
Reminder: Manual changes via external editors (VS Code, Notepad) do not trigger the settings watcher. Only changes written through PowerToys trigger reloads.
DispatcherQueue when updating UI from non-UI threads.%LOCALAPPDATA%\Microsoft\PowerToys\RunnerLogs and %LOCALAPPDATA%\Microsoft\PowerToys\Module\Service\<version> for the specific module.[!TIP] PowerToys caches
.nugetartifacts aggressively. Usegit clean -xfdwhen builds behave unexpectedly.
WixToolset.Heat for Wix5 via nugetinstaller\PowerToysInstallerVNext add a new file for your module: Module.wxs<!--ModuleNameFiles_Component_Def--> which is a placeholder for code that will be generated by generateFileComponents.ps1.Product.wxs add a line item in the <Feature Id="CoreFeature" ... > section. It will look like a list of <ComponentGroupRef Id="ModuleComponentGroup" /> items.generateFileComponents.ps1 you will need to add an entry to the bottom for your new module. It will follow the following format. -fileListName <Module>Files will match the string you set in Module.wxs, <ModuleServiceName> will match the name of your exe.# Module Name
Generate-FileList -fileDepsJson "" -fileListName <Module>Files -wxsFilePath $PSScriptRoot\<Module>.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\<ModuleServiceName>"
Generate-FileComponents -fileListName "<Module>Files" -wxsFilePath $PSScriptRoot\<Module>.wxs -regroot $registryroot
/modules/<YourModule>/Tests%LOCALAPPDATA%\MicrosoftIf your module has a shortcut, ensure that it is properly registered following the steps listed in the documentation for conflict detection.
The OOBE page is a custom settings page that gives the user at a glance information about each module. This window opens before the Settings application for new users and after updates. Create OOBE<ModuleName>.xaml at src\settings-ui\Settings.UI\SettingsXAML\OOBE\Views. You will also need to add your module name to the enum at src\settings-ui\Settings.UI\OOBE\Enums\PowerToysModules.cs.
Now that your PowerToy is done you can start to think about the assets that will represent your module.
[!NOTE] This step is something that the Design team will handle internally to ensure consistency throughout the application. If you have ideas or recommendations on what the icon or screenshots should be for your module feel free to leave it in the "Additional Comments" section of the PR and the team will take it into consideration.
There are two types of documentation that will be required when submitting a new PowerToy:
/doc/devdocs/modules/ and should tell a developer how to work on your app. It should outline the module architecture, key files, testing, and tips on debugging if necessary.Thank you again for contributing! If you need help, feel free to open an issue and use the Needs-Team-Response label so we know you need attention.