From: David Crow on
I've created a console window with redirected stdin and stdout. I send that
window a command (e.g., dir), and then read the results from that window.
This works intermittently. The command I send governs the "timing" of my
code. For example:

CreateProcess("cmd.exe", ...);

// write the command
DWORD dw;
WriteFile(write_stdin, cmd, cmd.GetLength(), &dw, NULL);

// wait for something to show up
do
{
PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);

} while (dw == 0);

// read until no more
do
{
ReadFile(read_stdout, buf, 31, &read, NULL);
PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);
} while (dw != 0);

If I change 31 to 32, it stops working when "dir" is the command. If the
command is "chkdsk /?" instead, I use 5.

I understand that ReadFile() will block until that number of bytes have been
read. Ultimately I'd just wait until the command was finished (and use
PeekNamedPipe() to know how much to read), but I don't know how to check
that, and the console-window buffer is only so big.

Any suggestions?

Thanks.

- DC


From: Tamas Demjen on
David Crow wrote:

> I understand that ReadFile() will block until that number of bytes have been
> read.

It doesn't necessarily have to block, you can opt to use overlapped I/O.

> Ultimately I'd just wait until the command was finished (and use
> PeekNamedPipe() to know how much to read), but I don't know how to check
> that

PeekNamedPipe can return the number of bytes available on the pipe:

DWORD bytesAvailable = 0;
if(PeekNamedPipe(pipeHandle, NULL, 0, NULL, &bytesAvailable, NULL))
...

Just read bytesAvailable number of bytes. It's not going to block
this way, because you're not reading more than what's already there.

Tom
From: David Crow on
I'm currently not using overlapped I/O. From my understanding of it, which
is not great, it was not needed.

Even if I changed the loop to be:

// wait for something to show up after sending command
do
{
PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);
} while (dw == 0);

// read until no more
do
{
ReadFile(read_stdout, buf, dw, &read, NULL);
PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);
} while (dw != 0);

there is a slight delay after the command is issued but before it starts
outputting anything. This results in "dw" being 0 and the second while()
loop exiting after one iteration.

The call to ReadFile() will read once getting the characters that show up
when the command prompt window is opened plus the characters that are in the
command (e.g., chkdsk, ipconfig, dir).

I can put a 1-second delay in between the two while() loops but that does
not seem right.

"Tamas Demjen" <tdemjen(a)yahoo.com> wrote in message
news:%23EV6HNNWKHA.4816(a)TK2MSFTNGP06.phx.gbl...
>
> PeekNamedPipe can return the number of bytes available on the pipe:
>
> DWORD bytesAvailable = 0;
> if(PeekNamedPipe(pipeHandle, NULL, 0, NULL, &bytesAvailable, NULL))
> ...
>
> Just read bytesAvailable number of bytes. It's not going to block
> this way, because you're not reading more than what's already there.
>
> Tom


From: Tamas Demjen on
David Crow wrote:

> // wait for something to show up after sending command
> do
> {
> PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);
> } while (dw == 0);
>
> // read until no more
> do
> {
> ReadFile(read_stdout, buf, dw, &read, NULL);
> PeekNamedPipe(read_stdout, NULL, 0, NULL, &dw, NULL);
> } while (dw != 0);

You have to repeatedly call PeekNamedePipe until the pipe has something
to read. You're doing a ReadFile, followed by an immediate peek. You're
likely to have an empty pipe right after reading. So you have to keep
peeking until your process exits.

I was thinking of something like this (with more error handling added):

bool stillRunning = true;
while(stillRunning)
{
DWORD bytesAvailable = 0;
do
{
DWORD exitCode = 0;
// Check if the process is still running
GetExitCodeProcess(hProcess, &exitCode);
if(exitCode != STILL_ACTIVE)
stillRunning = false;

PeekNamedPipe(hPipe, NULL, 0, NULL, &bytesAvailable, NULL);
}
while(bytesAvailable == 0 && stillRuning);
ReadFile(...);
}

You can add some sleep between peeks to avoid too much CPU utilization:
WaitForSingleObject(hProcess, 100);

Perhaps it would be better to do overlapped I/O in a thread with
WaitForMultipleObjects, and wait until either the process finishes,
or the overlapped I/O triggers. Then you don't have to do polling.

Tom