6

I want to detect whether or not there is input waiting on stdin in Windows.

I use the following generic structure on Linux:

fd_set currentSocketSet;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int result = 1;
while (result > 0){
    FD_ZERO(&currentSocketSet);
    FD_SET(STDIN, &currentSocketSet);
    result = select(STDIN+1, &currentSocketSet, NULL, NULL, &tv);
    if (result == -1){
        printf("Network Error -- select errored with id=%d.\n", errno);
        return;
    } else if (result > 0){
        //...do stuff
    }
}

Note: I do not want to deal with the keyboard and keyboard functions like kbhit. I want a way to do what I asked. I do not want a third party library to do this either, I would like a native Windows library call to get the answer.

Note #2: On windows the above code fails with windows error code 10038 "An operation was attempted on something that is not a socket" which probably means that windows does not support selecting on STDIN.

chacham15
  • 13,719
  • 26
  • 104
  • 207
  • Are we assuming this is a console application? – Cody Gray - on strike Apr 19 '14 at 00:49
  • 1
    Well, I don't know Linux, so I don't know what the code you've posted is doing. But from what I understand of the question, the `ReadConsoleInput` function should work. You'll have to call it manually, it isn't going to give you a notification. `GetNumberOfConsoleInputEvents` and `PeekConsoleInput` are also options. – Cody Gray - on strike Apr 19 '14 at 00:50
  • 3
    In Linux, `select()` operates on file descriptors, which includes sockets, stdin/out, etc. On Windows, `select()` only works on sockets and nothing else. You cannot pass `stdin` to `select()` on Windows. – Remy Lebeau Apr 19 '14 at 00:51

3 Answers3

7

As stated in the ReadConsoleInput() documentation:

A process can specify a console input buffer handle in one of the wait functions to determine when there is unread console input. When the input buffer is not empty, the state of a console input buffer handle is signaled.

To determine the number of unread input records in a console's input buffer, use the GetNumberOfConsoleInputEvents function. To read input records from a console input buffer without affecting the number of unread records, use the PeekConsoleInput function. To discard all unread records in a console's input buffer, use the FlushConsoleInputBuffer function.

You can use GetStdHandle() to get a handle to STDIN for use in the above functions.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    Note that this won't work as expected if standard input has been redirected. If you want to read from the console (ignoring redirection) you should explicitly open and read from `CONIN$` rather than using standard input. If you don't want to ignore redirection, you'll need to check whether standard input is a console handle or not and change behaviour accordingly. (I think the best way to do this is to detect the error from `ReadConsoleInput` when it is fed a non-console handle, but see also `GetFileType`.) – Harry Johnston Apr 20 '14 at 00:59
  • 3
    Catching a read error is not the best way to detect the input type. GetConsoleMode() will tell you if the input is coming from a real console or not. GetFileType() will tell you what type of input is used, if you need that. – Remy Lebeau Apr 20 '14 at 17:38
  • Yes, you're right: by the time you want to call ReadConsoleInput() you probably already need to know whether the handle is to the console or not. Checking for an error from GetConsoleMode() makes more sense. – Harry Johnston Apr 21 '14 at 01:19
3

As already pointed out, in Windows you have to use GetStdHandle() and the returned handle cannot be mixed with sockets. But luckily, the returned handle can be tested with WaitForSingleObject(), just like many other handles in Windows. Therefere, you could do something like this:

#include <stdio.h>
#include <windows.h>

BOOL key_was_pressed(void)
{
    return (WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE),0)==WAIT_OBJECT_0);
}

void wait_for_key_press(void)
{
    WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE),INFINITE);
}

int main()
{
    if(key_was_pressed())
        printf("Someone pressed a key beforehand\n");

    printf("Wait until a key is pressed\n");

    wait_for_key_press();

    if(key_was_pressed())
        printf("Someone pressed a key\n");
    else
        printf("That can't be happening to me\n");

    return 0;
}

EDIT: Forgot to say that you need to read the characters from the handle in order to key_was_pressed() to return FALSE.

Felix Dombek
  • 13,664
  • 17
  • 79
  • 131
guilleamodeo
  • 270
  • 2
  • 10
  • 3
    Nitpick: sockets *are* handles, but not all handles are sockets. If the standard input happens to have been redirected to a socket handle then select() will work. – Harry Johnston Apr 20 '14 at 01:01
  • Well pointed out. In windows almost everything is a handle. I should have worded it properly: 'Sockets are not file handles'. ;-) – guilleamodeo Apr 20 '14 at 08:23
1

As you know, Linux != Windows, and you're not going to get the same semantics for a "select()" in both environments.

You didn't specify whether this is a Windows GUI or console-mode program, either. It makes a difference.

... HOWEVER ...

Two Win32 APIs you might want to look at:

'Hope that helps

PS:

If this is a console-mode program, you're going to need a window handle. Call GetStdHandle(STD_INPUT_HANDLE) to get it.

FoggyDay
  • 11,962
  • 4
  • 34
  • 48