8

I am writing an application in C# that at some point starts an application as a child process using the Process class with Asynchronous IO redirection as shown below:

private void AppLaunch_Click(object sender, RoutedEventArgs e)
{

  Process appProcess = new Process();
  appProcess.StartInfo.FileName = currAppPath;
  appProcess.StartInfo.Arguments = "";

  //Setup Redirection
  appProcess.StartInfo.UseShellExecute = false;
  appProcess.StartInfo.ErrorDialog = false;
  appProcess.StartInfo.RedirectStandardError = true;
  appProcess.EnableRaisingEvents = true;
  // Attach Output Handler
  appProcess.ErrorDataReceived += appProc_DataReceived;
  appProcess.Exited += appProc_Exited;
  buildLogConsoleOutputTxtbox.AppendText(currAppPath + "\n");

  appProcess.Start();
  appProcess.BeginErrorReadLine();

}
private void appProc_DataReceived(object sender, DataReceivedEventArgs e)
{
  if (!String.IsNullOrEmpty(e.Data))
  {
    this.appendLogText(e.Data);
  }
}

private void appProc_Exited(object sender, System.EventArgs e)
{
  Process proc = (Process)sender;
  // Wait a short while to allow all console output to be processed and appended
  Thread.Sleep(40);
  this.appendLogText("\n>>");
  proc.Close();
}

private void appendLogText(string logText)
{
  // Use a delegate if called from a different thread,
  // else just append the text directly
  if (buildLogConsoleOutputTxtbox.Dispatcher.CheckAccess())
  {
    // Thread owns the TextBox
    buildLogConsoleOutputTxtbox.AppendText(logText + Environment.NewLine);
  }
  else
  {
    //Invocation Required
    appendLogCallBack appendLog = new appendLogCallBack(buildLogConsoleOutputTxtbox.AppendText);
    buildlogScrollEnd buildlogscrl = new buildlogScrollEnd(buildLogConsoleOutputTxtbox.ScrollToEnd);
    buildLogConsoleOutputTxtbox.Dispatcher.BeginInvoke(appendLog, new object[] { logText + Environment.NewLine });
    buildLogConsoleOutputTxtbox.Dispatcher.BeginInvoke(buildlogscrl);
  }

The Problem with this piece of code is that while I do get the stderr redirected properly to my textbox, This redirection seems to hide the process' stdout output, which I don't want redirected!

If I redirect stdout, I can see it redirected properly, but is it impossible to just redirect stderr and not stdout? I have looked around and googled regarding this topic but all discussions seem to be regarding redirecting stdout ... such as this : How to asynchronously read the standard output stream and standard error stream at once

I would be grateful for any help regarding this!

Community
  • 1
  • 1
bethrezan
  • 106
  • 4
  • 1
    Pretty sure this can't be done - the flag is "redirect all handles", in which case all the standard process handles are now your responsibility. If you want the standard output to write to your parent console then you could just read them and then write them back out to wherever you like (i.e. your console) – Justin Oct 16 '12 at 18:03
  • 1
    But then What is the point of having the following two statements if its always redirected? `StartInfo.RedirectStandardError = true; StartInfo.RedirectStandardOutput = true // or false in this case;` Also I want the stdout to write to the Child console, otherwise I can just add a Console.Writeline() command in the handler right? – bethrezan Oct 16 '12 at 18:06

1 Answers1

2

That is not possible as is - output and error handles are redirected simultaneously. MSDN article STARTUPINFO describes STARTF_USESTDHANDLES flag.

But there is a good news. Preserving child process output is still possible. You just have to:

  • redirect child process output
  • attach to child process console
  • write child process output back to its console

So right after process start invoke

[DllImport("Kernel32.dll", SetLastError = true) ]
static extern uint AttachConsole(int pid);

and then use simple Console.WriteLine in your DataReceived handler.

mikalai
  • 1,746
  • 13
  • 23
  • Thanks for the help! However, whenever I try to do the above using the following Code I keep getting a failure in attaching the process...GetLastError reports error code 0x31 or General Device Failure! Code: `uint consoleStatus = Utilities.AttachConsole(appProcess.Id); int lasterr = Marshal.GetLastWin32Error(); MessageBox.Show("CS:" + consoleStatus.ToString() + "--- ERR:" + lasterr);` – bethrezan Oct 16 '12 at 23:12
  • Sorry for the poor formatting! It doesn't seem to allow me to put in Carriage Returns! – bethrezan Oct 16 '12 at 23:13
  • You have to call to attachconsole after processes is started. What is consoleStatus value? And probably there are other os specifics I'm unaware of. – mikalai Oct 17 '12 at 10:08
  • You need to set UseShellExecute to true for the Verb to be respected and it must be set to 'false' to redirect standard output. You can't do both. I'm pretty sure Windows also won't allow you to redirect standard input/output/error across the admin/non-admin security boundary. You'll have to find a different way to get output from the program running as admin - Reference: http://stackoverflow.com/a/8690661 – Kiquenet Aug 28 '14 at 06:38