【问题标题】:Capturing console output from a .NET application (C#)从 .NET 应用程序 (C#) 捕获控制台输出
【发布时间】:2010-09-16 06:06:40
【问题描述】:

如何从我的 .NET 应用程序调用控制台应用程序并捕获控制台中生成的所有输出?

(请记住,我不想先将信息保存在文件中,然后再重新列出,因为我希望收到它作为实时信息。)

【问题讨论】:

标签: c# .net debugging console


【解决方案1】:

这比 @mdb 接受的答案有所改进。具体来说,我们还捕获过程的错误输出。此外,我们通过事件捕获这些输出,因为如果您想捕获 错误和常规输出,ReadToEnd() 不起作用。我花了一段时间才完成这项工作,因为它实际上还需要在Start() 之后调用BeginxxxReadLine()

异步方式:

using System.Diagnostics;

Process process = new Process();

void LaunchProcess()
{
    process.EnableRaisingEvents = true;
    process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
    process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
    process.Exited += new System.EventHandler(process_Exited);

    process.StartInfo.FileName = "some.exe";
    process.StartInfo.Arguments = "param1 param2";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;

    process.Start();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();          

    //below line is optional if we want a blocking call
    //process.WaitForExit();
}

void process_Exited(object sender, EventArgs e)
{
    Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString()));
}

void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}

void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}

【讨论】:

  • 谢谢,找这个很久了!
  • 谢谢。这是完美的。
  • 您将在我的申请的感谢名单中获得荣誉。
  • 这是易于理解的漂亮代码。我唯一的挑剔是你不必要地添加换行符。 “writeline”为您添加了一个,因此最终效果是您捕获的输出每隔一行插入一个空行。
  • 有同步的方法吗?
【解决方案2】:

我制作了一个响应式版本,它接受 stdOut 和 StdErr 的回调。
onStdOutonStdErr 被异步调用,
一旦数据到达(在进程退出之前)。

public static Int32 RunProcess(String path,
                               String args,
                       Action<String> onStdOut = null,
                       Action<String> onStdErr = null)
    {
        var readStdOut = onStdOut != null;
        var readStdErr = onStdErr != null;

        var process = new Process
        {
            StartInfo =
            {
                FileName = path,
                Arguments = args,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardOutput = readStdOut,
                RedirectStandardError = readStdErr,
            }
        };

        process.Start();

        if (readStdOut) Task.Run(() => ReadStream(process.StandardOutput, onStdOut));
        if (readStdErr) Task.Run(() => ReadStream(process.StandardError, onStdErr));

        process.WaitForExit();

        return process.ExitCode;
    }

    private static void ReadStream(TextReader textReader, Action<String> callback)
    {
        while (true)
        {
            var line = textReader.ReadLine();
            if (line == null)
                break;

            callback(line);
        }
    }


示例用法

以下将运行executableargs 并打印

  • stdOut 为白色
  • stdErr 为红色

到控制台。

RunProcess(
    executable,
    args,
    s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
    s => { Console.ForegroundColor = ConsoleColor.Red;   Console.WriteLine(s); } 
);

【讨论】:

    【解决方案3】:

    在创建控制台进程时使用ProcessInfo.RedirectStandardOutput 重定向输出。

    那么你就可以使用Process.StandardOutput来读取程序输出了。

    第二个链接有一个示例代码如何做到这一点。

    【讨论】:

      【解决方案4】:

      添加了process.StartInfo.**CreateNoWindow** = true;timeout

      private static void CaptureConsoleAppOutput(string exeName, string arguments, int timeoutMilliseconds, out int exitCode, out string output)
      {
          using (Process process = new Process())
          {
              process.StartInfo.FileName = exeName;
              process.StartInfo.Arguments = arguments;
              process.StartInfo.UseShellExecute = false;
              process.StartInfo.RedirectStandardOutput = true;
              process.StartInfo.CreateNoWindow = true;
              process.Start();
      
              output = process.StandardOutput.ReadToEnd();
      
              bool exited = process.WaitForExit(timeoutMilliseconds);
              if (exited)
              {
                  exitCode = process.ExitCode;
              }
              else
              {
                  exitCode = -1;
              }
          }
      }
      

      【讨论】:

      • 当您使用StandardOutput.ReadToEnd() 时,它不会返回到下一条语句,直到应用程序结束。所以你在 WaitForExit(timeoutMilliseconds) 中的超时不起作用! (你的代码会挂起!)
      【解决方案5】:

      ConsoleAppLauncher 是专门为回答该问题而设计的开源库。它捕获控制台中生成的所有输出,并提供简单的界面来启动和关闭控制台应用程序。

      每当控制台将新行写入标准/错误输出时,都会触发 ConsoleOutput 事件。这些行排队并保证遵循输出顺序。

      也可通过NuGet package 获得。

      获取完整控制台输出的示例调用:

      // Run simplest shell command and return its output.
      public static string GetWindowsVersion()
      {
          return ConsoleApp.Run("cmd", "/c ver").Output.Trim();
      }
      

      带有实时反馈的示例:

      // Run ping.exe asynchronously and return roundtrip times back to the caller in a callback
      public static void PingUrl(string url, Action<string> replyHandler)
      {
          var regex = new Regex("(time=|Average = )(?<time>.*?ms)", RegexOptions.Compiled);
          var app = new ConsoleApp("ping", url);
          app.ConsoleOutput += (o, args) =>
          {
              var match = regex.Match(args.Line);
              if (match.Success)
              {
                  var roundtripTime = match.Groups["time"].Value;
                  replyHandler(roundtripTime);
              }
          };
          app.Run();
      }
      

      【讨论】:

        【解决方案6】:

        来自PythonTR - Python Programcıları Derneği, e-kitap, örnek

        Process p = new Process();   // Create new object
        p.StartInfo.UseShellExecute = false;  // Do not use shell
        p.StartInfo.RedirectStandardOutput = true;   // Redirect output
        p.StartInfo.FileName = "c:\\python26\\python.exe";   // Path of our Python compiler
        p.StartInfo.Arguments = "c:\\python26\\Hello_C_Python.py";   // Path of the .py to be executed
        

        【讨论】:

          【解决方案7】:

          我在O2 Platform(开源项目)中添加了一些辅助方法,允许您通过控制台输出和输入轻松编写与另一个进程的交互脚本(请参阅http://code.google.com/p/o2platform/source/browse/trunk/O2_Scripts/APIs/Windows/CmdExe/CmdExeAPI.cs

          对您也有用的 API 可能允许查看当前进程的控制台输出(在现有控件或弹出窗口中)。有关详细信息,请参阅此博客文章:http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/(此博客还包含有关如何使用新进程的控制台输出的详细信息)

          【讨论】:

          【解决方案8】:

          使用ProcessStartInfo.RedirectStandardOutput 属性可以很容易地实现这一点。完整的示例包含在链接的 MSDN 文档中;唯一需要注意的是,您可能还必须重定向标准错误流才能查看应用程序的所有输出。

          Process compiler = new Process();
          compiler.StartInfo.FileName = "csc.exe";
          compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs";
          compiler.StartInfo.UseShellExecute = false;
          compiler.StartInfo.RedirectStandardOutput = true;
          compiler.Start();    
          
          Console.WriteLine(compiler.StandardOutput.ReadToEnd());
          
          compiler.WaitForExit();
          

          【讨论】:

          • 如果您不想在末尾多出新行,请改用Console.Write
          • 应该注意,如果您将 ReadToEnd() 与能够提示用户输入的控制台应用程序结合使用。例如:覆盖文件:Y 还是 N?等等 然后 ReadToEnd 可能导致内存泄漏,因为进程在等待用户输入时永远不会退出。捕获输出的更安全的方法是使用 process.OutputDataReceived 事件处理程序,并让进程通知您的应用程序要接收输出。
          • 如果代码部署到azure webapp,如何捕获,因为compiler.StartInfo.FileName = "csc.exe";可能不存在!
          • 如果代码部署到azure webapp,如何捕获,因为compiler.StartInfo.FileName = "csc.exe";可能不存在!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-06
          • 2016-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多