microsoft / microsoft/terminal
ReadConsole returns TRUE after CancelIoEx
- Dominant language
- C++
- Stars
- 105k
- Forks
- 9.6k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 29
Description
### Windows Terminal version
Latest source
### Windows build number
10.0.19045.4780
### Other Software
No
### Steps to reproduce
1. Compile the code:
Code
```C++
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include
#include
#include
#include
#include
using namespace std::literals;
int main()
{
const auto In = GetStdHandle(STD_INPUT_HANDLE);
std::thread Thread([&]
{
std::this_thread::sleep_for(3s);
CancelIoEx(In, {});
});
std::wcout << L"Do not touch anything for about 3s" << std::endl;
wchar_t Buffer[1024];
DWORD NumberOfCharsRead{};
const auto Result = ReadConsole(In, Buffer, ARRAYSIZE(Buffer), &NumberOfCharsRead, {});
const auto Error = GetLastError();
std::wcout << L"Result: " << Result << std::endl;
std::wcout << L"Error: " << Error << std::endl;
std::wcout << L"Chars: " << NumberOfCharsRead << std::endl;
std::wcout << L"Data: " << std::wstring(Buffer, NumberOfCharsRead) << std::endl;
Thread.join();
std::wcout << L"Now enter something: " << std::endl;
if (ReadConsole(In, Buffer, ARRAYSIZE(Buffer), &NumberOfCharsRead, {}))
{
std::wcout << L"You have entered: " << std::wstring(Buffer, NumberOfCharsRead) << std::endl;
}
}
```
2. Run it (anywhere: WT, OpenConsole, conhost)
3. Inspect the output
### Expected Behavior
The program reads the console input in a blocking way.
If there is no input, another thread issues CancelIoEx after 3 seconds.
Since the `ReadConsole` did not read anything, it should return FALSE:
- It is logical.
- Even Raymond Chen [says so](https://devblogs.microsoft.com/oldnewthing/20150323-00/?p=44413):
> If you had used ReadFile instead of fgets, the read would have failed with error code ERROR_OPERATION_ABORTED, as documented by CancelIoEx.
So the program should print `Result: 0`.
### Actual Behavior
`ReadConsole` does not read anything, does not update `NumberOfCharsRead`, but returns **TRUE**.
It also leaves the input in somewhat inconsistent state, which you can see by typing something after the cancellation: the first input will be discarded. I think this was already mentioned here: https://github.com/microsoft/terminal/issues/12143#issuecomment-1895629003.
The incorrect return value is much worse though: it is a common pattern to leave `NumberOfCharsRead` uninitialized, because either `ReadConsole` succeeds and initializes it, or it fails and it makes no sense to look there anyway.
In the code above I initialized it, but if I didn't do so an uninitialized read would've occurred, from both `NumberOfCharsRead` and `Buffer`.
Notably the last error is correctly set to 995 - `ERROR_OPERATION_ABORTED` - "The I/O operation has been aborted because of either a thread exit or an application request.", but who checks the last error on successful calls?
Contributor guide
Research direction
Start by running the supplied C++ reproducer in Windows Terminal, OpenConsole, or conhost, focusing on the ReadConsole call canceled by CancelIoEx. Trace the console input path and existing handling of ERROR_OPERATION_ABORTED. Done means a canceled read reports failure without consuming input, with behavior verified against the reproducer.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100