# How Windows Terminal Handles Pseudoconsole (ConPTY): Architecture and Implementation

> Discover how Windows Terminal leverages ConPTY to isolate applications and enable real-time UI operations. Learn about its architecture and pipe management for seamless console experiences.

- Repository: [Microsoft/terminal](https://github.com/microsoft/terminal)
- Tags: architecture
- Published: 2026-02-26

---

**Windows Terminal uses a dedicated ConPTY subsystem to spawn headless console host processes, managing three asynchronous pipes (input, output, and signal) to isolate console applications from the UI while enabling real-time resize, clear, and re-parent operations.**

Windows Terminal leverages the **pseudoconsole (ConPTY)** subsystem to embed traditional console applications within its modern UI framework. According to the `microsoft/terminal` source code, this architecture launches a hidden console host process and bridges it to the Terminal UI through a specialized C API exported from the **winconpty** library. The implementation ensures complete isolation between the console engine and the terminal frontend while maintaining full compatibility with existing command-line tools.

## ConPTY Architecture Overview

Windows Terminal treats ConPTY as a separate console-host process that runs in the background. The system creates a **headless** `conhost.exe` (or `OpenConsole.exe` when packaged with Terminal) and establishes three communication channels: an input pipe, an output pipe, and a dedicated **signal pipe** for out-of-band control commands.

### The Three-Handle Model

The pseudoconsole relies on three distinct handles to separate data flow from control operations:

- **Input pipe**: Receives keystrokes and mouse input from the Terminal UI and forwards them to the client application
- **Output pipe**: Carries rendered text and VT sequences from the console host back to the Terminal for display
- **Signal pipe**: Transmits asynchronous control messages including resize requests, clear commands, and window visibility changes

This design prevents control sequences from interfering with the primary data streams, allowing the Terminal to resize or clear the buffer without corrupting the application’s I/O.

### Server Process Isolation

The PTY server runs in its own process space rather than inside the Terminal executable. In [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) (L 45‑L 101), the helper `_ConsoleHostPath()` resolves the path to either the system `conhost.exe` or the side-by-side `OpenConsole.exe` that ships with Windows Terminal. Keeping the console host external ensures that legacy applications receive a genuine console environment while the Terminal UI remains a separate HWND that can be styled, resized, and composed freely.

## Core Implementation in winconpty

The low-level ConPTY logic lives in the **winconpty** static library, which exports a C-style API and manages the lifecycle of the hidden console host.

### Host Resolution and Driver Loading

Before creating the PTY, the library ensures the console driver is available. The function `_EnsureDriverIsLoaded()` in [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) (L 101‑L 112) loads `ntdll.dll` and invokes `NtSetSystemInformation` with `SystemConsoleInformation` to force-load the ConDrv driver on builds where it might not be initialized. This guarantees that subsequent console operations succeed even on non-Insider builds of Windows.

### Process Creation with Extended Attributes

Creating the headless console host requires careful handle inheritance setup. In [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) (L 124‑L 166), the code builds a `PROC_THREAD_ATTRIBUTE_LIST` containing the server handle, client input/output handles, and the signal pipe handle. This attribute list is passed to `CreateProcessAsUserW` (L 186‑L 227) along with a command line such as:

```text
"<conhostPath>" --headless --width <cols> --height <rows> --signal <signalHandle> --server <serverHandle>

```

The `--headless` flag instructs the console host to run without a visible window, while the `--signal` and `--server` parameters wire the process to the Terminal’s pipes.

### Exported C API Functions

The public interface declared in [`src/inc/conpty-static.h`](https://github.com/microsoft/terminal/blob/main/src/inc/conpty-static.h) (L 26‑L 48) and implemented in [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) (L 460‑L 508) provides:

- `ConptyCreatePseudoConsole`: Allocates the `PseudoConsole` struct and launches the headless host
- `ConptyResizePseudoConsole`: Sends `PTY_SIGNAL_RESIZE_WINDOW` via the signal pipe
- `ConptyClearPseudoConsole`: Sends `PTY_SIGNAL_CLEAR_WINDOW`
- `ConptyShowHidePseudoConsole`: Sends `PTY_SIGNAL_SHOWHIDE_WINDOW`
- `ConptyReparentPseudoConsole`: Sends `PTY_SIGNAL_REPARENT_WINDOW` to attach the hidden console window to a different HWND
- `ConptyClosePseudoConsole`: Releases the `HPCON` handle and closes all three pipes (L 624‑L 648)

These functions wrap the internal helpers `_CreatePseudoConsole`, `_ResizePseudoConsole`, and others, offering a RAII-friendly interface for C++ consumers.

## Terminal Integration via ConptyConnection

While **winconpty** handles the raw PTY mechanics, the **Cascadia** UI layer consumes these primitives through `ConptyConnection`.

### Pipe Creation and PTY Initialization

In [`src/cascadia/TerminalConnection/ConptyConnection.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalConnection/ConptyConnection.cpp) (L 96‑L 124), the `Start()` method creates overlapped I/O pipes using `Utils::CreateOverlappedPipe`, then invokes `ConptyCreatePseudoConsole` to obtain an `HPCON` handle. The method stores this handle and optionally re-parents the PTY window (L 146‑L 162) so that the invisible console window behaves correctly with focus and IME integration. After setup, the code releases the initial reference via `ConptyReleasePseudoConsole`, keeping the PTY alive only through the active pipe handles.

### Runtime Control Operations

All dynamic modifications to the pseudoconsole travel over the signal pipe, implemented in [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp):

- **Resize** (L 486‑L 525): `ConptyResizePseudoConsole` writes a `PTY_SIGNAL_RESIZE_WINDOW` structure containing new width and height values
- **Clear** (L 540‑L 564): `ConptyClearPseudoConsole` issues `PTY_SIGNAL_CLEAR_WINDOW` to reset the scrollback buffer
- **Show/Hide** (L 590‑L 620): `ConptyShowHidePseudoConsole` toggles visibility with `PTY_SIGNAL_SHOWHIDE_WINDOW`
- **Re-parent** (L 660‑L 685): `ConptyReparentPseudoConsole` sends `PTY_SIGNAL_REPARENT_WINDOW` with a target HWND, enabling the Terminal UI to own the PTY window for proper focus handling (see GitHub issue #2988)

Because these messages are asynchronous and travel over a dedicated pipe, the Terminal can resize or clear the buffer without blocking the data streams moving through the input and output pipes.

## Practical ConPTY Implementation Example

The following minimal C++ snippet mirrors the logic found in `ConptyConnection::Start()` and demonstrates how to create a pseudoconsole, launch a child process, and resize it programmatically:

```cpp
#include <windows.h>
#include <conpty-static.h>   // ConPTY public API
#include <wil/resource.h>    // for wil::unique_handle

int main()
{
    // 1. Create overlapped pipes for bidirectional I/O
    HANDLE hInRead, hInWrite;
    HANDLE hOutRead, hOutWrite;
    SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, TRUE };
    CreatePipe(&hInRead, &hInWrite, &sa, 0);
    CreatePipe(&hOutRead, &hOutWrite, &sa, 0);

    // 2. Create the pseudoconsole (80 columns x 24 rows)
    HPCON hPC = nullptr;
    COORD size{ 80, 24 };
    HRESULT hr = ConptyCreatePseudoConsole(
        size,
        hInRead,      // PTY input (data flowing to the client)
        hOutWrite,    // PTY output (data flowing from the client)
        0,            // Flags such as PSEUDOCONSOLE_INHERIT_CURSOR
        &hPC);
    if (FAILED(hr))
        return 1;

    // 3. Launch cmd.exe attached to the PTY
    STARTUPINFOEXW siEx{ sizeof(STARTUPINFOEXW) };
    PROCESS_INFORMATION pi{};
    siEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
    siEx.StartupInfo.hStdInput  = hInWrite;
    siEx.StartupInfo.hStdOutput = hOutRead;
    siEx.StartupInfo.hStdError  = hOutRead;

    CreateProcessW(L"cmd.exe", nullptr, nullptr, nullptr, TRUE,
                   EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
                   &siEx.StartupInfo, &pi);

    // 4. Resize the terminal to 100x30 via the signal pipe
    COORD newSize{ 100, 30 };
    ConptyResizePseudoConsole(hPC, newSize);

    // 5. Cleanup on exit
    ConptyClosePseudoConsole(hPC);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    CloseHandle(hInRead);   CloseHandle(hInWrite);
    CloseHandle(hOutRead);  CloseHandle(hOutWrite);
}

```

This example reflects the production implementation found in [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) (L 460‑L 468 for creation, L 486‑L 494 for resize) and [`src/cascadia/TerminalConnection/ConptyConnection.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalConnection/ConptyConnection.cpp) (L 96‑L 162 for integration).

## Summary

- Windows Terminal spawns a headless `conhost.exe` or `OpenConsole.exe` process to run the PTY server independently from the UI, isolating the console implementation from the terminal frontend.
- The system maintains three asynchronous handles: an input pipe, an output pipe, and a dedicated signal pipe for out-of-band control messages such as resize and clear commands.
- The **winconpty** library exports a C API (`ConptyCreatePseudoConsole`, `ConptyResizePseudoConsole`, etc.) that wraps low-level NT primitives and handle inheritance logic.
- `ConptyConnection` integrates these primitives into the Cascadia UI, managing pipe creation, process lifecycle, window re-parenting, and graceful shutdown.
- All control operations travel over the signal pipe, ensuring that data streams remain uncontaminated while enabling real-time terminal modifications.

## Frequently Asked Questions

### What is the difference between ConPTY and a traditional console host?

A traditional console host renders text directly to a visible window owned by the operating system. **ConPTY** creates a pseudoconsole that captures all output into pipes, allowing Windows Terminal to receive the rendered buffer and display it inside a modern, hardware-accelerated UI while the actual console engine runs headlessly in the background.

### How does Windows Terminal resize a pseudoconsole without disrupting the client process?

Windows Terminal calls `ConptyResizePseudoConsole`, which writes a `PTY_SIGNAL_RESIZE_WINDOW` packet to the signal pipe (implemented in [`src/winconpty/winconpty.cpp`](https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp) L 486‑L 525). The headless `conhost.exe` processes this message asynchronously, updating its internal buffer dimensions without interrupting the STDIN or STDOUT data flowing through the primary pipes.

### Can third-party applications use the same ConPTY API as Windows Terminal?

Yes. The API is publicly exported in [`src/inc/conpty-static.h`](https://github.com/microsoft/terminal/blob/main/src/inc/conpty-static.h) and demonstrated in the `samples/ConPTY/EchoCon` project. Any application can link against the winconpty static library and call `ConptyCreatePseudoConsole` to embed a full Windows console inside its own window, handling I/O through the same three-pipe model.

### Why does Windows Terminal use a separate signal pipe instead of embedding control codes in the data stream?

The dedicated signal pipe isolates control-plane messages (resize, clear, show/hide, re-parent) from the data flow. This prevents escape-sequence conflicts, avoids the need to parse inline control codes from application output, and enables asynchronous operations that do not block or buffer within the primary I/O pipes, ensuring responsive terminal behavior even under heavy load.