How PowerToys Settings v2 Works: JSON Configuration and IPC Architecture

PowerToys Settings v2 stores module configurations as JSON files in %LOCALAPPDATA%\Microsoft\PowerToys\ and synchronizes changes between the Settings UI and the runner process via a named-pipe, two-way IPC channel.

The PowerToys Settings v2 system provides a robust architecture for managing module configurations through JSON persistence and real-time inter-process communication. This architecture separates the Settings UI process from the runner process while ensuring configuration changes propagate instantly across the application. Understanding how PowerToys Settings v2 handles JSON configuration and IPC is essential for developers extending PowerToys or debugging configuration issues.

JSON Configuration Storage and Retrieval

The SettingsRepository Pattern

PowerToys Settings v2 centralizes configuration access through the generic SettingsRepository<T> class located in src/settings-ui/Settings.UI.Library/SettingsRepository[1.cs`](https://github.com/microsoft/PowerToys/blob/main/1.cs). This repository implements a singleton pattern that caches settings objects in memory while coordinating persistence operations.

When a view model requests configuration data, SettingsRepository<T>.GetInstance(SettingsUtils.Default).GetSettings() returns the strongly-typed settings object. The generic type parameter T must implement ISettingsConfig, ensuring consistent serialization behavior across all PowerToys modules.

File I/O with SettingsUtils

The actual file operations occur in src/settings-ui/Settings.UI.Library/SettingsUtils.cs. The GetSettings<T>(string moduleName, string fileName) method constructs the path %LOCALAPPDATA%\Microsoft\PowerToys\<moduleName>\<fileName>.json, creating the file with default values if missing.

// Example: Loading FancyZones settings
var fancyZonesSettings = SettingsRepository<FancyZonesSettings>.GetInstance(
    SettingsUtils.Default).GetSettings();
bool isEnabled = fancyZonesSettings.Enabled;

This utility handles JSON deserialization using System.Text.Json, converting the flat file structure into the hierarchical C# objects used by the view models.

Two-Way IPC Communication Architecture

Sending Changes from Settings UI to Runner

When users modify settings through the XAML interface, view models serialize the entire configuration object and transmit it via a named pipe. The entry point for this operation is ShellPage.SendDefaultIPCMessage in src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs.

The MainWindow.xaml.cs file initializes the static callback ShellPage.DefaultSndMSGCallBack during application startup, establishing the communication channel with the runner process.

// ViewModel implementation pattern
private void OnEnabledChanged(bool newValue)
{
    _settings.Enabled = newValue;
    var payload = _settings.ToJsonString();
    _sendMessage(payload); // Calls ShellPage.SendDefaultIPCMessage
}

The JSON payload travels through the named pipe to the runner process, where Program.IPCMessageReceivedCallback handles the incoming message.

Receiving and Processing Messages in the Runner

The runner process registers a global callback in Program.IPCMessageReceivedCallback that parses the JSON and dispatches it to the appropriate module. Each module registers a handler in ShellPage.ShellHandler.IPCResponseHandleList during initialization.

// Runner-side message handling pattern
Program.IPCMessageReceivedCallback = (msg) =>
{
    var json = JsonObject.Parse(msg);
    foreach (var handle in ShellPage.ShellHandler.IPCResponseHandleList)
    {
        handle(json);
    }
};

For C++ modules, the handler invokes set_config as declared in src/modules/interface/powertoy_module_interface.h. C# modules like PowerRename implement SetConfig directly to parse the JSON and update internal state.

Bidirectional Communication and Feedback

The IPC channel supports full duplex communication. The runner can send status updates, hotkey conflict notifications, or enable/disable commands back to the Settings UI. These messages follow the same JSON format and travel through the identical named pipe infrastructure.

When the runner sends feedback, the registered handlers in IPCResponseHandleList update the view models, which then propagate changes to the XAML bindings. This ensures the UI remains synchronized with the actual module state regardless of which process initiated the change.

Data Flow and Persistence Lifecycle

The complete configuration lifecycle demonstrates how PowerToys Settings v2 maintains consistency across processes:

  1. Initial Load: SettingsRepository<T> reads JSON from disk via SettingsUtils when the Settings UI starts
  2. User Modification: View models update the in-memory settings object and serialize to JSON
  3. IPC Transmission: ShellPage.SendDefaultIPCMessage writes the JSON to the named pipe
  4. Runner Processing: Program.IPCMessageReceivedCallback parses the JSON and dispatches to the module's set_config or SetConfig method
  5. Module Application: The module applies the configuration changes to its internal state
  6. Disk Persistence: The runner may write the configuration back to disk through its own SettingsUtils instance
  7. Feedback Loop: Optional runner-to-UI messages update the view models to confirm application

This architecture ensures that the JSON files in %LOCALAPPDATA%\Microsoft\PowerToys\ always represent the source of truth, while the IPC channel provides real-time synchronization between the Settings UI and the runner process.

Implementation Examples

Loading Settings in a ViewModel

using Microsoft.PowerToys.Settings.UI.Library;

public class MyModuleViewModel
{
    private readonly MyModuleSettings _settings;
    
    public MyModuleViewModel()
    {
        _settings = SettingsRepository<MyModuleSettings>.GetInstance(
            SettingsUtils.Default).GetSettings();
    }
    
    public bool IsEnabled
    {
        get => _settings.Enabled;
        set
        {
            _settings.Enabled = value;
            SendSettingsUpdate();
        }
    }
    
    private void SendSettingsUpdate()
    {
        var json = _settings.ToJsonString();
        ShellPage.SendDefaultIPCMessage(json);
    }
}

Handling IPC in the Runner (C++)

// In the runner's message loop
void Runner::HandleIPCMessage(const std::wstring& message)
{
    Json::Value root;
    Json::Reader reader;
    
    if (!reader.parse(message, root))
        return;
        
    std::wstring moduleName = root[L"moduleName"].asString();
    
    if (moduleName == L"MyModule")
    {
        auto& module = modules().at(moduleName);
        module->set_config(root.toStyledString());
    }
}

Summary

  • JSON Persistence: PowerToys Settings v2 stores all module configurations as JSON files in %LOCALAPPDATA%\Microsoft\PowerToys\, accessed through the SettingsRepository<T> and SettingsUtils classes.
  • Two-Way IPC: The Settings UI communicates with the runner process via ShellPage.SendDefaultIPCMessage using a named pipe, while the runner dispatches messages through Program.IPCMessageReceivedCallback and IPCResponseHandleList.
  • Module Integration: Each module implements set_config (C++) or SetConfig (C#) to receive JSON updates, ensuring type-safe configuration application across the entire PowerToys ecosystem.

Frequently Asked Questions

How does PowerToys Settings v2 handle configuration file conflicts?

PowerToys Settings v2 prevents file contention through the SettingsUtils class, which manages all JSON read/write operations. When the Settings UI saves changes, it writes to disk immediately, then sends the JSON payload via IPC to the runner. The runner may persist the same configuration through its own SettingsUtils instance, ensuring both processes maintain synchronized file states without corruption.

What is the difference between Settings v1 and Settings v2 in PowerToys?

Settings v1 used a different persistence mechanism and IPC approach, while Settings v2 introduces a standardized JSON-based configuration system with strongly-typed ISettingsConfig interfaces. The v2 architecture centralizes settings access through SettingsRepository<T>, implements consistent two-way IPC via named pipes, and provides a modern XAML-based UI that communicates through ShellPage.SendDefaultIPCMessage rather than the legacy communication methods.

Can third-party PowerToys modules use the Settings v2 JSON and IPC system?

Yes, third-party modules can integrate with Settings v2 by implementing the ISettingsConfig interface for their configuration classes and registering with the SettingsRepository<T>. For IPC, modules must handle the set_config callback (C++) or SetConfig method (C#) to receive JSON updates from the runner, and can send status updates back through the same named pipe infrastructure used by built-in PowerToys modules.

How does the Settings UI know when a module has successfully applied configuration changes?

The Settings v2 IPC channel supports bidirectional communication, allowing the runner to send confirmation messages or status updates back to the Settings UI. When a module successfully applies configuration changes, the runner can dispatch a JSON message through the same named pipe, which triggers the registered handlers in IPCResponseHandleList. These handlers update the view models, which then propagate the confirmation to the XAML bindings, ensuring the UI reflects the actual module state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →