16

私は簡単だと思うことをしようとしています.標準入力からブロック読み取りを行いますが、データが利用できない場合は指定された間隔でタイムアウトします.

Unix の世界ではこれは簡単ですが、ソケットではないselect()ため Windows では機能しません。stdin余分なスレッドなどを作成せずに次に簡単なオプションは何ですか?

Win32 環境を対象としたビジュアル C++ を使用しています。

これまで私は試しました:

  1. using select(入力がソケットでない場合は機能しません)

  2. を使用してWaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE))。- レミーの最初の提案。標準入力がコンソールの場合、これを呼び出すと常にすぐに返されるようです (他の人も同じ問題を報告しています)。

  3. 重複した IO を使用してWaitForSingleObject(Remy の 3 番目の提案) を実行します。この場合、入力がコンソールからのものである場合、読み取りは常にブロックされているように見えます -stdin非同期 I/O をサポートしていないようです。

現時点では、私の残りの唯一のオプションは、ブロッキング読み取りを実行してからイベントを通知するスレッドを作成し、タイムアウトでイベントを待機する別のスレッドを作成することだと考えています。

4

6 に答える 6

6

同様の問題を解決する必要がありました。Windows では、Linux ほど簡単でも明白でもありません。ただし、可能です。トリックは、Windows がコンソール イベントをコンソール入力イベント キューに配置することです。気にしないイベントを除外し、重要なイベント (キーの押下など) のみを処理する必要があります。

詳細については、Win32 コンソールのドキュメントを参照してください。

これは、私が取り組んでいたソケットと stdin マルチプレクサーに基づいた、大部分がデバッグされたサンプル コードです。

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
}
于 2014-03-10T20:30:32.560 に答える
4

GetStdHandle+を使用するとWaitForSingleObject正常に動作します。ただし、ループに入る前に、必ず適切なフラグを設定し、コンソール バッファもフラッシュしてください。

要するに(エラーチェックなし)

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);
于 2016-02-19T10:03:29.653 に答える
3

これはそれを行う必要があります:

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;
}
于 2014-02-13T08:43:05.673 に答える
2

誰かがクロムネイティブメッセージングホストを書いていて、ブロックせずに標準入力に入力があるかどうかを確認するソリューションを探している場合、これは完璧に機能します:

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);
    }
}
于 2016-01-28T11:48:40.310 に答える
1

GetStdHandle()stdin ハンドルを取得するために使用します。その後、次のいずれかを実行できます。

  1. WaitForSingleObject()stdin ハンドル自体を使用して、読み取り可能なコンソール入力があることを検出し、必要に応じてそこから読み取ります。

  2. GetNumberOfConsoleInputEvents()ループ内で stdin ハンドルに対してまたはを使用PeekConsoleInput()して、読み取り可能なデータがあるかどうかを判断し、必要に応じてそこから読み取ります。

  3. イベント ハンドルを含む構造体で使用ReadFile()OVERLAPPED、イベント ハンドルを使用しWaitForSingleObject()て読み取りがタイムアウトしたかどうかを検出します。

いずれにせよ、stdin がリダイレクトされている場合は注意してください。GetStdHandle()コンソール I/O 以外にリダイレクトされた場合、コンソール関数でハンドルを使用することはできません。ファイルにリダイレクトされる場合は、ReadFile().

于 2013-11-13T20:53:36.207 に答える
0

GetStdHandleコンソールへのハンドルを取得するには関数が必要です。その後WaitForSingleObject、タイムアウトを使用して、そのハンドルでイベントが発生するのを待つことができます。

于 2013-11-13T14:04:21.823 に答える