Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# console application stdin/stdout redirection

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.

like image 403
Stevo Avatar asked Dec 08 '12 14:12

Stevo


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Is C programming hard?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.


1 Answers

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.

like image 87
David Heffernan Avatar answered Oct 11 '22 23:10

David Heffernan