dotnet / dotnet/runtime

`Console.ReadLine()` returns each line one Enter late when `Console.TreatControlCAsInput` is `true` on Windows

Open
#133,814 4 comments 0 reactions 1 assignee Claimed by @caraioniurie47 View on GitHub
area-System.Console bug
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Description

With `Console.TreatControlCAsInput = true` on Windows, `Console.ReadLine()` does not return when Enter is pressed. It returns when the *next* line is entered, and what it returns is the previous line. An app reading lines in that mode is always one line behind the user.

The setter clears `ENABLE_PROCESSED_INPUT` and nothing else (`src/libraries/System.Console/src/System/ConsolePal.Windows.cs:475-482`). With that flag clear, the console host delivers Enter as a bare `\r`: calling `ReadConsoleW` directly on the input handle, a line comes back ending in `\r`, where the default mode gives `\r\n`. `Console.ReadLine()` calls `Console.In.ReadLine()`, and `Console.In` on Windows wraps a `StreamReader` over the console stream (`ConsolePal.Windows.cs:179-189`), and `StreamReader.ReadLine`, having found `\r`, calls `ReadBuffer()` to see whether a `\n` follows (`src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs:860-863`). On a console that read waits for the next line. The bare `\r` is measured; that the look-ahead is what waits comes from reading the source. In the same mode `Console.Read()` hands back the `\r` without waiting for another line, which points at the look-ahead rather than at the host.

Found while re-testing #44667, which is about the same property.

### Reproduction Steps

```csharp
Console.TreatControlCAsInput = true;

while (true)
{
string? line = Console.ReadLine();
Console.WriteLine($"ReadLine returned: \"{line}\"");
}
```

A `net10.0` console app with implicit usings. Run it in a console window, type `one` and Enter, `two` and Enter, `three` and Enter.

### Expected behavior

Each `ReadLine()` returns on its own Enter, as it does with the first line commented out:

| typed | printed after that Enter |
|---|---|
| `one` + Enter | `ReadLine returned: "one"` |
| `two` + Enter | `ReadLine returned: "two"` |
| `three` + Enter | `ReadLine returned: "three"` |

### Actual behavior

| typed | printed after that Enter |
|---|---|
| `one` + Enter | nothing |
| `two` + Enter | `ReadLine returned: "one"` |
| `three` + Enter | `ReadLine returned: "two"` |

The console screen buffer after `three` + Enter:

```
ReadLine returned: "one"
ReadLine returned: "two"
```

The typed text is not on screen either: the echoed Enter returns the cursor to column 0 of the same row without moving to a new one, so the next output overwrites it.

### Regression?

No. The same lag, measured with a probe that reads three lines the same way, on .NET Core 3.1.4 and .NET Framework 4.8.

### Known Workarounds

None tested.

### Configuration

Windows 11 build 26200, x64, .NET 10.0.12, inbox `conhost.exe`. Also measured under a standalone `OpenConsole.exe` from Windows Terminal v1.0.1401.0 (2020), with the same result. Not run on Linux or macOS.

### Other information

A related Windows-only quirk of the same reader is #40735 (`Console.In.Peek()` returning -1 after the first line).

I have not built a fix. Searching issues for `ReadLine TreatControlCAsInput`, `ReadLine ENABLE_PROCESSED_INPUT`, `Console.ReadLine enter twice` and `ReadLine previous line console` found no earlier report.

I'd like to work on fixing this, including working out where the fix belongs, and would appreciate being assigned.

> [!NOTE]
> AI-generated, written at my direction and reviewed by me before posting. Source read at `d84e42c1ca2`. The reproduction program above was built and run as written on .NET 10.0.12, Windows 11 build 26200, under inbox conhost and under `OpenConsole.exe` 1.0.200517002, with the keystrokes written into its console input from a separate process (`AttachConsole` + `WriteConsoleInput`) and the screen buffer read back from there. It was also run by hand under inbox conhost, typed on a keyboard, with the same result. The `ReadConsoleW` and `Console.Read()` results, and the .NET Core 3.1.4 and .NET Framework 4.8 results, come from a separate harness that writes keystrokes into its own console input.

https://github.com/user-attachments/assets/b96f70b8-70db-4719-9fcc-12694938b301

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.