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

从.NET应用程序(C#)捕获控制台输出,c#,.net,debugging,console,C#,.net,Debugging,Console,如何从.NET应用程序调用控制台应用程序并捕获控制台中生成的所有输出 (请记住,我不想先将信息保存在文件中,然后再重新列出,因为我希望以实时方式接收它。)使用属性可以很容易地实现这一点。完整样本包含在链接的MSDN文档中;唯一需要注意的是,您可能还必须重定向标准错误流才能查看应用程序的所有输出 Process compiler = new Process(); compiler.StartInfo.FileName = "csc.exe"; compiler.StartInfo.Argument

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


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

使用属性可以很容易地实现这一点。完整样本包含在链接的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();
用于在创建控制台进程时重定向输出

然后可以使用读取程序输出

第二个链接有一个示例代码,说明如何执行此操作。

来自:


我在(开源项目)中添加了许多帮助器方法,允许您通过控制台输出和输入轻松编写与另一个进程的交互脚本(请参阅)

允许查看当前进程(在现有控件或弹出窗口中)的控制台输出的API可能对您也很有用。有关更多详细信息,请参阅本博客:(本博客还包含如何使用新进程的控制台输出的详细信息)

是一个开源库,专门用于回答这个问题。它捕获控制台中生成的所有输出,并提供启动和关闭控制台应用程序的简单界面

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

也可作为

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

// 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();
}
//异步运行ping.exe并在回调中将往返时间返回给调用方
公共静态void PingUrl(字符串url、操作replyHandler)
{
var regex=new regex((time=| Average=)(?*ms)”,RegexOptions.Compiled);
var app=新控制台app(“ping”,url);
app.ConsoleOutput+=(o,args)=>
{
var match=regex.match(args.Line);
如果(匹配成功)
{
var roundtripTime=match.Groups[“time”].Value;
replyHandler(往返时间);
}
};
app.Run();
}

添加了
process.StartInfo.*CreateNoWindow**=true
超时

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;
        }
    }
}

这比从@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");
}

我制作了一个被动版本,它接受stdOut和StdErr的回调。
onStdOut
onStdErr
是异步调用的,
数据到达时(流程退出前)。


从那时起,我增加了对使用ConsoleOut的更多支持(在本例中,如果您自己启动.NET进程)。请看:,,如果您不想在末尾添加额外的新行,只需使用
Console.Write
。需要注意的是,如果您将ReadToEnd()与具有提示用户输入功能的控制台应用程序结合使用,则。例如:覆盖文件:Y还是N?etc然后ReadToEnd可能导致内存泄漏,因为进程在等待用户输入时从不退出。捕获输出的更安全方法是使用process.OutputDataReceived事件处理程序,并让流程通知您的应用程序要接收的输出。如果代码部署到azure webapp,如何捕获,因为compiler.StartInfo.FileName=“csc.exe”;可能不存在!如果代码部署到azure webapp,如何捕获,因为compiler.StartInfo.FileName=“csc.exe”;可能不存在!谢谢你,找了很久了!非常感谢。这太完美了。你将在我的应用程序的感谢名单中获得荣誉。这是容易理解的、漂亮的代码。我唯一的挑剔是你不必要地添加了新行。“writeline”为您添加了一个,因此净效果是您捕获的输出每隔一行插入一个空行。的可能重复请查看两个问题上的日期,并查看哪一个是“重复”的“可能重复”是一种清理方法-关闭类似的问题并保留一个最佳答案。日期不是必需的。查看您是否同意需要澄清。请投票决定何时使用
StandardOutput.ReadToEnd()
在应用程序结束前不会返回下一条语句。因此,WaitForExit中的超时(TimeOutMillimes)不起作用!(您的代码将挂起!)
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");
}
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);
        }
    }
RunProcess(
    executable,
    args,
    s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
    s => { Console.ForegroundColor = ConsoleColor.Red;   Console.WriteLine(s); } 
);