HotDog's Blog

Hotdog (Robert Verpalen) about C# and vb.net

This blog hosted by:
http://blogs.vbcity.com      
  Home :: Syndication  :: Login

AprMay 2008Jun
SMTWTFS
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Articles

Archives

Topics

CONTACT

Fun but useful linkies

General

VS 2005

Wolfenstein ET

Another small class with nothing fancy or advanced, but that could spare time in not having to do it yourself ;-)

.net has a lot of nifty debugging features. For Runtime support a lot of  convenient code exists in the System.Diagnostics namespace.
But.. if you're anything like me, you use the Console.Write and WriteLine functions when developing. (when sometimes I really should use the Debug.Write... functions, but find it too convenient to use Console)

Or.. other times when written for a console app, but now you want the same code to run in a windows app and still see the output.

In any case, .net has made  it incredibly easy to redirect the standard output. No tutorial here on that subject, but still, had made a default output several times and decided that I could do with something generic.
Well, here it is. It's very simple code which does nothing more than redirect the Console output to a listbox. It runs asynchronously with the calling code, so it won't hold it up that much, but the code might be ahead of the output in fast output generating output files.

To start using, simply set:
Subro.ConsoleOutput.Redirect = true;

To stop, surprise surprise, set Redirect to false. By default the maximum number of lines kept (and displayed) is 250, but this can be changed by setting the ConsoleOutput.MaxLines property

Code CopyHideScrollFull
namespace Subro
{
using System.Threading;
using
System.IO;
using
System;
using
System.Windows.Forms;
using
System.Drawing;
using
System.Text;
using
System.ComponentModel;
public
class ConsoleOutput : Form
{
static volatile ConsoleOutput instance;
///
<summary>
///
Starts or stops the redirection of the console output to a ConsoleOutput form window
///

public
static bool Redirect
{
get
{
return instance != null;
}
set

{
if (Redirect == value) return;
WaitLock();
//instanceLock.AcquireReaderLock(timeout);

if
(Redirect != value)
{
locked = true;
if
(value)
{
Thread t = new Thread(new ThreadStart(show));
t.Start();
}
else

{
instance.Invoke(new ThreadStart(instance.Close));
}
}
WaitLock();
}
}
const
int timeout = 5000;
volatile
static bool locked;
//static ReaderWriterLock instanceLock = new ReaderWriterLock();

///
<summary>
///
Thread start to start showing the output form in its own thread
///

static
void show()
{
new ConsoleOutput().ShowDialog();
}
static
void WaitLock()
{
while (locked) { }
}
static
volatile int max = 250;
public
static int MaxLines
{
get { return max; }
set

{
max = value;
if
(instance != null)
instance.Invoke(new ThreadStart(instance.CheckMax));
}
}

static
bool ShowTime = true;
public
static bool ShowEntryTimes
{
get { return ShowTime; }
set

{
ShowTime = value;
if
(instance != null)
instance.Invalidate();
}
}
/// <summary>
///
The original output of the console window
///

TextWriter
orgoutput;
protected
override void OnClosing(CancelEventArgs e)
{
if (orgoutput != null)
Console.SetOut(orgoutput);
if (stream != null)
stream.Dispose();
instance = null;
locked = false;
base
.OnClosing(e);
}
protected
override void OnShown(EventArgs e)
{
base.OnShown(e);
instance = this;
stream = new CStream(this);
orgoutput = Console.Out;
Console
.SetOut(stream);
locked = false;
}
protected
override Size DefaultSize
{
get
{
return new Size(500, 300);
}
}
protected
override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape) Close();
base
.OnKeyDown(e);
}
CStream
stream;
ConsoleBox
lb = new ConsoleBox();
ConsoleOutput()
{
lb.Dock = DockStyle.Fill;
Controls.Add(lb);
Text = "Console output";
KeyPreview = true;
}
class ConsoleBox : ListBox
{
public ConsoleBox()
{
BackColor = Color.Black;
ForeColor = Color.White;
brush = new SolidBrush(ForeColor);
DrawMode = DrawMode.OwnerDrawVariable;
}
SolidBrush
brush;
static
SolidBrush timeBrush = new SolidBrush(Color.Yellow);
static
Font timefont = new Font(FontFamily.GenericSansSerif, 7);
static
int timeHeight;
protected override void OnDrawItem(DrawItemEventArgs e)
{

if
(e.Index == -1) return;
Graphics
g = e.Graphics;
e.DrawBackground();
ConsoleEntry
ce = Items[e.Index] as ConsoleEntry;
Rectangle
rect = e.Bounds;
if
(ShowTime)
{
g.DrawString(ce.GetCreationTime(), timefont, timeBrush, rect);
rect.Offset(0, timeHeight);
}
g.DrawString(ce.Value, e.Font, brush,rect);
}
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
ConsoleEntry ce = Items[e.Index] as ConsoleEntry;
SizeF
size= e.Graphics.MeasureString(ce.Value, Font);
e.ItemHeight = (int)size.Height;
if
(ShowTime)
{
if (timeHeight == 0)
timeHeight = (int)
e.Graphics.MeasureString(ce.GetCreationTime(), timefont).Height;
e.ItemHeight += timeHeight;
}
}
}

void
CheckMax()
{
stream.CheckMax();
}
class
ConsoleEntry : INotifyPropertyChanged
{
public readonly DateTime CreationTime = DateTime.Now;
public
string Value
{
get { return value; }
set

{
this.value = value;
if
(ValueChanged != null)
ValueChanged(this, EventArgs.Empty);
if (PropertyChanged != null)
PropertyChanged(this, pe);
}
}
public
event EventHandler ValueChanged;
public
event PropertyChangedEventHandler PropertyChanged;
static
PropertyChangedEventArgs pe = new PropertyChangedEventArgs("Value");
string
value;
public
ConsoleEntry(string Value)
{
this.value = Value;
}
public
override string ToString()
{
return value;
}
public string GetCreationTime()
{
return CreationTime.ToString("hh:mm:ss.ffff");
}
}
class
CStream : TextWriter
{
ConsoleOutput owner;
ListBox
lb;
public
CStream(ConsoleOutput owner)
{
this.owner = owner;
lb = owner.lb;
lb.DataSource = list;
lb.DisplayMember = "Value";
}
public
override void Write(string value)
{
Write(value, false);
base
.Write(value);
}
public
override void WriteLine(string value)
{
Write(value, true);
base
.WriteLine(value);
}
ConsoleEntry
last;
BindingList
<ConsoleEntry> list = new BindingList<ConsoleEntry>();
delegate
void addDelegate(string value, bool end);
void
Write(string value, bool end)
{
if (locked) return;
try

{
lb.BeginInvoke(new addDelegate(write), new object[] { value, end });
}
catch
(ObjectDisposedException)
{
}
catch
(InvalidOperationException)
{
lb.CreateControl();
Write(value, end);
}
}
int
index;
void
write(string value, bool end)
{
lock (list)
{
if (last == null)
{
last = new ConsoleEntry(value);
list.Add(last);
index = list.Count - 1;
}
else

{
last.Value += value;
}
lb.SelectedIndex = index;
CheckMax();
if
(end)
last = null;
}
}
public
void CheckMax()
{
if (list.Count <= max) return;
lock
(list)
while (list.Count > max)
{
list.RemoveAt(0);
index--;
}
//list.RemoveRange(0, list.Count - max);
}
public
override Encoding Encoding
{
get { return Encoding.Unicode; }
}
}
}
}
. . .

 

26-6-6
Understandably, comments have arisen and might still arise about the very ugly thread locking procedure. Well, let's just keep it at that it got it reasons and that it is hardly used when running. Please see the comments below for further explanation :
http://blogs.vbcity.com/hotdog/archive/2006/07/10/6089.aspx#6120

But... please feel free to experiment for other small impact solutions, but when testing elaborately I hope you agree that the normal locking mechanisms don't do the job in this case. To be honest, I didn't really look any further because the lock is needed only when starting (or stopping) the redirect, which takes a few milliseconds and will not be noticed. So experimenting on a large scale could ultimately be a waste of your time :p ;-)

posted on Monday, July 10, 2006 6:31 AM