README.md
1 # PowerToy DLL Project For Visual Studio 2022 and 2026 2 3 ## Installation 4 5 - Put the `ModuleTemplate.zip` file inside the `%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates\` folder for VS 2022, or `%USERPROFILE%\Documents\Visual Studio 18\Templates\ProjectTemplates\` folder for VS 2026, which is the default *User project templates location*. You can change that location via `Tools > Options > Projects and Solutions`. 6 - The template will be available in Visual Studio, when adding a new project, under the `Visual C++` tab. 7 8 ## Contributing 9 If you'd like to work on a PowerToy template, make required modifications to `\tools\project_template\ModuleTemplate.vcxproj` and then use the dedicated solution `PowerToyTemplate.sln` to export it as a template. Note that `ModuleTemplate.vcxproj` is actually a project template, therefore uncompilable, so we also have a dedicated `ModuleTemplateCompileTest.vcxproj` project referenced from the `PowerToys.slnx` to help keeping the template sources up to date and verify it compiles correctly. 10 11 ## Create a new PowerToy Module 12 13 - Add the new PowerToy project to the `src\modules\` folder for all the relative paths to work. 14 - For the module interface implementation take a look at [the interface](/src/modules/interface). 15 - Each PowerToy is built as a DLL and in order to be loaded at run-time, the PowerToy's DLL name needs to be added to the `known_dlls` map in [src/runner/main.cpp](/src/runner/main.cpp). 16 17 ## DPI Awareness 18 19 All PowerToy modules need to be DPI aware and calculate dimensions and positions of the UI elements using the Windows API for DPI awareness. 20 The `/src/common` library has some helpers that you can use and extend: 21 - [`dpi_aware.h`](/src/common/dpi_aware.h), [`dpi_aware.cpp`](/src/common/dpi_aware.cpp) 22 - [`monitors.h`](/src/common/monitors.h), [`monitors.cpp`](/src/common/monitors.cpp) 23 24 ## PowerToy settings 25 26 ### Settings architecture overview 27 28 PowerToys provides a settings infrastructure to add a settings page for new modules. The PowerToys Settings application is accessed from the PowerToys tray icon, it provides a global settings page and a dedicated settings page for each module. 29 30 The PowerToys settings API provides a way to define the required information and controls for the module's settings page and methods to read and persist the settings values. A module may need a more complex way to configure the user's preferences, in that case it can provide its own custom settings editor that can be invoked from the module's settings page through a dedicated button. 31 32 The settings specification can be read at [doc/specs/PowerToys-settings.md](/doc/specs/PowerToys-settings.md). 33 34 A PowerToy can provide this general information about itself: 35 - **[name](#name)**: The name of the PowerToy. 36 - **[description](#description)**: A text describing the PowerToy. 37 - **[icon_key](#icon_key)**: The identifier of the PowerToy icon in the [`settings-web` project](/src/settings-web#updating-the-icons). 38 - **[overview_link](#overview_link)**: A link to an extended overview of the PowerToy. 39 - **[video_link](#video_link)**: A link to a video showcasing the PowerToy. 40 41 A PowerToy can define settings of the following types: 42 - **[bool_toggle](#bool_toggle)**: A boolean property, edited with a Toggle control. 43 - **[int_spinner](#int_spinner)**: An integer property, edited with a Spinner control. 44 - **[string](#string)**: A string property, edited with a TextBox control. 45 - **[color_picker](#color_picker)**: A color property, edited with a ColorPicker control. 46 - **[custom_action](#custom_action)**: A custom action property, invoked from the settings by a Button control. 47 48 Here's an example of what the settings look like in the Settings screen: 49 50  51 52 ### How to add your module's settings page 53 54 The PowerToy can set its settings information and controls by overriding the [PowerToy's Interface `get_config` method](/src/modules/interface/README.md#get_config) and returning a serialized [`PowerToysSettings::Settings`](/src/common/settings_object.h) object that's been filled with the required information and controls. 55 56 The PowerToy can receive the new values by overriding the [PowerToy's Interface `set_config` method](/src/modules/interface/README.md#set_config), parsing the serialized [`PowerToysSettings::PowerToyValues`](/src/common/settings_object.h) object and applying the new settings. 57 58 Here's an example settings implementation: 59 ```cpp 60 // Return JSON with the configuration options. 61 virtual bool get_config(wchar_t* buffer, int* buffer_size) override { 62 HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase); 63 64 // Create a Settings object. 65 PowerToysSettings::Settings settings(hinstance, get_name()); 66 settings.set_description(L"Serves as an example powertoy, with example settings."); 67 68 // Show an overview link in the Settings page 69 settings.set_overview_link(L"https://github.com/microsoft/PowerToys"); 70 71 // Show a video link in the Settings page. 72 settings.set_video_link(L"https://www.youtube.com/watch?v=d3LHo2yXKoY&t=21462"); 73 74 // Add a bool property with a toggle editor. 75 settings.add_bool_toggle( 76 L"test_bool_toggle", // property name. 77 L"This is what a BoolToggle property looks like", // description or resource id of the localized string. 78 g_settings.test_bool_prop // property value. 79 ); 80 81 // Add an integer property with a spinner editor. 82 settings.add_int_spinner( 83 L"test_int_spinner", // property name 84 L"This is what a IntSpinner property looks like", // description or resource id of the localized string. 85 g_settings.test_int_prop, // property value. 86 0, // min value. 87 100, // max value. 88 10 // incremental step. 89 ); 90 91 // Add a string property with a textbox editor. 92 settings.add_string( 93 L"test_string_text", // property name. 94 L"This is what a String property looks like", // description or resource id of the localized string. 95 g_settings.test_string_prop // property value. 96 ); 97 98 // Add a string property with a color picker editor. 99 settings.add_color_picker( 100 L"test_color_picker", // property name. 101 L"This is what a ColorPicker property looks like", // description or resource id of the localized string. 102 g_settings.test_color_prop // property value. 103 ); 104 105 // Add a custom action property. When using this settings type, the "PowertoyModuleIface::call_custom_action()" 106 // method should be overridden as well. 107 settings.add_custom_action( 108 L"test_custom_action", // action name. 109 L"This is what a CustomAction property looks like", // label above the field. 110 L"Call a custom action", // button text. 111 L"Press the button to call a custom action in the Example PowerToy" // display values / extended info. 112 ); 113 114 return settings.serialize_to_buffer(buffer, buffer_size); 115 } 116 117 // Called by the runner to pass the updated settings values as a serialized JSON. 118 virtual void set_config(const wchar_t* config) override { 119 try { 120 // Parse the input JSON string. 121 PowerToysSettings::PowerToyValues values = 122 PowerToysSettings::PowerToyValues::from_json_string(config); 123 124 // Update the bool property. 125 if (values.is_bool_value(L"test_bool_toggle")) { 126 g_settings.test_bool_prop = values.get_bool_value(L"test_bool_toggle"); 127 } 128 129 // Update the int property. 130 if (values.is_int_value(L"test_int_spinner")) { 131 g_settings.test_int_prop = values.get_int_value(L"test_int_spinner"); 132 } 133 134 // Update the string property. 135 if (values.is_string_value(L"test_string_text")) { 136 g_settings.test_string_prop = values.get_string_value(L"test_string_text"); 137 } 138 139 // Update the color property. 140 if (values.is_string_value(L"test_color_picker")) { 141 g_settings.test_color_prop = values.get_string_value(L"test_color_picker"); 142 } 143 144 // If you don't need to do any custom processing of the settings, proceed 145 // to persists the values calling: 146 values.save_to_settings_file(); 147 // Otherwise call a custom function to process the settings before saving them to disk: 148 // save_settings(); 149 } 150 catch (std::exception& ex) { 151 // Improper JSON. 152 } 153 } 154 ``` 155 156 ### Settings Informations 157 The PowerToys settings object supports adding additional information to a PowerToys Settings description: 158 159 #### name 160 The name of the PowerToy. Its a required information that's applied in the settings object constructor: 161 162 ```cpp 163 PowerToysSettings::Settings settings(hinstance, get_name()); 164 ``` 165 166 #### description 167 A short description of the PowerToy. 168 169 ```cpp 170 settings.set_description(L"Serves as an example powertoy, with example settings."); 171 ``` 172 or 173 ```cpp 174 settings.set_description(description_resource_id); 175 ``` 176 where `description_resource_id` is the UINT index of a resource string in the project .rc file. 177 178 #### icon_key 179 The identifier of the PowerToy icon in the [`settings-web` project](/src/settings-web#updating-the-icons). 180 By default, a `CircleRing` icon from *FabricUI* is shown for the PowerToy if no icon is specified. 181 182 ```cpp 183 settings.set_icon_key(L"pt-shortcut-guide"); 184 ``` 185 186 #### overview_link 187 A link to an extended overview of the PowerToy. 188 ```cpp 189 settings.set_overview_link(L"https://github.com/microsoft/PowerToys"); 190 ``` 191 192 193 #### video_link 194 A link to a video showcasing the PowerToy. 195 196 ```cpp 197 settings.set_video_link(L"https://www.youtube.com/watch?v=d3LHo2yXKoY&t=21462"); 198 ``` 199 200 ### Setting Controls 201 202 #### bool_toggle 203 204 A boolean property, edited with a Toggle control. 205 206 It can be added to a `Settings` object by calling `add_bool_toggle`. 207 ```cpp 208 // Add a bool property with a toggle editor. 209 settings.add_bool_toggle( 210 L"test_bool_toggle", // property name. 211 L"This is what a BoolToggle property looks like", // description or resource id of the localized string. 212 g_settings.test_bool_prop // property value. 213 ); 214 ``` 215 216 It can be read from a `PowerToyValues` object by calling `get_bool_value`. 217 ```cpp 218 // Update the bool property. 219 if (values.is_bool_value(L"test_bool_toggle")) { 220 g_settings.test_bool_prop = values.get_bool_value(L"test_bool_toggle"); 221 } 222 ``` 223 224 #### int_spinner 225 226 An integer property, edited with a Spinner control. 227 228 It can be added to a `Settings` object by calling `add_int_spinner`. 229 ```cpp 230 // Add an integer property with a spinner editor. 231 settings.add_int_spinner( 232 L"test_int_spinner", // property name 233 L"This is what a IntSpinner property looks like", // description or resource id of the localized string. 234 g_settings.test_int_prop, // property value. 235 0, // min value. 236 100, // max value. 237 10 // incremental step. 238 ); 239 ``` 240 241 It can be read from a `PowerToyValues` object by calling `get_int_value`. 242 ```cpp 243 // Update the int property. 244 if (values.is_int_value(L"test_int_spinner")) { 245 g_settings.test_int_prop = values.get_int_value(L"test_int_spinner"); 246 } 247 ``` 248 249 #### string 250 251 A string property, edited with a TextBox control. 252 253 It can be added to a `Settings` object by calling `add_string`. 254 ```cpp 255 // Add a string property with a textbox editor. 256 settings.add_string( 257 L"test_string_text", // property name. 258 L"This is what a String property looks like", // description or resource id of the localized string. 259 g_settings.test_string_prop // property value. 260 ); 261 ``` 262 263 It can be read from a `PowerToyValues` object by calling `get_string_value`. 264 ```cpp 265 // Update the string property. 266 if (values.is_string_value(L"test_string_text")) { 267 g_settings.test_string_prop = values.get_string_value(L"test_string_text"); 268 } 269 ``` 270 271 #### color_picker 272 273 A color property, edited with a ColorPicker control. Its value is a string with the `'#RRGGBB'` format, with two hexadecimal digits for each color component. 274 275 It can be added to a `Settings` object by calling `add_color_picker`. 276 ```cpp 277 // Add a string property with a color picker editor. 278 settings.add_color_picker( 279 L"test_color_picker", // property name. 280 L"This is what a ColorPicker property looks like", // description or resource id of the localized string. 281 g_settings.test_color_prop // property value. 282 ); 283 ``` 284 285 The `'#RRGGBB'`-format string can be read from a `PowerToyValues` object by calling `get_string_value`. 286 ```cpp 287 // Update the color property. 288 if (values.is_string_value(L"test_color_picker")) { 289 g_settings.test_color_prop = values.get_string_value(L"test_color_picker"); 290 } 291 ``` 292 293 #### custom_action 294 A custom action property, invoked from the settings by a Button control. This can be used to spawn a custom editor by the PowerToy. 295 296 It can be added to a `Settings` object by calling `add_custom_action`. 297 ```cpp 298 // Add a custom action property. When using this settings type, the "PowertoyModuleIface::call_custom_action()" 299 // method should be overridden as well. 300 settings.add_custom_action( 301 L"test_custom_action", // action name. 302 L"This is what a CustomAction property looks like", // label above the field: a string literal or a resource id 303 L"Call a custom action", // button text: a string literal or a resource id 304 L"Press the button to call a custom action in the Example PowerToy" // display values / extended info: a string literal or a resource id 305 ); 306 ``` 307 308 When the custom action button is pressed, the PowerToy's `call_custom_action()` is called with a serialized [`PowerToysSettings::CustomActionObject`](/src/common/settings_object.h) object. 309 ```cpp 310 // Signal from the Settings editor to call a custom action. 311 // This can be used to spawn more complex editors. 312 virtual void call_custom_action(const wchar_t* action) override { 313 static UINT custom_action_num_calls = 0; 314 try { 315 // Parse the action values, including name. 316 PowerToysSettings::CustomActionObject action_object = 317 PowerToysSettings::CustomActionObject::from_json_string(action); 318 319 if (action_object.get_name() == L"test_custom_action") { 320 // Custom action code to increase and show a counter. 321 ++custom_action_num_calls; 322 std::wstring msg(L"I have been called "); 323 msg += std::to_wstring(custom_action_num_calls); 324 msg += L" time(s)."; 325 MessageBox(NULL, msg.c_str(), L"Custom action call.", MB_OK | MB_TOPMOST); 326 } 327 } 328 catch (std::exception& ex) { 329 // Improper JSON. 330 } 331 } 332 ``` 333 334 ### Settings Persistence 335 336 By default, the PowerToys settings are persisted in the User's `%LocalAppData%\Microsoft\PowerToys` path. 337 Each PowerToy has its own folder for saving the persisted settings data. 338 339 Loading and saving the settings in the default location can be achieved through the use of a [`PowerToysSettings::PowerToyValues`](/src/common/settings_object.h) object. 340 341 #### Loading settings 342 343 The PowerToy can load the saved `PowerToyValues` object through the use of the `load_from_settings_file` method. 344 345 Here's an example: 346 ```cpp 347 // Load the settings file. 348 void ExamplePowertoy::init_settings() { 349 try { 350 // Load and parse the settings file for this PowerToy. 351 PowerToysSettings::PowerToyValues settings = 352 PowerToysSettings::PowerToyValues::load_from_settings_file(get_name()); 353 354 // Load the bool property. 355 if (settings.is_bool_value(L"test_bool_toggle")) { 356 g_settings.test_bool_prop = settings.get_bool_value(L"test_bool_toggle"); 357 } 358 359 // Load the int property. 360 if (settings.is_int_value(L"test_int_spinner")) { 361 g_settings.test_int_prop = settings.get_int_value(L"test_int_spinner"); 362 } 363 364 // Load the string property. 365 if (settings.is_string_value(L"test_string_text")) { 366 g_settings.test_string_prop = settings.get_string_value(L"test_string_text"); 367 } 368 369 // Load the color property. 370 if (settings.is_string_value(L"test_color_picker")) { 371 g_settings.test_color_prop = settings.get_string_value(L"test_color_picker"); 372 } 373 } 374 catch (std::exception& ex) { 375 // Error while loading from the settings file. Let default values stay as they are. 376 } 377 } 378 ``` 379 380 #### Saving settings 381 382 The PowerToy can save the `PowerToyValues` object received in `set_config` through the use of the `save_to_settings_file` method. 383 384 Here's an example: 385 ```cpp 386 // Called by the runner to pass the updated settings values as a serialized JSON. 387 virtual void set_config(const wchar_t* config) override { 388 try { 389 // Parse the input JSON string. 390 PowerToysSettings::PowerToyValues values = 391 PowerToysSettings::PowerToyValues::from_json_string(config); 392 ... 393 values.save_to_settings_file(); 394 } 395 catch (std::exception& ex) { 396 // Improper JSON. 397 } 398 } 399 ``` 400 401 Alternatively, the `PowerToyValues` object can be built manually and then saved if more complex logic is needed: 402 ```cpp 403 // This method of saving the module settings is only required if you need to do any 404 // custom processing of the settings before saving them to disk. 405 void ExamplePowertoy::save_settings() { 406 try { 407 // Create a PowerToyValues object for this PowerToy 408 PowerToysSettings::PowerToyValues values(get_name()); 409 410 // Save the bool property. 411 values.add_property( 412 L"test_bool_toggle", // property name 413 g_settings.test_bool_prop // property value 414 ); 415 416 // Save the int property. 417 values.add_property( 418 L"test_int_spinner", // property name 419 g_settings.test_int_prop // property value 420 ); 421 422 // Save the string property. 423 values.add_property( 424 L"test_string_text", // property name 425 g_settings.test_string_prop // property value 426 ); 427 428 // Save the color property. 429 values.add_property( 430 L"test_color_picker", // property name 431 g_settings.test_color_prop // property value 432 ); 433 434 // Save the PowerToyValues JSON to the power toy settings file. 435 values.save_to_settings_file(); 436 } 437 catch (std::exception& ex) { 438 // Couldn't save the settings. 439 } 440 } 441 ``` 442 443 ## Add a new PowerToy to the Installer 444 445 In the `installer` folder, open the `PowerToysSetup.slnx` solution. 446 Under the `PowerToysSetup` project, edit `Product.wxs`. 447 You will need to add a component for your module DLL. Search for `Module_ShortcutGuide` to see where to add the component declaration and where to reference that declaration so the DLL is added to the installer. 448 Each component requires a newly generated GUID (you can use the Visual Studio integrated tool to generate one). 449 Repeat the process for each extra file your PowerToy module requires. 450 If your PowerToy comes with a subfolder containing for example images, follow the example of the `PowerToysSvgs` component.