# Understanding the Differences Between Simple, External Application, and Context Handler Module Patterns in PowerToys

> Explore the distinctions between Simple, External Application, and Context Handler module patterns in PowerToys. Understand how each integrates with the runner and File Explorer for enhanced functionality.

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

---

**PowerToys implements each utility as a module conforming to the `PowertoyModuleIface` interface, with Simple modules running in-process within the runner, External Application modules spawning separate UI processes via named-pipe IPC, and Context Handler modules registering as COM shell extensions that integrate directly into File Explorer.**

The Microsoft PowerToys repository organizes its utilities into distinct architectural patterns that determine how each tool presents its interface and interacts with Windows. Understanding these differences between Simple modules, External Application modules, and Context Handler modules is essential for contributors extending the PowerToys ecosystem or optimizing existing utilities.

## The PowerToys Module Interface Foundation

Every PowerToys utility, regardless of pattern, implements the **`PowertoyModuleIface`** contract defined in [`src/modules/interface/powertoy_module_interface.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/interface/powertoy_module_interface.h). This interface standardizes lifecycle management across all modules, requiring implementations to handle hot-key registration, enable/disable toggling, and telemetry reporting through uniform methods.

The runner process, implemented in [`src/runner/main.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/main.cpp), discovers and loads each module DLL via `LoadLibrary`, then invokes the standard interface methods to manage utility state. While this loading mechanism remains consistent, the architectural pattern determines what happens when `Enable()` is called.

## Simple Modules vs External Application Modules

### Simple Modules (In-Process Execution)

**Simple modules** execute entirely within the runner process. These utilities perform background tasks or lightweight operations without spawning separate UI windows. The module's logic runs in-process, communicating directly with the runner through the standard interface callbacks.

### External Application Modules (Out-of-Process UI)

**External Application modules** follow a distinct pattern where the module DLL lives in the runner process, but activating the utility spawns a **separate standalone application** process. This pattern supports utilities requiring complex UI frameworks like WPF or WinUI 3 that run independently of the runner.

The **Color Picker** utility exemplifies this pattern. When enabled, the module creates a shared system event that the external UI process monitors:

```cpp
// src/modules/colorPicker/ColorPicker/ColorPicker.cpp
bool ColorPicker::Enable(bool isEnabled) noexcept {
    if (isEnabled) {
        // Signal the UI process to show the picker
        SignalEventAsync(Constants::ShowColorPickerSharedEvent(), L"Color Picker");
    }
    return true;
}

```

The UI application, implemented in C# within [`src/modules/colorPicker/ColorPickerUI/Program.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/colorPicker/ColorPickerUI/Program.cs), waits for this event before displaying the interface:

```csharp
// src/modules/colorPicker/ColorPickerUI/Program.cs
private static void Main() {
    var watcher = new EventWaitHandle(false, EventResetMode.AutoReset, 
        Constants.ShowColorPickerSharedEvent());
    watcher.WaitOne();          // blocks until the runner signals
    ShowColorPickerWindow();   // launch the UI
}

```

Communication between the runner and the external UI process occurs via **named-pipe IPC** implemented in [`src/common/interop/two_way_pipe_message_ipc.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/interop/two_way_pipe_message_ipc.h):

```cpp
// src/common/interop/two_way_pipe_message_ipc.h
class TwoWayPipeMessageIPC {
public:
    void SendMessage(const std::wstring& msg);   // used by UI to push telemetry
    std::wstring ReceiveMessage();               // runner reads commands
};

```

## Context Handler Modules

**Context Handler modules** register as **Windows shell extensions** that integrate directly into File Explorer's right-click context menu. Unlike External Application modules that spawn independent processes, Context Handlers implement COM interfaces such as `IContextMenu` and `IShellExtInit`, running inside the Explorer process when users right-click files.

The **Power Rename** utility demonstrates this pattern. During enablement, the module writes registry keys to register the COM class:

```cpp
// src/modules/powerrename/PowerRenameModule.cpp
bool PowerRenameModule::Enable(bool isEnabled) noexcept {
    if (isEnabled) {
        // Register the shell extension under HKCR\*\shellex\ContextMenuHandlers
        RegSetKeyValue(HKEY_CLASSES_ROOT, 
            L"*\\shellex\\ContextMenuHandlers\\PowerRename", 
            nullptr, REG_SZ, CLSID_PowerRename, 
            wcslen(CLSID_PowerRename) * sizeof(wchar_t));
    } else {
        // Remove registration
        RegDeleteKey(HKEY_CLASSES_ROOT, 
            L"*\\shellex\\ContextMenuHandlers\\PowerRename");
    }
    return true;
}

```

The COM class implementing the context menu behavior resides in [`src/modules/powerrename/PowerRenameContextMenu.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/powerrename/PowerRenameContextMenu.cpp):

```cpp
// src/modules/powerrename/PowerRenameContextMenu.cpp
class PowerRenameContextMenu : public IContextMenu, public IShellExtInit {
public:
    // IShellExtInit
    HRESULT Initialize(LPCITEMIDLIST pidlFolder, IDataObject* pDataObj, 
                       HKEY hProgID) {
        // Store selected files from pDataObj for later use
        return S_OK;
    }

    // IContextMenu
    HRESULT QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst,
                             UINT idCmdLast, UINT uFlags) {
        InsertMenu(hMenu, indexMenu, MF_STRING, idCmdFirst, L"Power Rename");
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
    }

    HRESULT InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo) {
        // Launch the PowerRename UI (separate EXE) with the selected files
        ShellExecute(nullptr, L"open", L"PowerToys.PowerRename.exe", 
                     selectedFiles, nullptr, SW_SHOWNORMAL);
        return S_OK;
    }
};

```

Standard COM registration functions handle DLL registration:

```cpp
// src/modules/powerrename/DllMain.cpp
STDAPI DllRegisterServer() {
    // Register CLSID_PowerRename with Windows
    return S_OK;
}
STDAPI DllUnregisterServer() {
    // Cleanup registration keys
    return S_OK;
}

```

When users select **Power Rename** from the context menu, the shell extension launches the separate `PowerToys.PowerRename.exe` process with the selected file paths, combining the Context Handler pattern with External Application UI presentation.

## Key Technical Distinctions

| Aspect | Simple/External Application | Context Handler |
|---|---|---|
| **Process Model** | Runner loads module DLL; External Apps spawn separate UI processes (`PowerToys.ColorPickerUI.exe`). | Runner writes registry; Explorer loads DLL **in-process** via COM. |
| **UI Location** | Independent window using WPF/WinUI 3 frameworks. | Integrated into Explorer's right-click menu; may launch dialog via `ShellExecute`. |
| **IPC Mechanism** | **Named pipe** (`TwoWayPipeMessageIPC`) for settings, telemetry, and hot-key events. | COM interfaces (`IContextMenu`, `IShellExtInit`); telemetry reported back via IPC layer. |
| **Registration** | DLL loaded dynamically by runner; UI binary packaged alongside. | Registry keys written to `HKCR\*\shellex\ContextMenuHandlers` or MSIX manifest entries. |
| **Lifetime** | UI process lives only during user interaction. | Shell extension instantiated per right-click; persists with Explorer process. |
| **Performance Impact** | Isolated process; no direct impact on Explorer stability. | Runs in Explorer process; must be lightweight to avoid shell performance degradation. |

## Summary

- **Simple modules** execute entirely within the PowerToys runner process, suitable for background tasks without complex UI requirements.
- **External Application modules** separate the UI into standalone executables that communicate with the runner via named-pipe IPC, enabling rich WPF or WinUI 3 interfaces while maintaining process isolation.
- **Context Handler modules** implement COM shell extensions that integrate directly into File Explorer's context menu, running in-process to provide immediate file operation access, often launching External Application UIs when user interaction is required.
- The choice between patterns depends on **UI complexity**, **process isolation requirements**, and **integration points** with the Windows shell.

## Frequently Asked Questions

### What is the main difference between External Application and Context Handler modules in PowerToys?

**External Application modules** spawn a separate process for the UI that runs independently of File Explorer, communicating with the PowerToys runner via named pipes. **Context Handler modules** register as COM objects that Explorer loads directly into its process to extend the right-click context menu, providing tighter integration with the file system but requiring careful memory management since they run in-process.

### How does PowerToys handle IPC between the runner and External Application modules?

PowerToys uses the **`TwoWayPipeMessageIPC`** class defined in [`src/common/interop/two_way_pipe_message_ipc.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/interop/two_way_pipe_message_ipc.h) to establish named-pipe communication channels. The runner and the external UI process exchange JSON messages for settings synchronization, telemetry reporting, and hot-key activation events, ensuring the utility remains responsive while isolated in its own process.

### Can a Context Handler module also use an External Application UI?

Yes, many Context Handler modules launch External Application UIs when user interaction is required. For example, the **Power Rename** context handler collects selected files via the `IContextMenu` interface running in-process, then calls `ShellExecute` to launch `PowerToys.PowerRename.exe` with those file paths as arguments. This hybrid approach combines the immediacy of shell integration with the flexibility of a standalone UI application.

### What are the performance implications of choosing between these module patterns?

**Simple modules** have minimal overhead since they run entirely within the runner process. **External Application modules** consume additional memory for the separate process but isolate UI complexity and potential crashes from the runner and Explorer. **Context Handler modules** must be lightweight because they run inside the Explorer process; poorly implemented handlers can slow down right-click menus or destabilize the shell, requiring strict adherence to COM threading rules and efficient resource management.