c中的非阻塞i / o? (视窗)

时间:2014-01-22 21:48:54

标签: c io nonblocking

我正在尝试在Windows终端应用程序上获取非阻塞I / O(仅限Windows,抱歉!)。

如果我希望输入时间短,用户可以按一个按钮怎么办?如果他没有输入停止并且程序继续?

例如:

当用户按下某个键时,从1到任何停止计时的计时器: 我应该有一个while循环,但是如果我执行getch或getchar函数它会停止程序,对吗?

我知道我可以使用kbhit(); ,但对于“程序”,我试图让我需要知道输入,而不仅仅是输入! 是否有任何简单的函数可以让我像键盘缓冲区中的最后一个键一样读取?

2 个答案:

答案 0 :(得分:3)

来自documentation for _kbhit()

  

_kbhit函数检查控制台是否有最近的击键。如果函数返回非零值,则按键在缓冲区中等待。然后,程序可以调用_getch_getche来获取击键。

所以,在你的循环中:

while (true) {
    // ...
    if (_kbhit()) {
        char c = _getch();
        // act on character c in whatever way you want
    }
}

因此,您仍然可以使用_getch(),但仅在_kbhit()表示有等待之后才限制其使用。这样它就不会阻止。

答案 1 :(得分:0)

以下是如何使用正确的 API 对 Windows 中的 stdin 进行非阻塞调用:

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

void ErrorExit(LPSTR);
void KeyEventProc(KEY_EVENT_RECORD ker);

// Global variables are here for example, avoid that.
DWORD fdwSaveOldMode;
HANDLE hStdin;

void printToCoordinates(int x, int y, char* text)
{
    printf("\033[%d;%dH%s", y, x, text);
}

int main()
{
    printf("\033[H\033[J");
    int i = 0;
    char* s = "*";

    DWORD fdwMode, cNumRead;
    INPUT_RECORD irInBuf[128];
    DWORD bufferSize = 0;

    hStdin = GetStdHandle(STD_INPUT_HANDLE);

    // Just a check to ensure everything is fine at this state
    if (hStdin==INVALID_HANDLE_VALUE){
        printf("Invalid handle value.\n");
        exit(EXIT_FAILURE);
    }

    // Just a check to ensure everything is fine at this state
    if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
        ErrorExit("GetConsoleMode");

    // Those constants are documented on Microsoft doc
    // ENABLE_PROCESSED_INPUT allows you to use CTRL+C
    // (so it's not catched by ReadConsoleInput here)
    fdwMode = ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT;
    if (! SetConsoleMode(hStdin, fdwMode) )
        ErrorExit("SetConsoleMode");


    while (i < 60) {
        // The goal of this program is to print a line of stars
        printToCoordinates(i, 5, s);
        i++;


        GetNumberOfConsoleInputEvents(hStdin, &bufferSize);

        // ReadConsoleInput block if the buffer is empty
        if (bufferSize > 0) {
            if (! ReadConsoleInput(
                    hStdin,      // input buffer handle
                    irInBuf,     // buffer to read into
                    128,         // size of read buffer
                    &cNumRead) ) // number of records read
                ErrorExit("ReadConsoleInput");

            // This code is not rock solid, you should iterate over
            // irInBuf to get what you want, the last event may not contain what you expect
            // Once again you'll find an event constant list on Microsoft documentation
            if (irInBuf[cNumRead-1].EventType == KEY_EVENT) {
                KeyEventProc(irInBuf[cNumRead-1].Event.KeyEvent);
                Sleep(2000);
            }
        }

        Sleep(100);
    }
    // Setting the console back to normal
    SetConsoleMode(hStdin, fdwSaveOldMode);
    CloseHandle(hStdin);

    printf("\nFIN\n");

    return 0;
}

void ErrorExit (LPSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);

    // Restore input mode on exit.

    SetConsoleMode(hStdin, fdwSaveOldMode);

    ExitProcess(0);
}

void KeyEventProc(KEY_EVENT_RECORD ker)
{
    printf("Key event: \"%c\" ", ker.uChar.AsciiChar);

    if(ker.bKeyDown)
        printf("key pressed\n");
    else printf("key released\n");
}

请注意这项在全新的终端应用程序中的工作,而不是在 CMD 中(由于代码中使用了 termcaps),但它会编译并且您仍然可以运行它。