19

I have an external dll written in C# and I studied from the assemblies documentation that it writes its debug messages to the Console using Console.WriteLine.

this DLL writes to console during my interaction with the UI of the Application, so i don't make DLL calls directly, but i would capture all console output , so i think i got to intialize in form load , then get that captured text later.

I would like to redirect all the output to a string variable.

I tried Console.SetOut, but its use to redirect to string is not easy.

geogeek
  • 1,274
  • 3
  • 25
  • 42

5 Answers5

36

As it seems like you want to catch the Console output in realtime, I figured out that you might create your own TextWriter implementation that fires an event whenever a Write or WriteLine happens on the Console.

The writer looks like this:

    public class ConsoleWriterEventArgs : EventArgs
    {
        public string Value { get; private set; }
        public ConsoleWriterEventArgs(string value)
        {
            Value = value;
        }
    }

    public class ConsoleWriter : TextWriter
    {
        public override Encoding Encoding { get { return Encoding.UTF8; } }

        public override void Write(string value)
        {
            if (WriteEvent != null) WriteEvent(this, new ConsoleWriterEventArgs(value));
            base.Write(value);
        }

        public override void WriteLine(string value)
        {
            if (WriteLineEvent != null) WriteLineEvent(this, new ConsoleWriterEventArgs(value));
            base.WriteLine(value);
        }

        public event EventHandler<ConsoleWriterEventArgs> WriteEvent;
        public event EventHandler<ConsoleWriterEventArgs> WriteLineEvent;
    }

If it's a WinForm app, you can setup the writer and consume its events in the Program.cs like this:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        using (var consoleWriter = new ConsoleWriter())
        {
            consoleWriter.WriteEvent += consoleWriter_WriteEvent;
            consoleWriter.WriteLineEvent += consoleWriter_WriteLineEvent;

            Console.SetOut(consoleWriter);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

    static void consoleWriter_WriteLineEvent(object sender, Program.ConsoleWriterEventArgs e)
    {
        MessageBox.Show(e.Value, "WriteLine");
    }

    static void consoleWriter_WriteEvent(object sender, Program.ConsoleWriterEventArgs e)
    {
        MessageBox.Show(e.Value, "Write");
    }
huysentruitw
  • 27,376
  • 9
  • 90
  • 133
  • this DLL writes to console during my interaction with the UI of the Application, so i don't make DLL calls directly, but i would capture all console output , so i think i got to intialize in form load , then get that captured text later. – geogeek Aug 11 '12 at 04:50
  • Is it a WinForms app? When in the program flow do you need the output? – huysentruitw Aug 11 '12 at 04:51
  • yes it's a c# winform app , i need to capture console output after an GUI event. – geogeek Aug 11 '12 at 04:56
  • i'm using RAPI library, i would like to get console output just after the RAPIDisconnected event , or during the application lifetime. – geogeek Aug 11 '12 at 05:00
  • 2
    @geogeek: Answer modified. I wrote a simple class based on TextWriter that implements event handlers for Write and WriteLine that helps you to catch console output as it comes in. – huysentruitw Aug 11 '12 at 05:11
  • i tested the code severale times , there's no errors , but there's no messagebox printing the console output , Thanks a lot @WouterH – geogeek Aug 11 '12 at 05:36
  • Try to override other methods in the ConsoleWriter class like I did for Write and WriteLine – huysentruitw Aug 11 '12 at 05:39
  • In order for this to work in your console app, you need to set Console.SetOut(new ConsoleWriter()); – David C Fuchs Mar 04 '20 at 22:12
  • 1
    @DavidCFuchs do you mean the consolewriter that is already created cannot be passed to the SetOut like in my example? and you need to create a new, unused, instance? – huysentruitw Mar 05 '20 at 15:55
  • @huysentruitw Not at all, I didn't see where you explained that SetOut() needs to be set. Adding the following two lines at the top of the console code section would resolve that .... ConsoleWriter passThis = new ConsoleWriter(); ... Console.SetOut(passThis); – David C Fuchs Mar 06 '20 at 21:32
22

It basically amounts to the following:

var originalConsoleOut = Console.Out; // preserve the original stream
using(var writer = new StringWriter())
{
    Console.SetOut(writer);

    Console.WriteLine("some stuff"); // or make your DLL calls :)

    writer.Flush(); // when you're done, make sure everything is written out

    var myString = writer.GetStringBuilder().ToString();
}

Console.SetOut(originalConsoleOut); // restore Console.Out

So in your case you'd set this up before making calls to your third-party DLL.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
  • this DLL writes to console during my interaction with the UI of the Application, so i don't make DLL calls directly, but i would capture all console output , so i think i got to intialize in form load , then get that captured text later. – geogeek Aug 11 '12 at 04:11
4

You can also call SetOut with Console.OpenStandardOutput, this will restore the original output stream:

Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
Ricardo Peres
  • 13,724
  • 5
  • 57
  • 74
2

Or you can wrap it up in a helper method that takes some code as an argument run it and returns the string that was printed. Notice how we gracefully handle exceptions.

public string RunCodeReturnConsoleOut(Action code) 
{
  string result;
  var originalConsoleOut = Console.Out;
  try 
  {
    using (var writer = new StringWriter()) 
    {
      Console.SetOut(writer);
      code();
      writer.Flush();
      result = writer.GetStringBuilder().ToString();
    }
    return result;
  }
  finally 
  {
    Console.SetOut(originalConsoleOut); 
  }
}
Carlo V. Dango
  • 13,322
  • 16
  • 71
  • 114
0

Using solutions proposed by @Adam Lear and @Carlo V. Dango I created a helper class:

public sealed class RedirectConsole : IDisposable
{
    private readonly Action<string> logFunction;
    private readonly TextWriter oldOut = Console.Out;
    private readonly StringWriter sw = new StringWriter();

    public RedirectConsole(Action<string> logFunction)
    {
        this.logFunction = logFunction;
        Console.SetOut(sw);
    }

    public void Dispose()
    {
        Console.SetOut(oldOut);
        sw.Flush();
        logFunction(sw.ToString());
        sw.Dispose();
    }
}

which can be used in the following way:

public static void MyWrite(string str)
{
    // print console output to Log/Socket/File
}

public static void Main()
{
    using(var r = new RedirectConsole(MyWrite)) {
        Console.WriteLine("Message 1");
        Console.WriteLine("Message 2");
    }
    // After the using section is finished,
    // MyWrite will be called once with a string containing all messages,
    // which has been written during the using section,
    // separated by new line characters
}
PolarBear
  • 1,117
  • 15
  • 24