ReadConsoleInput can read 0 characters and return success and it almost certainly shouldn't #20390

Closed
opened 2026-01-31 07:12:18 +00:00 by claunia · 1 comment
Owner

Originally created by @zadjii-msft on GitHub (Aug 21, 2023).

Originally assigned to: @lhecker on GitHub.

More discussion notes in https://github.com/dotnet/runtime/issues/88697. I'm filing this here so we have a local reference to the discussion, even if we just close this out.

Here are some miscellaneous noted comments from that thread:

The Console.ReadKey function can throw an InvalidOperationException when "application does not have a console or when console input has been redirected". That makes sense. However, there is another case where it throws an InvalidOperationException, which happens when there are multiple processes attached to the same console, both waiting for input, and one dies or is killed.


BTW it's interesting that the official docs say that it's impossible to read 0 characters on success:

The function does not return until at least one input record has been read.


Is it a doc bug, or a code bug?


Bear with me as I collect some notes

ApiDispatchers::ServerGetConsoleInput looks like the guy that handles ReadConsoleInput microsoft/terminal@b556594/src/server/ApiDispatchers.cpp#L127-L140

That calls into ApiRoutines::GetConsoleInputImpl microsoft/terminal@b556594/src/host/directio.cpp#L73-L78

That always passes WaitForData as true, in the read from the input buffer. Doc comment on that method:

// - WaitForData - if true, wait until an event is input (if there aren't enough to fill client buffer). if false, return immediately

oh but then I think we start getting into the realm of the console read waits. Those are nasty to try and track down. My gut says it's not impossible that there's a case where a client disconnecting might try to complete the other waits that were pending. Tracking that down would be trickier. I also have no idea which is right, certainly not off the top of my head.


Originally created by @zadjii-msft on GitHub (Aug 21, 2023). Originally assigned to: @lhecker on GitHub. More discussion notes in https://github.com/dotnet/runtime/issues/88697. I'm filing this here so we have a local reference to the discussion, even if we just close this out. Here are some miscellaneous noted comments from that thread: > The `Console.ReadKey` function can throw an `InvalidOperationException` when "application does not have a console or when console input has been redirected". That makes sense. However, there is _another_ case where it throws an `InvalidOperationException`, which happens when there are multiple processes attached to the same console, both waiting for input, and one dies or is killed. ---- > BTW it's interesting that the [official docs](https://learn.microsoft.com/en-us/windows/console/readconsoleinput) say that it's impossible to read `0` characters on success: > > > The function does not return until at least one input record has been read. ---- > Is it a doc bug, or a code bug? ---- > Bear with me as I collect some notes > > `ApiDispatchers::ServerGetConsoleInput` looks like the guy that handles `ReadConsoleInput` [microsoft/terminal@`b556594`/src/server/ApiDispatchers.cpp#L127-L140](https://github.com/microsoft/terminal/blob/b556594793006ba49934d9e5291cbf235e18b4b3/src/server/ApiDispatchers.cpp#L127-L140) > > That calls into `ApiRoutines::GetConsoleInputImpl` [microsoft/terminal@`b556594`/src/host/directio.cpp#L73-L78](https://github.com/microsoft/terminal/blob/b556594793006ba49934d9e5291cbf235e18b4b3/src/host/directio.cpp#L73-L78) > > That always passes `WaitForData` as `true`, in the read from the input buffer. Doc comment on that method: > > > `// - WaitForData - if true, wait until an event is input (if there aren't enough to fill client buffer). if false, return immediately` > > oh but then I think we start getting into the realm of the console read waits. Those are nasty to try and track down. My gut says it's not impossible that there's a case where a client disconnecting might try to complete the other waits that were pending. Tracking that down would be trickier. I also have no idea which is right, certainly not off the top of my head. ----
claunia added the Product-ConhostIssue-BugIn-PRNeeds-Tag-FixArea-ServerPriority-2 labels 2026-01-31 07:12:19 +00:00
Author
Owner

@lhecker commented on GitHub (Aug 21, 2023):

oh but then I think we start getting into the realm of the console read waits. Those are nasty to try and track down.

No actually, it's gonna be super easy, barely an inconvenience. 1

GetConsoleInputImpl constructs a DirectReadData which is the callback that runs when input data is finally (supposedly) available sometime later. It passes false as WaitForData and so it returns STATUS_SUCCESS when the buffer is empty. It's been like that ever since the first commit on GitHub:
d4d59fa339/src/host/readDataDirect.cpp (L138)

But if we compare that with the conhost v1 code where the callback was a function called DirectReadWaitRoutine in directio.cpp then that one passes:

!(a->Flags & CONSOLE_READ_NOWAIT)

Which I think is the expected behavior. In other words, I believe that this is a conhost v1 -> v2 regression.

@lhecker commented on GitHub (Aug 21, 2023): > oh but then I think we start getting into the realm of the console read waits. Those are nasty to try and track down. No actually, it's gonna be super easy, barely an inconvenience. [^1] `GetConsoleInputImpl` constructs a `DirectReadData` which is the callback that runs when input data is finally (supposedly) available sometime later. It passes `false` as `WaitForData` and so it returns `STATUS_SUCCESS` when the buffer is empty. It's been like that ever since the first commit on GitHub: https://github.com/microsoft/terminal/blob/d4d59fa3395012ca37ba12665da4ec11c7dcf9cb/src/host/readDataDirect.cpp#L138 But if we compare that with the conhost v1 code where the callback was a function called `DirectReadWaitRoutine` in `directio.cpp` then that one passes: ```cpp !(a->Flags & CONSOLE_READ_NOWAIT) ``` Which I think is the expected behavior. In other words, I believe that this is a conhost v1 -> v2 regression. [^1]: https://www.youtube.com/@PitchMeetings
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#20390