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

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 (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 (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 (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:

"<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 (L 26‑L 48) and implemented in 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 (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:

  • 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:

#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 (L 460‑L 468 for creation, L 486‑L 494 for resize) and 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 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 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.

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 →