WinAPI C ++客户端在读取之前检测匿名管道上的写入

时间:2013-01-23 23:32:49

标签: winapi visual-c++ pipe

我正在编写一个C ++(Windows)客户端控制台应用程序,它从STDIN上的匿名管道读取。我希望能够按如下方式使用我的程序:

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上的缓冲区读取,直到它检测到管道另一端的写操作结束(perhapse读取某种“写入结束”)令牌?)。我想知道是否有某种“开始写入”令牌将在缓冲区中,如果正在管道中我可以检查STDIN BEFORE 我调用ReadFile。如果是这种情况,我可以跳过调用ReadFile并使用一些默认文本。

如果没有办法做到这一点,我总是可以传入一个命令行参数,表示我不应该检查管道并只使用默认文本(或者反过来),但我更喜欢按照我指定的方式去做。

3 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:3)

看起来你真正要做的就是确定你是否有控制台输入(使用默认值)和管道输入(使用管道输入)。

建议直接测试,而不是试图检查输入是否准备就绪:试图嗅探管道中是否存在数据的问题是如果源应用程序生成输出的速度很慢,那么您的应用程序可能会做出错误的假设,因为没有输入尚未可用。 (也可能是由于预先输入,用户可能输入了字符,可以在应用程序到达检查输入是否可用之前准备好从控制台STDIN读取区域。)

另外,请记住,允许您的应用与文件重定向一起使用可能很有用,而不仅仅是管道 - 例如:

myapp.exe < some_input_file

在unix上执行此“交互模式,vs使用重定向输入”测试的经典方法是使用isatty();幸运的是,在Windows CRT中有一个等价物 - 见function _isatty();或者在GetStdHandle(STD_INPUT_HANDLE)上使用GetFileType()检查FILE_TYPE_CHAR - 或者像Remy那样使用GetConsoleMode,这只能在真正的控制台句柄上成功。

答案 2 :(得分:0)

在使用第二个线程执行同步ReadFile调用时,这也可以在没有重叠I / O的情况下工作。然后主线程等待任意数量的时间并像上面一样......

希望这会有所帮助......