Guten Tag!
The problem is: I have some code which prints some text messages via Console class on terminal (command line window). I need this info to be placed in two 'containers' - terminal and text file.
Is there a way of adding output stream to Console class in order to make it output data not only on terminal?
It'd be grate if I wont need to change existing code too much (there are a lot of places where Console.Write() and Console.WriteLine() are used).
This is not a full implementation but it should be enough to get you started down the path you seek.
class Program
{
static void Main(string[] args)
{
DualOut.Init();
Console.WriteLine("Hello");
Console.ReadLine();
}
}
public static class DualOut
{
private static TextWriter _current;
private class OutputWriter : TextWriter
{
public override Encoding Encoding
{
get
{
return _current.Encoding;
}
}
public override void WriteLine(string value)
{
_current.WriteLine(value);
File.WriteAllLines("Output.txt", new string[] { value });
}
}
public static void Init()
{
_current = Console.Out;
Console.SetOut(new OutputWriter());
}
}
If you run this code you will see that "Hello" is printed to both the console and to the file "Output.txt"
You can't do it without changing your code. Simply create another class, for example "MyConsole" and add static methods "Write()" and "WriteLine()" to it. Internally call Console.Write() and Console.WriteLine() methods from your created methods. Also call methods that write text to a file from your methods. Then use your MyConsole class instead of Console for output data. Visual Studio Find/Replace feature will help you changing your code.
You should use StreamWriter. It requires minimum additional code.
Follow this example and it will work.
you need to type " using System.IO" in order for this code to work. Don't forget to write it.
the "YourTextFile.txt" needs the .txt.
In order to see the results in you text file, you need to go to the actual folder in your computer, and open the Text file from there. The text file in Visual Studio will appear empty.
( e.g. c:/documents/visualstudio/projects/ConsoleApplication1/bin/debug/YourTextFile.txt)
Solution
First Create a text file YourTextFile.txt in Visual Studio. Then in the Solution Explorer menu to the right, click on YourTextFile in order to see the Properties below the Solution Explorer. Change the property "Copy to Output Directory" from "Do Not Copy" to "Copy Always". (This is an absolute must)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Answer
{
static void Main(string[] args)
{
string text = "This text will appear on your console and be saved in the txt file";
Console.WriteLine(text);
StreamWriter saveText = new StreamWriter("YourTextFile.txt");
saveText.WriteLine(text);
saveText.Close();
Console.ReadLine();
}
}
}
This should do the job. I don't know what your output in the console looks like, but if it is not just one line, and there are multiple Console.Writelines giving output to the console, then you can use while and if, to check every line of the console and if it is not null to write it out. e.g. while (line != null) followed byif (line != null)(assuming you set "line" to equal the output in your console)
I can't get the post by iamkrillin to work.
But the following does work:
class Program
{
static void Main(string[] args)
{
ConsoleFileOutput cf = new ConsoleFileOutput("Output.txt", Console.Out);
Console.SetOut(cf);
Console.WriteLine("0ne");
Console.WriteLine("two");
Console.WriteLine("three");
cf.Close();
}
public class ConsoleFileOutput : TextWriter
{
#region Fields
private Encoding encoding = Encoding.UTF8;
private StreamWriter writer;
private TextWriter console;
#endregion
#region Properties
public override Encoding Encoding
{
get
{
return encoding;
}
}
#endregion
#region Constructors
public ConsoleFileOutput(string filePath, TextWriter console, Encoding encoding = null)
{
if (encoding != null)
{
this.encoding = encoding;
}
this.console = console;
this.writer = new StreamWriter(filePath, false, this.encoding);
this.writer.AutoFlush = true;
}
#endregion
#region Overrides
public override void Write(string value)
{
Console.SetOut(console);
Console.Write(value);
Console.SetOut(this);
this.writer.Write(value);
}
public override void WriteLine(string value)
{
Console.SetOut(console);
Console.WriteLine(value);
this.writer.WriteLine(value);
Console.SetOut(this);
}
public override void Flush()
{
this.writer.Flush();
}
public override void Close()
{
this.writer.Close();
}
#endregion
#region IDisposable
new public void Dispose()
{
this.writer.Flush();
this.writer.Close();
this.writer.Dispose();
base.Dispose();
}
#endregion
}
}
Related
Im trying to take a screenshot of my screen with a console application and then save it to my desktop but for some reason.. its telling me that my clipboard is empty when clearly its not.. If you check the code you can see that I press PrintScreen and when you do that it saves it to the clipboard.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ScreenshotConsole
{
class Program
{
static void Main(string[] args)
{
screenshot();
Console.WriteLine("Printescreened");
saveScreenshot();
Console.ReadLine();
}
static void screenshot()
{
SendKeys.SendWait("{PRTSC}");
}
static void saveScreenshot()
{
//string path;
//path = "%AppData%\\Sys32.png"; // collection of paths
//path = Environment.ExpandEnvironmentVariables(path);
if (Clipboard.ContainsImage() == true)
{
Image image = (Image)Clipboard.GetDataObject().GetData(DataFormats.Bitmap);
image.Save("image.jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
else
{
Console.WriteLine("Clipboard empty.");
}
}
}
}
It will take some time to screenshot, so you shoud add a delay after pressing {PRTSC}:
static void screenshot()
{
SendKeys.SendWait("{PRTSC}");
Thread.Sleep(500);
}
UPDATE
OK, I figured it out, add STAThreadAttribute to your main method:
[STAThread]
static void Main(string[] args)
{
screenshot();
Console.WriteLine("Printescreened");
saveScreenshot();
Console.ReadLine();
}
MSDN says that:
The Clipboard class can only be used in threads set to single thread apartment (STA) mode. To use this class, ensure that your Main method is marked with the STAThreadAttribute attribute.
More detail
I can't get rid of this problem. It's very strage, when I try to run my NUnit test in Debug mode, I get the expected result but when I just run it normally, the result is Wrong.
What i'm trying to do is detect Binding errors. Here is a sample
[TestFixture, RequiresSTA]
public class BindingTests
{
[Test]
public void T1_BindingErrorsExpected()
{
string error = null;
using (var listener = new ObservableTraceListener())
{
listener.TraceCatched += s => error = s;
TextBlock myText = new TextBlock();
UserControl control = new UserControl();
Binding myBinding = new Binding("BadBinding");
myBinding.Source = control;
myText.SetBinding(TextBlock.BackgroundProperty, myBinding);
}
Assert.IsNotNull(error);
}
}
And the ObservableTraceListener
public sealed class ObservableTraceListener : DefaultTraceListener
{
private readonly StringBuilder _Builder = new StringBuilder();
public ObservableTraceListener()
{
PresentationTraceListener.Add(SourceLevels.Error, this);
}
public new void Dispose()
{
Flush();
Close();
PresentationTraceListener.Remove(this);
base.Dispose();
}
public override void Write(string message)
{
_Builder.Append(message);
}
public override void WriteLine(string message)
{
Write(message);
if (TraceCatched != null)
TraceCatched(_Builder.ToString());
_Builder.Clear();
}
public event Action<string> TraceCatched;
}
public static class PresentationTraceListener
{
public static void Add(SourceLevels level, TraceListener trace)
{
PresentationTraceSources.DataBindingSource.Listeners.Add(trace);
PresentationTraceSources.DataBindingSource.Switch.Level = level;
PresentationTraceSources.ResourceDictionarySource.Listeners.Add(trace);
PresentationTraceSources.ResourceDictionarySource.Switch.Level = level;
}
public static void Remove(TraceListener trace)
{
PresentationTraceSources.DataBindingSource.Listeners.Remove(trace);
PresentationTraceSources.ResourceDictionarySource.Listeners.Remove(trace);
}
}
Result in debug -> Fail (What I expect)
Result in Run-> Success (Not what expected)
Thanks MatthewMartin but I found the solution looking at https://github.com/bblanchon/WpfBindingErrors
The probem was my ObservableTraceListener.
I needed to add a static constructor calling PresentationTraceSources.Refresh() to get it works correctly. As said in MSDN doc, it Refreshes trace sources, by forcing the app.config file to be re-read. So, some initialization was just done when I started the test in "debug" mode, which probably caused the app.config file to be read.
MSDN doc -> PresentationTraceSources.Refresh() http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.refresh%28v=vs.100%29.aspx
Here is my final ObservableTraceListener and the PresentationTraceListener is the same as the one in the question
public sealed class ObservableTraceListener : TraceListener
{
private readonly StringBuilder _Builder = new StringBuilder();
static ObservableTraceListener()
{
PresentationTraceSources.Refresh();
}
public ObservableTraceListener()
{
PresentationTraceListener.Add(SourceLevels.Error, this);
}
public new void Dispose()
{
Flush();
Close();
PresentationTraceListener.Remove(this);
base.Dispose();
}
public override void Write(string message)
{
_Builder.Append(message);
}
public override void WriteLine(string message)
{
_Builder.Append(message);
if (TraceCatched != null)
TraceCatched(_Builder.ToString());
_Builder.Clear();
}
public event Action<string> TraceCatched;
}
Posting an answer because a comment would be too small to hold what I want to say...
There is no reference to PresentationTraceListener on the internet-- how it treats the TraceListener is important because AFAIK the PresentationTraceListener is what calls the Write/WriteLine methods of the TraceListener (possibly indirectly via TraceSource)
This code below compiles in a brand new project. If I Write(x), I get null, if I writeLine(x), I get a value. Generally when you write a custom TraceListener, you redirect all the overloads to a single method so that they all behave the same (Write and WriteLine should be identical except WriteLine tacks a newline onto the message for the caller.)
Another observation is that System.Diagnostics trace is a quirky logging library whose main advantage is that it is always available even when there is some barrier to using 3rd party libraries. System.Diagnostics trace really wants you to register a TraceSwitch, TraceListener in your app.config or web.config and use that to enable & disable trace. It also requires the the TRACE flag be registered (in the same way the DEBUG flag gets registered, in the project properities-- by default TRACE is defined for DEBUG and RELEASE)
[TestFixture, RequiresSTA]
public class BindingTests
{
[Test]
public void T1_BindingErrorsExpected()
{
string error = null;
using (var listener = new ObservableTraceListener())
{
listener.TraceCatched += s => error = s;
//TextBlock myText = new TextBlock();
//UserControl control = new UserControl();
//Binding myBinding = new Binding("BadBinding");
//myBinding.Source = control;
//myText.SetBinding(TextBlock.BackgroundProperty, myBinding);
PresentationTraceSources.DataBindingSource.TraceEvent(TraceEventType.Error,0, "Hello World!");
}
Assert.IsNotNull(error);
Console.WriteLine(error);
}
}
public sealed class ObservableTraceListener : TraceListener
{
private readonly StringBuilder _Builder = new StringBuilder();
public ObservableTraceListener()
{
//PresentationTraceListener.Add(SourceLevels.Error, this);
}
protected override void Dispose(bool disposing)
{
Flush();
Close();
//PresentationTraceListener.Remove(this);
}
public override void Write(string message)
{
_Builder.Append(message);
}
public override void WriteLine(string message)
{
Write(message);
if (TraceCatched != null)
TraceCatched(_Builder.ToString());
_Builder.Clear();
}
public event Action<string> TraceCatched;
}
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.
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");
}
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.
You can also call SetOut with Console.OpenStandardOutput, this will restore the original output stream:
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
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);
}
}
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
}
I was wondering if anyone could explain why when I use the following code I get different results. For further explaination, I'm using a dll that was created in C#, it's an rcon framework. The richtextbox displays 3 lines then will not display anymore whereas my debug console continues to get data from my rcon connection.
I'm using:
Private Shared Sub HandleMessage(args As BattlEyeMessageEventArgs)
Debug.WriteLine(args.Message)
Form1.RichTextBox3.AppendText(args.Message & vbNewLine)
Form1.RichTextBox3.SelectionStart = Form1.RichTextBox3.TextLength
If args.Message = "Connected!" Then
Form1.Button3.Enabled = True
End If
End Sub
If it helps, here's the C# code for the EventHandler:
using System;
namespace BattleNET
{
public delegate void BattlEyeMessageEventHandler(BattlEyeMessageEventArgs args);
public class BattlEyeMessageEventArgs : EventArgs
{
public BattlEyeMessageEventArgs(string message)
{
Message = message;
}
public string Message { get; private set; }
}
}
private delegate void UpdateRichTextBox3Delegate(RichTextBox3 textBox, string text);
private void UpdateRichTextBox3(RichTextBox3 textBox, string text){
if(textBox.InvokeRequired){
textBox.Invoke(new UpdateRichTextBox3Delegate(UpdateRichTextBox3),new object[]{textBox, text});
return;
}
textBox.AppendText(String.format("{0}{1}", text,Environment.NewLine));
}
Check if RichTextBox3 doesn't require to be invoked first before updating it.
call UpdateRichTextBox3(Form1.RichTextBox3, "some text to append");
So pretty straightforward question. I have a c# .dll with a whole lot of Console.Writeline code and would like to be able to view that output in a forms application using this .dll. Is there a relatively easy way of binding the console output to a RichEdit (or other suitable control)? Alternatively, can I embed an actual console shell within the form? I have found a few somewhat similar questions but in most cases people wanted to be able to recieve console input, which for me is not necessary.
Thanks.
You can use Console.SetOut() to redirect the output. Here's a sample form that demonstrates the approach. Drop a RichTextBox and a button on the form.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
button1.Click += button1_Click;
Console.SetOut(new MyLogger(richTextBox1));
}
class MyLogger : System.IO.TextWriter {
private RichTextBox rtb;
public MyLogger(RichTextBox rtb) { this.rtb = rtb; }
public override Encoding Encoding { get { return null; } }
public override void Write(char value) {
if (value != '\r') rtb.AppendText(new string(value, 1));
}
}
private void button1_Click(object sender, EventArgs e) {
Console.WriteLine(DateTime.Now);
}
}
I assume it won't be very fast but looked okay when I tried it. You could optimize by overriding more methods.
IMO, it would be better to refactor the existing code, replacing the existing Console.WriteLine to some central method in your code, and then repoint this method, presumably by supplying another TextWriter:
private static TextWriter output = Console.Out;
public static TextWriter Output {
get {return output;}
set {output = value ?? Console.Out;}
}
public static void WriteLine(string value) {
output.WriteLine(value);
}
public static void WriteLine(string format, params string[] args) {
output.WriteLine(format, args);
}
Or (simpler and less hacky re a static field), simply pass a TextWriter into your existing code and write to that?