Win32 - read from stdin with timeout Win32 - read from stdin with timeout c c

Win32 - read from stdin with timeout


I had to solve a similar problem. On Windows it is not as easy or obvious as Linux. It is, however, possible. The trick is that Windows places console events in the console input event queue. You've got to filter out the events you don't care about and only process those events you do care about (like key presses).

For further reading: see the Win32 console documentation

Here is some mostly-debugged sample code based on a socket and stdin multiplexer I was working on:

void ProcessStdin(void){    INPUT_RECORD record;    DWORD numRead;    if(!ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &record, 1, &numRead)) {        // hmm handle this error somehow...        return;    }    if(record.EventType != KEY_EVENT) {        // don't care about other console events        return;    }    if(!record.Event.KeyEvent.bKeyDown) {        // really only care about keydown        return;    }    // if you're setup for ASCII, process this:    //record.Event.KeyEvent.uChar.AsciiChar} // end ProcessStdinint main(char argc, char* argv[]){    HANDLE eventHandles[] = {        GetStdHandle(STD_INPUT_HANDLE)        // ... add more handles and/or sockets here        };    DWORD result = WSAWaitForMultipleEvents(sizeof(eventHandles)/sizeof(eventHandle[0]),         &eventHandles[0],         FALSE,         1000,         TRUE        );    switch(result) {        case WSA_WAIT_TIMEOUT: // no I/O going on right now            break;        case WSA_WAIT_EVENT_0 + 0: // stdin at array index 0            ProcessStdin();            break;        case WSA_WAIT_EVENT_0 + 1: // handle/socket at array index 1            break;        case WSA_WAIT_EVENT_0 + 2: // ... and so on            break;        default: // handle the other possible conditions            break;    } // end switch result}


Using GetStdHandle + WaitForSingleObject works fine. But be sure to set the approriate flags and flush the console buffer as well before entering the loop.

In short (without error checks)

std::string inStr;DWORD fdwMode, fdwOldMode;HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);GetConsoleMode(hStdIn, &fdwOldMode);// disable mouse and window inputfdwMode = fdwOldMode ^ ENABLE_MOUSE_INPUT ^ ENABLE_WINDOW_INPUT;SetConsoleMode(hStdIn, fdwMode);// flush to remove existing eventsFlushConsoleInputBuffer(hStdIn);while (!abort){    if (WaitForSingleObject(hStdIn, 100) == WAIT_OBJECT_0)    {         std::getline(std::cin, inStr);    }}// restore console mode when exitSetConsoleMode(hStdIn, fdwOldMode);


This should do it:

int main(){    static HANDLE stdinHandle;    // Get the IO handles    // getc(stdin);    stdinHandle = GetStdHandle(STD_INPUT_HANDLE);    while( 1 )    {        switch( WaitForSingleObject( stdinHandle, 1000 ) )        {        case( WAIT_TIMEOUT ):            cerr << "timeout" << endl;            break; // return from this function to allow thread to terminate        case( WAIT_OBJECT_0 ):            if( _kbhit() ) // _kbhit() always returns immediately            {                int i = _getch();                cerr << "key: " << i << endl;            }            else // some sort of other events , we need to clear it from the queue            {                // clear events                INPUT_RECORD r[512];                DWORD read;                ReadConsoleInput( stdinHandle, r, 512, &read );                cerr << "mouse event" << endl;            }            break;        case( WAIT_FAILED ):            cerr << "WAIT_FAILED" << endl;            break;        case( WAIT_ABANDONED ):             cerr << "WAIT_ABANDONED" << endl;            break;        default:            cerr << "Someting's unexpected was returned.";        }    }    return 0;}