CancelIoEx Needs Twice Enter for Input

Investigation

CancelIoEx is a Windows API to cancel I/O blocking operation.

However, once you use it to cancel the blocking getline() or getchar() and resume getline() or getchar() again, the first input (hit Enter key) will not be caught by your code… It’s weird.

#include <iostream>
#include <thread>
#include <string>
#include <windows.h>

using namespace std;
HANDLE hStdin = nullptr;

void readInput() {
    string input;
    while (true) {
        cout << endl << "input: ";
        getline(std::cin, input);
        if (cin.good())
            cout << "got msg: " << input << std::endl;
        else
        {
            cin.clear();
        }
    }
}

void CancelInputPeriodically() {
    while (true) {
        this_thread::sleep_for(std::chrono::seconds(7));
        CancelIoEx(hStdin, nullptr);
        cout << "time's up!!!" << endl;
    }
}

int main() {
    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    thread cancelThread(CancelInputPeriodically);

    readInput();

    cancelThread.join();
    return 0;
}

Workaround

A workaround is to send a return key after each CancelIoEx() calling.

In other words, put the following code after cin.clear();

DWORD dwTmp;
INPUT_RECORD ir[2] = { 0 };
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);

ir[1] = ir[0];
ir[1].Event.KeyEvent.bKeyDown = FALSE;
WriteConsoleInput(hStdin, ir, 2, &dwTmp);

Follow-up

I posted this question on Stack Overflow:

https://stackoverflow.com/questions/78108601/why-cancelioex-doesnt-cancel-getline-completely-and-need-twice-enter-to-trigger


comments powered by Disqus