-
Notifications
You must be signed in to change notification settings - Fork 8.7k
Description
Windows Terminal version
Latest source
Windows build number
10.0.19045.4780
Other Software
No
Steps to reproduce
- Compile the code:
Code
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <chrono>
#include <iostream>
#include <thread>
#include <string>
#include <windows.h>
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;
}
}
- Run it (anywhere: WT, OpenConsole, conhost)
- 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:
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: #12143 (comment).
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?