【问题标题】:How can I detect if there is input waiting on stdin on windows?如何检测 Windows 上的标准输入是否有输入等待?
【发布时间】:2014-04-19 00:48:06
【问题描述】:

我想检测 Windows 中的标准输入是否有输入等待。

我在 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
    }
}

注意:我不想像 kbhit 那样处理键盘和键盘功能。我想要一种方法来做我所要求的。我也不希望第三方库执行此操作,我希望通过本地 Windows 库调用来获得答案。

注意 #2:在 windows 上,上面的代码失败,出现 windows 错误代码 10038“尝试对不是套接字的东西进行操作”,这可能意味着 windows 不支持在 STDIN 上选择。

【问题讨论】:

  • 我们假设这是一个控制台应用程序吗?
  • 好吧,我不了解Linux,所以我不知道您发布的代码在做什么。但根据我对这个问题的理解,ReadConsoleInput 函数应该可以工作。您必须手动调用它,它不会给您通知。 GetNumberOfConsoleInputEventsPeekConsoleInput 也是选项。
  • 在 Linux 中,select() 对文件描述符进行操作,包括套接字、stdin/out 等。在 Windows 上,select() 仅对套接字起作用,没有别的。您不能在 Windows 上将 stdin 传递给 select()

标签: c++ c windows


【解决方案1】:

ReadConsoleInput() documentation中所述:

进程可以在wait functions 之一中指定控制台输入缓冲区句柄,以确定何时存在未读控制台输入。当输入缓冲区不为空时,会发出控制台输入缓冲区句柄的状态信号。

要确定控制台输入缓冲区中未读输入记录的数量,请使用GetNumberOfConsoleInputEvents 函数。要从控制台输入缓冲区读取输入记录而不影响未读记录的数量,请使用PeekConsoleInput 函数。要丢弃控制台输入缓冲区中的所有未读记录,请使用FlushConsoleInputBuffer 函数。

您可以使用GetStdHandle() 获取STDIN 的句柄,以便在上述函数中使用。

【讨论】:

  • 请注意,如果标准输入已被重定向,这将无法按预期工作。如果您想从控制台读取(忽略重定向),您应该显式打开并从CONIN$ 读取,而不是使用标准输入。如果您不想忽略重定向,则需要检查标准输入是否是控制台句柄并相应地更改行为。 (我认为最好的方法是在输入非控制台句柄时检测来自ReadConsoleInput 的错误,但另请参阅GetFileType。)
  • 捕获读取错误并不是检测输入类型的最佳方法。 GetConsoleMode() 将告诉您输入是否来自真实控制台。如果需要,GetFileType() 会告诉你使用的是什么类型的输入。
  • 是的,你是对的:当你想调用 ReadConsoleInput() 时,你可能已经需要知道句柄是否指向控制台。从 GetConsoleMode() 检查错误更有意义。
【解决方案2】:

正如已经指出的,在 Windows 中您必须使用 GetStdHandle() 并且返回的句柄不能与套接字混合。但幸运的是,可以使用WaitForSingleObject() 测试返回的句柄,就像 Windows 中的许多其他句柄一样。裁判,你可以这样做:

#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;
}

编辑:忘了说您需要从句柄中读取字符才能使 key_was_pressed() 返回 FALSE。

【讨论】:

  • Nitpick:套接字句柄,但并非所有句柄都是套接字。如果标准输入恰好被重定向到套接字句柄,那么 select() 将起作用。
  • 指出得很好。在 Windows 中,几乎所有东西都是句柄。我应该用正确的措辞:'套接字不是文件句柄'。 ;-)
【解决方案3】:

如您所知,Linux != Windows,您不会在两种环境中为“select()”获得相同的语义。

您也没有指定这是 Windows GUI 还是控制台模式程序。它有所作为。

...然而...

您可能想了解的两个 Win32 API:

'希望有帮助

PS:

如果这是一个控制台模式程序,您将需要一个窗口句柄。拨打GetStdHandle(STD_INPUT_HANDLE)获取。

【讨论】:

    猜你喜欢
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多