3

STDINの無名パイプから読み取るC++(Windows)クライアントコンソールアプリケーションを作成しています。私のプログラムを次のように使用できるようにしたいと思います。

echo input text here | my_app.exe

パイプで送られるテキストを使ってアプリで何かをします

また

my_app.exe

次に、パイプからの入力の代わりに、アプリ内でデフォルトのテキストを使用します。

私は現在、最初の状況でSTDINのパイプから正常に読み取るコードを持っています。

#include <Windows.h>
#include <iostream>
#include <string>

#define BUFSIZE 4096

int main(int argc, const char *argv[]) {
    char char_buffer[BUFSIZE]; 
    DWORD bytes_read;
    HANDLE stdin_handle;
    BOOL continue_reading;
    unsigned int required_size;
    bool read_successful = true;

    stdin_handle = GetStdHandle(STD_INPUT_HANDLE);

    if (stdin_handle == INVALID_HANDLE_VALUE) {
        std::cout << "Error: invalid handle value!\n\n";
    } else {
        continue_reading = true;

        while (continue_reading) { 
            continue_reading = ReadFile(stdin_handle, char_buffer, BUFSIZE,
                &bytes_read, NULL); 

            if (continue_reading) {
                if (bytes_read != 0) {
                    // Output what we have read so far
                    for (unsigned int i = 0; i < bytes_read; i++) {
                        std::cout << char_buffer[i];
                    }
                } else {
                    continue_reading = false;
                }
            }
        }
    }

    return 0;
}

私は、匿名パイプを使用する唯一のオプションは、ReadFileを使用してブロッキング読み取りを行うことであることを知っています。私が正しく理解していれば、それを呼び出す方法に関して、ReadFileは、パイプのもう一方の端で書き込み操作の終了を検出するまで(おそらく、ある種の「書き込みの終了」を読み取るまで)、STDINのバッファーから読み取りを続けます。トークン??)。ReadFileを呼び出すに、STDINをチェックできる何かがパイプされている場合に、バッファーにある種の「書き込み開始」トークンがあるかどうかを知りたいです。この場合、ReadFileの呼び出しをスキップして、デフォルトのテキストを使用できます。

これを行う方法がない場合は、パイプをチェックせずにデフォルトのテキストを使用する(またはその逆)ことを示すコマンドライン引数をいつでも渡すことができますが、それを行う方がはるかに望ましいです私が指定した方法。

4

3 に答える 3

5

を見てくださいPeekNamedPipe()。その名前にもかかわらず、名前付きパイプと無名パイプの両方で機能します。

int main(int argc, const char *argv[])
{
    char char_buffer[BUFSIZE]; 
    DWORD bytes_read;
    DWORD bytes_avail;
    DWORD dw;
    HANDLE stdin_handle;
    bool is_pipe;

    stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
    is_pipe = !GetConsoleMode(stdin_handle, &dw);

    if (stdin_handle == INVALID_HANDLE_VALUE) {
        std::cout << "Error: invalid handle value!\n\n";
    } else {
        while (1) { 
            if (is_pipe) {
                if (PeekNamedPipe(stdin_handle, NULL, 0, NULL, &bytes_avail, NULL)) {
                    if (bytes_avail == 0) {
                        Sleep(100);
                        continue;
                    }
                }
            }

            if (!ReadFile(stdin_handle, char_buffer, min(bytes_avail, BUFSIZE), &bytes_read, NULL)) {
                break; 
            }

            if (bytes_read == 0) {
                break;
            }

            // Output what we have read so far
            for (unsigned int i = 0; i < bytes_read; i++) {
                std::cout << char_buffer[i];
            }
        }
    }

    return 0;
}
于 2013-01-24T01:16:41.203 に答える
3

ここで実際にやろうとしているのは、コンソール入力(デフォルト値を使用する場合)とパイプ入力(パイプからの入力を使用する場合)があるかどうかを判断することです。

入力の準備ができているかどうかを確認するのではなく、直接テストすることをお勧めします。パイプにデータがあるかどうかをスニッフィングしようとする場合の落とし穴は、ソースアプリの出力生成が遅い場合、アプリがないという理由だけで誤った仮定を行う可能性があることです。 t入力はまだ利用可能です。(事前入力により、アプリが入力可能かどうかを確認する前に、ユーザーがコンソールSTDINから読み取る準備ができている文字を入力した可能性もあります。)

また、パイプだけでなく、ファイルのリダイレクトでアプリを使用できるようにすると便利な場合があることに注意してください。例:

myapp.exe < some_input_file

UNIXでこの「対話型モードとリダイレクトされた入力で使用される」テストを実行する古典的な方法は、isatty()を使用することです。そして幸いなことに、WindowsCRTには同等のものがあります-function_isatty()を参照してください; または、GetFileType()を使用してGetStdHandle(STD_INPUT_HANDLE)でFILE_TYPE_CHARをチェックします。または、RemyのようにGetConsoleModeを使用します。これは、実際のコンソールハンドルでのみ成功します。

于 2013-01-24T08:25:59.240 に答える
0

これは、同期ReadFile呼び出しを行う2番目のスレッドを使用しているときにI/Oが重複することなく機能します。次に、メインスレッドは任意の時間待機し、上記のように動作します...

お役に立てれば...

于 2013-01-23T23:44:59.720 に答える