6

I have an interesting (read: frustrating) problem kicking off a console application from a C# WPF app and redirecting its stdin and stdout.

It is mostly up and working but I seem to end up not getting some data from stdout as soon as I start redirecting stdin.

I'll clarify with an example. If I don't set hStdInput in the STARTUPINFO structure, when I start the child process I receive the following:

MongoDB shell version: 2.2.0
connecting to: test
local:PRIMARY>

Once I set hStdInput however, I just get this:

MongoDB shell version: 2.2.0
connecting to: test

I know that the BackgroundWorker processing stdout is still functioning because if I send something to the process on stdin, it responds accordingly.

use TestDB
switched to db TestDB

So, this is how I create the process:

_processInfo = new ProcessInfo();

bool ok = false;

SECURITY_ATTRIBUTES sattr = new SECURITY_ATTRIBUTES();
sattr.bInheritHandle = 1;
unsafe
{
    sattr.lpSecurityDescriptor = null;
}
sattr.nLength = Marshal.SizeOf(sattr);

IntPtr hWrite;
ok = CreatePipe(out _hReadStdOut, out hWrite, ref sattr, 0);
ok = SetHandleInformation(_hReadStdOut, HANDLE_FLAGS.INHERIT, 0);
IntPtr hRead;
ok = CreatePipe(out hRead, out _hWriteStdIn, ref sattr, 0);
ok = SetHandleInformation(_hWriteStdIn, HANDLE_FLAGS.INHERIT, 0);

var startInfo = new StartupInfo
{
    dwFlags = 0x0001 | 0x0100,
    wShowWindow = 0,
    hStdOutput = hWrite,
    hStdError = hWrite,
    hStdInput = hRead // If this is IntPtr.Zero, I get everything from stdout
};

SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
tSec.nLength = Marshal.SizeOf(tSec);

unsafe
{
    ok = CreateProcess(
        null,
        pathToExeAndArgs,
        ref pSec,
        ref tSec,
        true,
        0,
        IntPtr.Zero,
        null,
        ref startInfo,
        out _processInfo);
}

I have a BackgroundWorker processing stdout on DoWork which reads the pipe like so:

success = ReadFile(
    _hReadStdOut,
    bufPtr,
    1024,
    &read,
    IntPtr.Zero);

I'm not using the .Net Process class because it didn't obtain data from stdout until the console application sent a newline, so I didn't get the prompt back in that case either.

Any help with this greatly appreciated.

Cheers.

Stevo
  • 1,424
  • 11
  • 20

1 Answers1

1

I suspect the following explains what you have observed:

  • When you don't define hStdInput the child process uses the standard input device attached to the console. The child process detects that standard input is an interactive console device and writes a prompt.
  • When you do define hStdInput the child process detects that the standard input is a pipe and so neglects to write a prompt. After all, what's the point of prompting a non-interactive input device?

The child process will use GetFileType(GetStdHandle(STD_INPUT_HANDLE)) to detect what type of device is attached to the standard input. A value of FILE_TYPE_CHAR indicates a console. When you attach a pipe to the standard input then the standard input file type will be FILE_TYPE_PIPE.

My conclusion is that everything is working as designed and intended.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490