Ctrl+Break does not send 0x03 in raw mode #7153

Closed
opened 2026-01-31 00:56:31 +00:00 by claunia · 4 comments
Owner

Originally created by @alexrp on GitHub (Mar 26, 2020).

Environment

Windows build number: Microsoft Windows [Version 10.0.19041.153]

Steps to reproduce

using System;
using System.Threading;
using Vanara.PInvoke;
using static Vanara.PInvoke.Kernel32;

namespace Test
{
    static class Program
    {
        static HandlerRoutine _handler;

        unsafe static void Main()
        {
            _handler = (e) =>
            {
                Console.WriteLine(e);
                return true;
            };

            SetConsoleCtrlHandler(_handler, true);

            var stdin = GetStdHandle(StdHandleType.STD_INPUT_HANDLE);

            GetConsoleMode(stdin, out CONSOLE_INPUT_MODE m);
            m |= CONSOLE_INPUT_MODE.ENABLE_VIRTUAL_TERMINAL_INPUT;
            m &= ~(CONSOLE_INPUT_MODE.ENABLE_PROCESSED_INPUT |
                   CONSOLE_INPUT_MODE.ENABLE_LINE_INPUT);
            SetConsoleMode(stdin, m);

            var buf = new byte[4096];

            fixed (byte* p = buf)
                Kernel32.ReadFile(stdin, (IntPtr)p, (uint)buf.Length, out _, IntPtr.Zero);

            Console.WriteLine(Win32Error.GetLastError());
            Console.WriteLine("0x{0:x2}", buf[0]);
            Thread.Sleep(2500);
        }
    }
}

Expected behavior

ERROR_SUCCESS
0x03

Actual behavior

CTRL_BREAK_EVENT
ERROR_SUCCESS
0x00

Notes

  • If the SetConsoleCtrlHandler call is removed, no output is printed at all.
  • Using ReadConsole instead makes no difference (except that ERROR_OPERATION_ABORTED is printed).
  • On Linux, if the terminal is in raw mode, both Ctrl+C and Ctrl+Break will return 0x03.
Originally created by @alexrp on GitHub (Mar 26, 2020). # Environment ```none Windows build number: Microsoft Windows [Version 10.0.19041.153] ``` # Steps to reproduce ```csharp using System; using System.Threading; using Vanara.PInvoke; using static Vanara.PInvoke.Kernel32; namespace Test { static class Program { static HandlerRoutine _handler; unsafe static void Main() { _handler = (e) => { Console.WriteLine(e); return true; }; SetConsoleCtrlHandler(_handler, true); var stdin = GetStdHandle(StdHandleType.STD_INPUT_HANDLE); GetConsoleMode(stdin, out CONSOLE_INPUT_MODE m); m |= CONSOLE_INPUT_MODE.ENABLE_VIRTUAL_TERMINAL_INPUT; m &= ~(CONSOLE_INPUT_MODE.ENABLE_PROCESSED_INPUT | CONSOLE_INPUT_MODE.ENABLE_LINE_INPUT); SetConsoleMode(stdin, m); var buf = new byte[4096]; fixed (byte* p = buf) Kernel32.ReadFile(stdin, (IntPtr)p, (uint)buf.Length, out _, IntPtr.Zero); Console.WriteLine(Win32Error.GetLastError()); Console.WriteLine("0x{0:x2}", buf[0]); Thread.Sleep(2500); } } } ``` # Expected behavior ``` ERROR_SUCCESS 0x03 ``` # Actual behavior ``` CTRL_BREAK_EVENT ERROR_SUCCESS 0x00 ``` # Notes * If the `SetConsoleCtrlHandler` call is removed, no output is printed at all. * Using `ReadConsole` instead makes no difference (except that `ERROR_OPERATION_ABORTED` is printed). * On Linux, if the terminal is in raw mode, both Ctrl+C and Ctrl+Break will return `0x03`.
claunia added the Needs-TriageNeeds-Tag-Fix labels 2026-01-31 00:56:31 +00:00
Author
Owner

@eryksun commented on GitHub (Mar 26, 2020):

It's documented that "CTRL+BREAK is always treated as a signal". In HandleGenericKeyEvent Ctrl+Break is always processed to flush the input buffer, generate a control event, and terminate the read.

Is this a request to change the documented behavior to match how a raw read in Linux handles Ctrl+Break? Can you provide sample code? I tested in Linux (not WSL) using Python's tty.setraw. In raw mode, Ctrl+C is read as "\x03", but Ctrl+Break is apparently ignored.

@eryksun commented on GitHub (Mar 26, 2020): It's [documented](https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals) that "CTRL+BREAK is always treated as a signal". In [`HandleGenericKeyEvent`](https://github.com/microsoft/terminal/blob/5de9fa9cf375e9b5bdc582d8d166604eeb519844/src/host/input.cpp#L105) Ctrl+Break is always processed to flush the input buffer, generate a control event, and terminate the read. Is this a request to change the documented behavior to match how a raw read in Linux handles Ctrl+Break? Can you provide sample code? I tested in Linux (not WSL) using Python's [`tty.setraw`](https://github.com/python/cpython/blob/master/Lib/tty.py#L18). In raw mode, Ctrl+C is read as "\x03", but Ctrl+Break is apparently ignored.
Author
Owner

@alexrp commented on GitHub (Mar 26, 2020):

Sample C code:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int main()
{
    printf("reading: ");
    fflush(stdout);

    struct termios old, new;

    tcgetattr(STDIN_FILENO, &old);
    new = old;
    cfmakeraw(&new);

    tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);
    char c = 0;
    ssize_t len = read(STDIN_FILENO, &c, sizeof(c));
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);

    printf("0x%x\n", c);

    return 0;
}

I only have WSL2 available on this system right now; I can test on a real Linux machine later today. I tested under WSL2 with Windows Terminal and WSLtty, and interestingly the results are different: Windows Terminal gives 0x03 for Ctrl+Break while WSLtty gives 0x1c. So my initial assumption that Ctrl+C and Ctrl+Break should both produce 0x03 is almost certainly incorrect.

I'll investigate in more depth later today. Clearly this is a bit more complicated than it seemed at first. 😕

@alexrp commented on GitHub (Mar 26, 2020): Sample C code: ```c #include <stdio.h> #include <termios.h> #include <unistd.h> int main() { printf("reading: "); fflush(stdout); struct termios old, new; tcgetattr(STDIN_FILENO, &old); new = old; cfmakeraw(&new); tcsetattr(STDIN_FILENO, TCSAFLUSH, &new); char c = 0; ssize_t len = read(STDIN_FILENO, &c, sizeof(c)); tcsetattr(STDIN_FILENO, TCSAFLUSH, &old); printf("0x%x\n", c); return 0; } ``` I only have WSL2 available on this system right now; I can test on a real Linux machine later today. I tested under WSL2 with Windows Terminal and WSLtty, and interestingly the results are different: Windows Terminal gives `0x03` for Ctrl+Break while WSLtty gives `0x1c`. So my initial assumption that Ctrl+C and Ctrl+Break should both produce `0x03` is almost certainly incorrect. I'll investigate in more depth later today. Clearly this is a bit more complicated than it seemed at first. 😕
Author
Owner

@alexrp commented on GitHub (Mar 26, 2020):

#1119 might be related wrt the Windows Terminal vs WSLtty discrepancy.

@alexrp commented on GitHub (Mar 26, 2020): #1119 might be related wrt the Windows Terminal vs WSLtty discrepancy.
Author
Owner

@alexrp commented on GitHub (Mar 26, 2020):

OK, well, I've tested on a number of different terminal emulators (mintty, WSLtty, Windows Terminal, PuTTY, GNOME Terminal, PowerShell, CMD, ...) on native Windows/Linux as well as WSL2, and there does not appear to be any kind of sane standard for how [Ctrl/Alt/Shift+]Break should actually work. Some terminals produce a value for those key combinations but they are all over the place (0x03, 0x1b, 0x1c, 0x1a, etc for different combinations). Some just ignore them. Some give EOF on Shift+Break. It seems like quite a mess.

So, I think it's probably safe to close this.

@alexrp commented on GitHub (Mar 26, 2020): OK, well, I've tested on a number of different terminal emulators (mintty, WSLtty, Windows Terminal, PuTTY, GNOME Terminal, PowerShell, CMD, ...) on native Windows/Linux as well as WSL2, and there does not appear to be any kind of sane standard for how [Ctrl/Alt/Shift+]Break should actually work. Some terminals produce a value for those key combinations but they are all over the place (`0x03`, `0x1b`, `0x1c`, `0x1a`, etc for different combinations). Some just ignore them. Some give EOF on Shift+Break. It seems like quite a mess. So, I think it's probably safe to close this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#7153