Windows: how do I let the child process read the anonymous channel without closing it?

advertisements

As per subject I'm trying to develop a simple piped parent/child program. Main purpose of this program is to keep the child process alive and use std::cin and std::cout to communicate between parent/child processes.

On Linux all of this works quite well.

On Windows I've been following the example here and there's one peculiar difference with Linux: one has to invoke

CloseHandle(g_hChildStd_IN_Wr)

To write to the child pipe and flush it. This has the side effect to close the pipe, thus terminating my in-connection to the child process.
I've also tried to use FlushFileBuffers but it doesn't work.

Any idea how can I flush the buffer without having to close the anonymous pipe?

Below sources of both Parent and Child processes.
If the code of the parent process is basically the one in the example above:

// IN_Wr_ is initialized as below with bInheritHandle=TRUE
::CreatePipe(&IN_Rd_, &IN_Wr_, &saAttr, 0);
// and
::SetHandleInformation(IN_Wr_, HANDLE_FLAG_INHERIT, 0)
// When I spawn the child process I do
STARTUPINFO         siStartInfo = {0};
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = INVALID_HANDLE_VALUE;
siStartInfo.hStdOutput = OUT_Wr_;
siStartInfo.hStdInput = IN_Rd_;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
...
// then in order to write to std::cin
const DWORD reqSz = static_cast<DWORD>(std::strlen(request));
DWORD       written = 0;
while(true) {
    DWORD   curWritten = 0;
    if(!WriteFile(IN_Wr_, request + written, reqSz-written, &curWritten, NULL))
        throw std::runtime_error("Error on WriteFile");
    written += curWritten;
    if(written == reqSz) {
        // all written, done
        break;
    }
}
::FlushFileBuffers(IN_Wr_);
// only when I do this CloseHandle then the child process
// is able to read data
::CloseHandle(IN_Wr_);

this child code is a simple echo server, along the lines of:

buif[2048+1];
while(std::cin) {
    std::cin.read(buf, 2048);
    const auto  rb = std::cin.gcount();
    buf[rb] = '\0';
    std::cout << buf << std::endl; // this does flush
}


Here's your problem:

std::cin.read(buf, 2048);

It's doing exactly what you've asked it to: waiting until it has read 2048 characters or reaches the end of file. You're not sending 2048 characters, so nothing happens until the server closes the pipe, which counts as the end of file in this context.

Instead, you should be using something like getline(s, 2048, '\0') which will stop reading when it sees a null character. (And, of course, you will need to modify the sender so that it writes that null character at the end of the string.)

Alternatively, you could use the native API: ReadFile has the semantics you seem to be wanting. Ideally you would use a message-mode pipe, which is designed precisely for this sort of use.