/ tools / project_template / README.md
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  ![Image of the Options](/doc/images/settings/example_settings.png)
 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.