重定向标准输出

时间:2014-01-02 16:30:10

标签: c# process output io-redirection

我有一个C#应用程序A,它调用另一个C#应用程序B,该应用程序调用应用程序C的多个实例。

我想将应用程序C的输出重定向到应用程序A的输出。

我已经将应用程序B的输出重新定向到应用程序A的输出中。

现在,在应用程序B的代码中,我重定向了每个进程的输出,并且我将重定向的输出打印到控制台。不幸的是,出于某种原因,什么都没有打印出来。

(我目前正在测试它而不使用应用A - 我只运行应用B)。

以下是代码:

private void runSingleFile (string execFile, string commandArgs)
{
      Process processToRun = new Process();
      processToRun .StartInfo.FileName = execFile;
      processToRun .StartInfo.Arguments = commandArgs;
      processToRun .StartInfo.UseShellExecute = false;
      processToRun .StartInfo.RedirectStandardOutput = true;
      processToRun .OutputDataReceived += outputRedirection;
      processToRun.Start();
      Console.WriteLine("");
      Thread.Sleep(100);  
      processToRun.BeginOutputReadLine();
}

private void outputRedirection(object sendingProcess, DataReceivedEventArgs outLine)
{
      try
      {
           if (outLine.Data != null)
              Console.WriteLine(outLine.Data);
      }
      catch (Exception ex)
      {
          Console.WriteLine(ex.Message);
          return;
      }
}

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

您发布的代码有效。给出测试程序

// ConsoleApplication2.exe
static void Main(string[] args)
{
    Console.WriteLine("Test1...");
    Console.WriteLine("Test2...");
    System.Threading.Thread.Sleep(100);
    Console.WriteLine("Test3...");
}

称为:

static void Main(string[] args)
{
    runSingleFile("ConsoleApplication2.exe", "");
    runSingleFile("ConsoleApplication2.exe", "");
    runSingleFile("ConsoleApplication2.exe", "");
    Console.ReadLine();
}

产生输出:

enter image description here

显然,在代码的另一部分中没有其他东西工作(你没有向我们展示的东西)。

如果应用程序C工作正常,最可能的解释是,在B的所有实例都已完成之前,应用程序C正在终止。您可能需要添加一些代码,使B等待C的所有实例返回。

请注意:

 static void Main(string[] args)
 {
     runSingleFile("ConsoleApplication2.exe", "");
     runSingleFile("ConsoleApplication2.exe", "");
     runSingleFile("ConsoleApplication2.exe", "");
     //Console.ReadLine();   // ** Don't wait!
 }

立即完成,无法返回部分或全部数据(特别是如果您删除了Sleep中的runSingleFile来电。

考虑一下:

 static long processCount = 0;               //ADD

 static void runSingleFile(string execFile, string commandArgs)
 {
    Interlocked.Increment(ref processCount);  //ADD
    Process processToRun = new Process();
    processToRun.StartInfo.FileName = execFile;
    processToRun.StartInfo.Arguments = commandArgs;
    processToRun.StartInfo.UseShellExecute = false;
    processToRun.StartInfo.RedirectStandardOutput = true;
    processToRun.OutputDataReceived += outputRedirection;
    processToRun.EnableRaisingEvents = true;  //ADD
    processToRun.Exited += processExited;     //ADD
    processToRun.Start();
    Console.WriteLine("");
    processToRun.BeginOutputReadLine();
 }

 static void processExited(object sender, EventArgs e)
 {
     Interlocked.Decrement(ref processCount);
 }

 static void Main(string[] args)
 {
     runSingleFile("ConsoleApplication2.exe", "");
     runSingleFile("ConsoleApplication2.exe", "");
     runSingleFile("ConsoleApplication2.exe", "");
     while (Interlocked.Read(ref processCount) > 0)
     {
        System.Threading.Thread.Sleep(100);
     }
 }

上面的内容使应用程序B等到所有生成的进程都返回。这个例子很简单,显然可以改进,但我认为它证明了这个问题,并为解决方案提供了一种方法。

你可能想要使用像WaitHandle.WaitAll()这样更优雅的东西,但是这确实引入了josh注意到输出事件在进程终止之前可能不会触发的问题 - 进程句柄将发出信号表明它已终止但其发布的消息可能仍在队列中。等待Exited事件整理该竞争条件,因为该事件将始终是队列中的最后一条消息(AFAIK)。

另外,请注意这里使用Interlocked函数 - Console.WriteLine是线程安全的,但其他变量访问不是。由于控制台应用程序中没有同步上下文,因此生成的进程引发的事件由线程池中的线程(而不是控制台应用程序的主线程)处理。这介绍了与多线程相关的所有问题,必须妥善管理。

答案 1 :(得分:1)

.net Process对象无法让人们在正确处理IO方面“做正确的事”。

这些页面列出了一些需要解决的问题:

在您的代码示例中,以下是您需要查看的一些项目:

  1. 您需要同时捕获stderr和stdin(如果不使用,后者应立即关闭)。
  2. 您还需要注意,在子进程退出后,您仍然可以在outputRedirection回调上收到活动。