如何使用我自己的c#app捕获,记录和显示任意进程的控制台输出?

时间:2011-10-11 17:03:53

标签: c# stdout

我想在C#程序上运行一个特定的运行文件,并在屏幕上显示输出并将其保存在文件中。

不希望将输出保存在文件中,然后在屏幕上显示。

我希望他们两个一起发生。

我知道通过“tee”做到这一点的方法但是我每次尝试都失败了。

任何人都可以通过使用“tee”给我一个例子(有效)吗?

4 个答案:

答案 0 :(得分:2)

主要问题是,您是否可以控制要记录和显示其输出的程序的源代码?

如果是这样,那么你有几个选择。可能最简单的方法是使用复合TextWriter“劫持”Console的输出流:

public class CompoundWriter:TextWriter
{
    public readonly List<TextWriter> Writers = new List<TextWriter>();

    public override void WriteLine(string line)
    {
        if(Writers.Any())
            foreach(var writer in Writers)
                writer.WriteLine(line);
    }

    //override other TextWriter methods as necessary
}

...

//When the program starts, get the default Console output stream
var consoleOut = Console.Out;

//Then replace it with a Compound writer set up with a file writer and the normal Console out.
var compoundWriter = new CompoundWriter();
compoundWriter.Writers.Add(consoleOut);
compoundWriter.Writers.Add(new TextWriter("c:\temp\myLogFile.txt");

Console.SetOut(compoundWriter);

//From now on, any calls to Console's Write methods will go to your CompoundWriter,
//which will send them to the console and the file.

您还可以使用Trace侦听器来处理您想要去两个地方的任何输出:

Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Trace.Listeners.Add(new TextWriterTraceListener(File.Open("C:\temp\myLogFile.txt");

//replace any call to Console.WriteLine() with Trace.WriteLine()

如果您无法控制想要“tee”的控制台应用程序的源代码,并且控制台应用程序在执行过程中不需要任何输入,那么您可以使用命名管道来获取应用程序的输出并重定向它。

var appLocation = @"C:\temp\myApp.exe";
 var pipeName = "ConsoleNamedPipe";

 using(var namedPipe = new NamedPipeServerStream(pipeName, PipeDirection.In))
 {

   var info = new ProcessStartInfo
                   {
                       FileName = "cmd.exe",
                       Arguments = String.Format(@"/C {1} >>\\.\pipe\{0}",
                                                 pipeName, appLocation),                                                             
                       WindowStyle = ProcessWindowStyle.Hidden
                   };
    ConsoleProcess = Process.Start(info);
    pipe.WaitForConnection();
    using (var reader = new StreamReader(pipe))
    {
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            Console.WriteLine(line);
            myFileWriter.WriteLine(line);                        
        }
    }
 }

这是一个非常简单的例子;你可以使用双向管道提供更多的交互,但是你需要更多的代码。

答案 1 :(得分:0)

您可以尝试以下操作:

C:\you_csharp_program.exe arg1 arg2 arg3 |tee filename

答案 2 :(得分:0)

我不打算写出具体的代码示例,但我会告诉您,您可以查看日志框架,例如log4net,它具有控制台和文件追加程序,可以执行您想要的操作。您的代码中没有错误的日志语句Log.Debug(“some message”)将log4net配置设置为使用您想要的任意数量的appender并让它一次性将消息写入所有源,例如屏幕,文件, db,同时给大家发电子邮件。

我似乎错过了关于让它与Tee一起工作的问题的最后一句,所以我的答案可能无效。

答案 3 :(得分:0)

阐述KeithS的答案,这是一个有效的实施方案。它应该适用于所有Write / WriteLine调用,而不必覆盖每个重载,因为TextWriter的当前(4.0,我假设更早)实现指向通过Write(char)的所有写入。

public class TeeTextWriter : TextWriter
{
    readonly TextWriter[] _redirectTo;

    public override Encoding Encoding { get { return Encoding.UTF8; } }

    public TeeTextWriter(params TextWriter[] redirectTo)
    {
        _redirectTo = redirectTo ?? new TextWriter[0];
    }

    public override void Write(char value)
    {
        foreach (var textWriter in _redirectTo)
        {
            textWriter.Write(value);
        }
    }
}

用法:

var realConsoleStream = Console.Out;
using (var fileOut = new StreamWriter(outFileName, false))
{
    Console.SetOut(new TeeTextWriter(fileOut, realConsoleStream));
    try
    {
        Console.WriteLine("Test");
    }
    finally
    {
        Console.SetOut(realConsoleStream);
    }
}