Win32 - read from stdin with timeout

前端 未结 6 1436
时光取名叫无心
时光取名叫无心 2020-12-06 10:11

I\'m trying to do something which I think should be simple: do a blocking read from standard input, but timing out after a specified interval if no data is available.

<
相关标签:
6条回答
  • 2020-12-06 11:04

    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;
    }
    
    0 讨论(0)
  • 2020-12-06 11:07

    Use GetStdHandle() to get the stdin handle. You can then either:

    1. use WaitForSingleObject() i the stdin handle itself to detect when there is console input available for reading, then read from it as needed.

    2. use GetNumberOfConsoleInputEvents() or PeekConsoleInput() on the stdin handle in a loop to determine when there is data available for reading, then read from it as needed.

    3. use ReadFile() with an OVERLAPPED structure containing an event handle, then use the event handle with WaitForSingleObject() to detect if the read times out.

    In any case, be careful if stdin has been redirected. If it is redirected to something than is not console I/O, you can't use a GetStdHandle() handle with console functions. If it is redirected to a file, you have to use ReadFile().

    0 讨论(0)
  • 2020-12-06 11:07

    You'll need the GetStdHandle function to obtain a handle to the console, then you can use WaitForSingleObject to wait for an event to happen on that handle, with a timeout.

    0 讨论(0)
  • 2020-12-06 11:16

    In case anyone is writing chrome native messaging host and is looking for solution to check if there is any input on stdin without blocking then this works perfect:

    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    int timer = GetTickCount();
    while(timer+10000 > GetTickCount())
    {
        unsigned int length = 0;
        DWORD bytesAvailable = 0; 
        PeekNamedPipe(hStdin,NULL,0,NULL,&bytesAvailable,NULL);
        if(bytesAvailable > 0)
        {
            for (int i = 0; i < 4; i++)
            {
                unsigned int read_char = getchar();
                length = length | (read_char << i*8);
            }
    
    
            for (int i = 0; i < length; i++)
            {
                msg += getchar();
            }
            timer = GetTickCount();
        }
        else
        {
            // nothing to read, stdin empty
            Sleep(10);
        }
    }
    
    0 讨论(0)
  • 2020-12-06 11:17

    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 ProcessStdin
    
    int 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
    }
    
    0 讨论(0)
  • 2020-12-06 11:18

    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 input
    fdwMode = fdwOldMode ^ ENABLE_MOUSE_INPUT ^ ENABLE_WINDOW_INPUT;
    SetConsoleMode(hStdIn, fdwMode);
    // flush to remove existing events
    FlushConsoleInputBuffer(hStdIn);
    while (!abort)
    {
        if (WaitForSingleObject(hStdIn, 100) == WAIT_OBJECT_0)
        {
             std::getline(std::cin, inStr);
        }
    }
    // restore console mode when exit
    SetConsoleMode(hStdIn, fdwOldMode);
    
    0 讨论(0)
提交回复
热议问题