我如何用我自己的c#应用程序捕获、记录和显示任意进程的控制台输出

本文关键字:任意 显示 进程 输出 控制台 记录 自己的 何用我 应用程序 | 更新日期: 2023-09-27 18:11:11

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

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

我希望这两件事同时发生。

我知道用"tee"来做这件事的方法,但每次我都失败了。

谁能给我一个例子(有效)使用"tee"?

我如何用我自己的c#应用程序捕获、记录和显示任意进程的控制台输出

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

如果是,那么您有几个选择。可能最简单的方法是使用复合textwwriter"劫持"控制台的输出流:

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

这是一个非常简单的例子;您可以通过使用双向管道提供更多的交互,但这将需要在您的端编写更多的代码。

您可以尝试以下操作:

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

我不打算写出具体的代码示例,但我会告诉你,你可以看看日志框架,比如log4net,它有控制台和文件appender,可以做你想做的事情。你不能在你的代码日志中错误的记录语句。调试("一些消息")设置log4net配置,使用任意数量的appender,并让它一次将消息写入所有源,例如screen, file, db和email。

我似乎错过了问题的最后一句话,所以我的回答可能无效。

详细说明keith的回答,这是一个工作实现。它应该适用于所有的Write/WriteLine调用,而不必覆盖每个重载,因为当前(4.0,我假设更早)的textwwriter实现通过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);
    }
}