# Understanding TwoWayPipeMessageIPC Communication Between the PowerToys Runner and Modules

> Learn how TwoWayPipeMessageIPC enables bidirectional JSON communication between PowerToys Runner and modules using named pipes. Explore the C++ implementation and C# WinRT wrapper.

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

---

**The TwoWayPipeMessageIPC system uses paired named pipes to enable bidirectional JSON messaging between the PowerToys Runner and its UI modules, implemented in C++ and exposed through a WinRT wrapper for C# consumption.**

The microsoft/PowerToys repository implements a robust inter-process communication (IPC) mechanism to coordinate between the central Runner process and various UI modules such as Settings, Quick Access, and Workspaces. Understanding TwoWayPipeMessageIPC communication between the PowerToys Runner and modules is essential for developers extending PowerToys or debugging message flow issues.

## Architecture Overview

The IPC architecture relies on a named-pipe based, bidirectional channel. The **Runner** creates two named pipes: an **input pipe** for receiving messages and an **output pipe** for sending messages. Each **module** instantiates a `TwoWayPipeMessageIPCManaged` object using the corresponding pipe names, establishing a two-way communication channel that allows JSON-encoded messages to flow in both directions.

## Core Components

The implementation spans native C++ and managed C# layers:

| Component | Language | Role | Key File |
|-----------|----------|------|----------|
| `TwoWayPipeMessageIPC` | C++ | Low-level pipe handling (create, send, start, end) | [`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) |
| `TwoWayPipeMessageIPCManaged` | C++/WinRT | WinRT wrapper exposing `Start`, `Send`, `End`, `Close` | [`src/common/interop/TwoWayPipeMessageIPCManaged.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/interop/TwoWayPipeMessageIPCManaged.h) |
| Runner pipe creation | C++ | Instantiates pipes and dispatches messages | [`src/runner/settings_window.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/settings_window.cpp) |
| Settings UI | C# | Consumes the managed IPC | [`src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs`](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs) |

## How the Communication Works

1. **Pipe Creation**: The Runner creates two named pipes using `TwoWayPipeMessageIPC`.
2. **Module Connection**: The module creates a `TwoWayPipeMessageIPCManaged` instance with matching pipe names.
3. **Start**: Calling `Start()` initializes the native server on the input pipe and connects as a client to the output pipe.
4. **Message Sending**: `Send(message)` serializes the JSON string as UTF-16 and writes to the pipe.
5. **Message Receiving**: Incoming messages trigger the registered callback on the receiving thread.

## Implementation Details

### Runner Side Implementation

In [`src/runner/settings_window.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/settings_window.cpp), the Runner establishes the pipe pair:

```cpp
// src/runner/settings_window.cpp (excerpt)
std::wstring powertoys_pipe_name = L"\\\\.\\pipe\\powertoys_settings_ipc";
std::wstring settings_pipe_name = L"\\\\.\\pipe\\powertoys_settings_ui_ipc";

// Callback that will be invoked when the UI sends a message.
auto receive_json_send_to_main_thread = [](std::wstring json) {
    // Marshal onto the UI thread and process the JSON.
    // Example: parse the JSON and update the settings model.
};

TwoWayPipeMessageIPC* current_settings_ipc = nullptr;
current_settings_ipc = new TwoWayPipeMessageIPC(
    powertoys_pipe_name,
    settings_pipe_name,
    receive_json_send_to_main_thread);

// Start listening for messages from the UI.
current_settings_ipc->start(nullptr);

```

### Module Side Implementation

In [`src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs`](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs), the Settings UI connects to the Runner:

```csharp
// src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs (excerpt)
private static TwoWayPipeMessageIPCManaged ipcmanager;

// Called when Settings is launched by the Runner.
private void OnLaunchedFromRunner(string[] cmdArgs)
{
    // Pipe names must line‑up with the Runner's names.
    ipcmanager = new TwoWayPipeMessageIPCManaged(
        cmdArgs[(int)Arguments.SettingsPipeName],   // input pipe (runner → UI)
        cmdArgs[(int)Arguments.PTPipeName],          // output pipe (UI → runner)
        (string message) => {
            // This callback receives messages from the Runner.
            IPCMessageReceivedCallback?.Invoke(message);
        });

    // Begin the IPC session.
    ipcmanager.Start();
}

```

### Sending Messages

To send a message from the module to the Runner:

```csharp
// Register a hot‑key conflict handler (simplified)
GlobalHotkeyConflictManager.Initialize(message =>
{
    // Forward the conflict JSON to the Runner.
    ipcmanager.Send(message);
    return 0;
});

```

### Quick Access and Workspaces Examples

The Quick Access UI uses the same pattern in [`src/settings-ui/QuickAccess.UI/Services/QuickAccessCoordinator.cs`](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/QuickAccess.UI/Services/QuickAccessCoordinator.cs):

```csharp
// src/settings-ui/QuickAccess.UI/Services/QuickAccessCoordinator.cs (excerpt)
_ipcManager = new TwoWayPipeMessageIPCManaged(
    _launchContext.AppPipeName,   // Settings → QuickAccess output
    _launchContext.RunnerPipeName, // Settings ← QuickAccess input
    OnIpcMessageReceived);        // Callback for incoming messages

_ipcManager.Start();

```

Similarly, the Workspaces module implements this in [`src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs):

```csharp
// src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs (excerpt)
ipcmanager = new TwoWayPipeMessageIPCManaged(
    @"\\.\pipe\powertoys_workspaces_ui_",
    @"\\.\pipe\powertoys_workspaces_launcher_ui_",
    (string message) => { /* handle incoming JSON */ });

ipcmanager.Start();

```

## Security and Threading

The `TwoWayPipeMessageIPC` implementation in [`src/common/interop/two_way_pipe_message_ipc.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/common/interop/two_way_pipe_message_ipc.cpp) handles several critical concerns:

- **Security**: The pipe server runs under Medium integrity and optionally allows restricted tokens via `change_pipe_security_allow_restricted_token`.
- **Asynchronous Processing**: Uses `AsyncMessageQueue` with dedicated threads for input and output queue processing.
- **UTF-16 Encoding**: Messages are serialized as `std::wstring` to ensure Windows native string compatibility.

## Summary

- The **TwoWayPipeMessageIPC** system enables bidirectional JSON communication between the PowerToys Runner and UI modules.
- **Named pipes** provide the transport layer, with the Runner creating the pipe pair and modules connecting via `TwoWayPipeMessageIPCManaged`.
- **WinRT wrapping** allows C# modules to consume the native C++ implementation cleanly.

- **Security and threading** are handled internally, requiring no manual synchronization from callers.

## Frequently Asked Questions

### What is the purpose of TwoWayPipeMessageIPC in PowerToys?

The TwoWayPipeMessageIPC provides a secure, bidirectional communication channel between the PowerToys Runner process and its various UI modules. It allows the Runner to send commands and receive status updates using JSON messages over named pipes, enabling loose coupling between the background service and the user interface components.

### How do pipe names get coordinated between the Runner and modules?

The Runner generates unique pipe names (such as `\\.\pipe\powertoys_settings_ipc` and `\\.\pipe\powertoys_settings_ui_ipc`) and passes them as command-line arguments when launching the module process. The module extracts these names from `cmdArgs` and instantiates `TwoWayPipeMessageIPCManaged` with the corresponding input and output pipe names, ensuring both sides reference the same pipe endpoints.

### Is TwoWayPipeMessageIPC thread-safe for concurrent message sending?

Yes, the native implementation in [`two_way_pipe_message_ipc.cpp`](https://github.com/microsoft/PowerToys/blob/main/two_way_pipe_message_ipc.cpp) handles thread safety internally using `AsyncMessageQueue` and dedicated threads for input and output processing. C# callers can invoke `Send()` from any thread without manual synchronization, as the implementation queues messages and processes them asynchronously on background threads.

### Can third-party PowerToys modules use this IPC mechanism?

While the TwoWayPipeMessageIPC infrastructure is technically reusable, it is designed specifically for internal PowerToys communication between the Runner and official Microsoft modules. Third-party extensions would need to coordinate pipe naming conventions with the Runner and implement compatible JSON message protocols, which is not officially supported as a public API for external developers.