WinAPI C++ client detect write on anonymous pipe before reading

I am writing a C++ (Windows) client console application which reads from an anonymous pipe on STDIN. I would like to be able to use my program as follows:

echo input text here | my_app.exe

and do something in the app with the text that is piped in

OR

my_app.exe

and then use some default text inside of the app instead of the input from the pipe.

I currently have code that successfully reads from the pipe on STDIN given the first situation:

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

I know that my only option with anonymous pipes is to do a blocking read with ReadFile. If I understand correctly, in regard to how I am invoking it, ReadFile will continue to read from the buffer on STDIN until it detects an end of write operation on the other end of the pipe (perhapse reads some sort of "end of write" token??). I would like to know if there is some sort of "beginning write" token that will be in the buffer if something is being piped in which I can check on STDIN BEFORE I call ReadFile. If this were the case I could just skip calling ReadFile and use some default text.

If there is not a way to do this, I can always pass in a command line argument that denotes that I should not check the pipe and just use the default text (or the other way around), but I would much prefer to do it the way that I specified.

Answers


It looks like what you're really trying to do here is to determine whether you've got console input (where you use default value) vs pipe input (where you use input from the pipe).

Suggest testing that directly instead of trying to check if there's input ready: the catch with trying to sniff whether there's data in the pipe is that if the source app is slow in generating output, your app might make an incorrect assumption just because there isn't input yet available. (It might also be possible that, due to typeahead, there's a user could have typed in characters that area ready to be read from console STDIN before your app gets around to checking if input is available.)

Also, keep in mind that it might be useful to allow your app to be used with file redirection, not just pipes - eg:

myapp.exe < some_input_file

The classic way to do this "interactive mode, vs used with redirected input" test on unix is using isatty(); and luckily there's an equivalent in the Windows CRT - see function _isatty(); or use GetFileType() checking for FILE_TYPE_CHAR on GetStdHandle(STD_INPUT_HANDLE) - or use say GetConsoleMode as Remy does, which will only succeed on a real console handle.


Look at PeekNamedPipe(). Despite its name, it works for both named and anonymous pipes.

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

This also works without overlapped I/O while using a second thread, that does the synchronous ReadFile-call. Then the main thread waits an arbitrary amount of time and acts like above...

Hope this helps...


Need Your Help

Tidy returns empty string

php tidy

On my local server, this works fine. However, when I try to do this on the production server, the following returns an empty string:

XSLT transformation - multiple input XML files

xml xslt foreach merge concat

I have a following problem: I have several input XML files, and one of them contains links to the others.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.