从 .NET 应用程序 (C#) 捕获控制台输出

Posted

技术标签:

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

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

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

【问题讨论】:

Process.start: how to get the output?的可能重复 请查看两个问题的日期,看看哪一个是“重复的” “可能重复”是一种清理方法 - 关闭类似问题并保留最佳答案。日期不是必需的。见Should I vote to close a duplicate question, even though it's much newer, and has more up to date answers?如果你同意它需要澄清,请投票Add clarification link to "Possible duplicate" automated comment 【参考方案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/(此博客还包含有关如何使用新进程的控制台输出的详细信息)

【讨论】:

从那时起,我添加了对使用 ConsoleOut 的更多支持(在这种情况下,如果您自己启动 .NET 进程)。看看:How to use the Console output in the C# REPL,Adding 'Console Out' to VisualStudio IDE as a native Window,Viewing 'Console Out' messages created inside UserControls【参考方案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";可能不存在!

以上是关于从 .NET 应用程序 (C#) 捕获控制台输出的主要内容,如果未能解决你的问题,请参考以下文章

视频捕获 + 上传 + 处理 + 流回 - .NET & C#

如何从 Python Tkinter 应用程序捕获到控制台的任何输出?

将图像捕获照片从电话间隙传输到 C# asp.net Web 服务

如何从Python Tkinter应用程序中捕获任何输出到控制台?

SharpPcap - 从标准输出捕获

C#控制台程序怎么实现输出彩色字体?