# How to Implement the PowertoyModuleIface Interface for a New PowerToys Utility

> Learn how to implement the PowertoyModuleIface interface for your new PowerToys utility. This guide covers DLL creation, interface derivation, and essential life-cycle methods like enable and disable.

- Repository: [Microsoft/PowerToys](https://github.com/microsoft/PowerToys)
- Tags: how-to-guide
- Published: 2026-02-25

---

**To implement the PowertoyModuleIface interface for a new PowerToys utility, create a DLL that exports `powertoy_create()`, derive a class from the abstract interface defined in [`src/modules/interface/powertoy_module_interface.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/interface/powertoy_module_interface.h), and implement the mandatory life-cycle methods including `enable()`, `disable()`, and `get_config()`.**

PowerToys utilities are dynamically loaded as DLLs by the **Runner** process at startup. Each module must expose a C-linkage factory function that instantiates an object implementing the `PowertoyModuleIface` abstract base class, enabling the Runner to manage the utility's lifecycle, settings, and hotkey registration through a standardized API.

## Understanding the PowertoyModuleIface Interface

### Core Interface Location and Purpose

The `PowertoyModuleIface` interface is defined in [`src/modules/interface/powertoy_module_interface.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/interface/powertoy_module_interface.h). This header establishes the contract between the PowerToys Runner and individual utilities. According to the Microsoft PowerToys source code, the Runner interacts with modules exclusively through this interface, treating each utility as a black box that responds to standard enable, disable, and configuration commands.

### Mandatory Methods to Implement

Every class deriving from `PowertoyModuleIface` must override these pure-virtual methods:

- **`get_name()`** – Returns the human-readable module name displayed in the Settings UI
- **`get_key()`** – Returns a unique identifier used as the JSON key for settings storage
- **`get_config()`** – Serializes the module's configuration schema to JSON for the Settings UI
- **`set_config()`** – Deserializes JSON from the Settings UI and persists values to disk
- **`enable()`** – Called when the user activates the utility; start background threads or hooks here
- **`disable()`** – Called when the user deactivates the utility; release resources here
- **`is_enabled()`** – Returns the current runtime enabled state
- **`destroy()`** – Self-deletion hook called by the Runner during shutdown

## Step-by-Step Implementation Guide

### Create the DLL Project Structure

Microsoft provides a **ModuleTemplate** in `tools/project_template/ModuleTemplate/` that preconfigures the correct project settings, export definitions, and folder layout. Use this template to ensure your DLL compiles with the proper runtime settings and linker options compatible with the PowerToys Runner.

The template includes boilerplate for registering an ETW trace provider in `DllMain()`, which allows your module to emit structured logs that PowerToys aggregates:

```cpp
BOOL APIENTRY DllMain(HMODULE, DWORD reason, LPVOID) {
    if (reason == DLL_PROCESS_ATTACH)   Trace::RegisterProvider();
    if (reason == DLL_PROCESS_DETACH)   Trace::UnregisterProvider();
    return TRUE;
}

```

### Implement the Factory Function

The Runner locates your module by looking up the `powertoy_create` symbol via `GetProcAddress`. You must export this function with C linkage to prevent name mangling:

```cpp
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() {
    return new MyNewUtility();   // MyNewUtility derives from PowertoyModuleIface
}

```

This factory is the sole entry point. The Runner calls it once during startup to obtain the module instance, then manages the object through the `PowertoyModuleIface` pointer.

### Handle Settings Persistence

Use the `PowerToysSettings` helper classes from [`src/common/SettingsAPI/settings_objects.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/SettingsAPI/settings_objects.h) to guarantee a JSON-based schema that the Settings UI can render. Implement `get_config()` to describe your configurable properties:

```cpp
virtual bool get_config(wchar_t* buffer, int* buffer_size) override {
    HINSTANCE hinst = reinterpret_cast<HINSTANCE>(&__ImageBase);
    PowerToysSettings::Settings settings(hinst, get_name());
    
    settings.add_bool_toggle(L"enabled", L"Enable feature", g_settings.enabled);
    settings.add_int_spinner(L"interval", L"Refresh interval", g_settings.interval, 1, 60, 1);
    
    return settings.serialize_to_buffer(buffer, buffer_size);
}

```

Implement `set_config()` to parse incoming JSON and persist values:

```cpp
virtual void set_config(const wchar_t* config) override {
    try {
        auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
        if (auto v = values.get_bool_value(L"enabled"))   g_settings.enabled = *v;
        if (auto v = values.get_int_value(L"interval"))   g_settings.interval = *v;
        values.save_to_settings_file();   // Writes to %LOCALAPPDATA%\PowerToys\
    } catch (...) { /* ignore malformed JSON */ }
}

```

### Add Hotkey Support (Optional)

To expose system-wide hotkeys that the Runner registers on your behalf, override `get_hotkeys()` and `on_hotkey()`:

```cpp
virtual size_t get_hotkeys(Hotkey* buffer, size_t buffer_sz) override {
    if (buffer && buffer_sz > 0) {
        buffer[0] = { .win = true, .ctrl = false, .shift = true, .alt = false,
                      .key = 'U', .id = 0, .isShown = true };  // Win+Shift+U
    }
    return 1; // Number of hotkeys defined
}

virtual bool on_hotkey(size_t hotkeyId) override {
    if (hotkeyId == 0) {
        // Execute action
        return true; // Swallow the keypress
    }
    return false;
}

```

Alternatively, implement `GetHotkeyEx()` and `OnHotkeyEx()` for extended registration options.

## Complete Code Example

The following trimmed implementation from [`tools/project_template/ModuleTemplate/dllmain.cpp`](https://github.com/microsoft/PowerToys/blob/main/tools/project_template/ModuleTemplate/dllmain.cpp) demonstrates the essential structure:

```cpp
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>

const static wchar_t* MODULE_NAME = L"MyNewUtility";

struct ModuleSettings {
    bool enabled = true;
    int refresh_interval = 5;
} g_settings;

class MyNewUtility : public PowertoyModuleIface {
    bool m_enabled = false;

    void init_settings() {
        try {
            auto values = PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
            if (auto v = values.get_bool_value(L"enabled"))           g_settings.enabled = *v;
            if (auto v = values.get_int_value(L"refresh_interval"))   g_settings.refresh_interval = *v;
        } catch (...) {}
    }

public:
    MyNewUtility() { init_settings(); }

    virtual const wchar_t* get_name() override { return MODULE_NAME; }
    virtual const wchar_t* get_key() override { return MODULE_NAME; }
    virtual void destroy() override { delete this; }

    virtual bool get_config(wchar_t* buffer, int* buffer_size) override {
        PowerToysSettings::Settings settings(reinterpret_cast<HINSTANCE>(&__ImageBase), get_name());
        settings.add_bool_toggle(L"enabled", L"Enable", g_settings.enabled);
        settings.add_int_spinner(L"refresh_interval", L"Interval (sec)", g_settings.refresh_interval, 1, 60, 1);
        return settings.serialize_to_buffer(buffer, buffer_size);
    }

    virtual void set_config(const wchar_t* config) override {
        auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
        if (auto v = values.get_bool_value(L"enabled"))           g_settings.enabled = *v;
        if (auto v = values.get_int_value(L"refresh_interval"))   g_settings.refresh_interval = *v;
        values.save_to_settings_file();
    }

    virtual void enable() override { m_enabled = true; }
    virtual void disable() override { m_enabled = false; }
    virtual bool is_enabled() const override { return m_enabled; }
};

extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() {
    return new MyNewUtility();
}

```

## Registering Your Module with the Runner

After building your DLL, add the filename to the Runner's loading list in [`src/runner/main.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/main.cpp). The Runner iterates through this list during startup, calling `LoadLibrary` and `powertoy_create()` for each entry. Without this registration step, your module will not be instantiated even if the DLL is present in the installation directory.

## Summary

- **Implement `PowertoyModuleIface`** by deriving from the abstract base class in [`src/modules/interface/powertoy_module_interface.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/interface/powertoy_module_interface.h)
- **Export `powertoy_create()`** as a C-linkage factory function returning your implementation
- **Override mandatory methods**: `get_name()`, `get_key()`, `get_config()`, `set_config()`, `enable()`, `disable()`, `is_enabled()`, and `destroy()`
- **Use `PowerToysSettings` helpers** from [`src/common/SettingsAPI/settings_objects.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/SettingsAPI/settings_objects.h) for JSON schema generation and persistence
- **Optionally implement hotkey methods** (`get_hotkeys()`, `on_hotkey()`) to receive global shortcut events
- **Register your DLL filename** in [`src/runner/main.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/main.cpp) so the Runner loads it at startup

## Frequently Asked Questions

### What is the minimum set of methods required to implement PowertoyModuleIface?

You must implement seven pure-virtual methods: `get_name()`, `get_key()`, `get_config()`, `set_config()`, `enable()`, `disable()`, and `destroy()`. Additionally, you must implement `is_enabled()` to track runtime state. These methods allow the Runner to identify your module, render its settings UI, and control its lifecycle.

### How does the PowerToys Runner load custom modules?

The Runner loads modules by searching for the `powertoy_create` symbol in DLLs listed in its internal registry within [`src/runner/main.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/main.cpp). It dynamically loads each DLL using `LoadLibrary`, retrieves the factory function via `GetProcAddress`, and calls it to obtain a `PowertoyModuleIface` instance. The Runner then manages this instance throughout the application lifecycle.

### Where are module settings stored on disk?

Settings are persisted as JSON files in the user's local application data folder (`%LOCALAPPDATA%\PowerToys\`). The `PowerToyValues::save_to_settings_file()` method writes to a file named `{module_key}-settings.json`, where `{module_key}` is the string returned by your `get_key()` implementation.

### Can a PowerToys module implement multiple hotkeys?

Yes. Return a count greater than one from `get_hotkeys()` and populate the buffer with multiple `Hotkey` structures, each with a unique `id` field. The `on_hotkey()` method receives the `id` of the triggered hotkey, allowing you to distinguish between different shortcuts within the same module.