|
// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void OutputViewAddCallback(ListViewItem Elem);
private void OutputViewAdd(ListViewItem Elem)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.OutputView.InvokeRequired)
{
OutputViewAddCallback d = new OutputViewAddCallback(OutputViewAdd);
this.Invoke(d, new object[] { Elem });
}
else
{
if(Elem.Text.Length>0)
this.OutputView.Items.Add(Elem );
else
this.OutputView.EnsureVisible(this.OutputView.Items.Count - 1);
}
}
public ListViewItem CreateEventRow()
{
DateTime d=DateTime.Now;
// create a ListView item/subitems : [event nb] - [time] - [empty string]
string msg1 = (++EventCounter).ToString();
string msg2 = d.ToLongTimeString();
ListViewItem elem = new ListViewItem(msg1);
elem.SubItems.Add(msg2);
elem.SubItems.Add("");
//OutputViewAdd(elem );
//this.OutputView.Items.Add(elem);
// we save the message item for incoming text updates
CurrentMsgItem=elem.SubItems[2];
CurrentMsgItem.Tag = elem;
return elem;
}
public void UpdateCurrentRow(bool CreateRowNextTime)
{
ListViewItem elem;
if (CurrentMsgItem==null)
CreateEventRow();
elem = (ListViewItem )CurrentMsgItem.Tag;
CurrentMsgItem.Text=Buffer.ToString();
OutputViewAdd(elem );
// if null, a new row will be created next time this function is called
if (CreateRowNextTime==true) CurrentMsgItem=null;
// this is the autoscroll, move to the last element available in the ListView
if (this.CheckScroll.CheckState == CheckState.Checked)
{
OutputViewAdd(new ListViewItem(""));
//this.OutputView.EnsureVisible(this.OutputView.Items.Count-1);
}
}
Henry
|
|
|
|
|
Thank you for that short but really useful code .
I couldn't use it "as is" because i have a really "multithreaded" app which
could be enhanced through your window. The implementation is not threadsafe and
i got errors because some calls are not running from this form context!
I have reorganized some code snippets to
get it threadsafe and in the forms context also - This may helpful also for others:
Replacements in class DebugConsole:
---------------------------------------------------------------------
override public void Write(string message) {<br />
DebugForm.invokewrite(message);<br />
}<br />
<br />
override public void WriteLine(string message) {<br />
DebugForm.invokewriteline(message , this.UseCrWl);<br />
}
---------------------------------------------------------------------
Inserts in class DebugConsoleWrapper:
--------------------------------- inserts ---------------------------
<br />
private delegate void WriteLineDelegate(string message, bool busecr );<br />
private delegate void WriteDelegate(string message);<br />
private object lockObj = new object();<br />
<br />
public void FormObj_WriteInvoke(string message) {<br />
lock (lockObj) {<br />
this.Buffer.Append(message);<br />
this.UpdateCurrentRow(false);<br />
}<br />
}<br />
<br />
public void invokewrite(string message) {<br />
BeginInvoke(new WriteDelegate(FormObj_WriteInvoke), message);<br />
}<br />
<br />
public void FormObj_WriteLineInvoke(string message, bool busecr ) {<br />
lock (this.lockObj) {<br />
if (busecr == true) {<br />
this.CreateEventRow();<br />
this.Buffer = new StringBuilder();<br />
}<br />
<br />
this.Buffer.Append(message);<br />
this.UpdateCurrentRow(true);<br />
this.Buffer = new StringBuilder();<br />
}<br />
}<br />
<br />
public void invokewriteline(string message , bool busecr) {<br />
BeginInvoke(new WriteLineDelegate(FormObj_WriteLineInvoke), message, busecr ) ;<br />
}
-------------------------------------------------------------------
With kind Regards, Knoepfle
|
|
|
|
|
It does not seem to work for VS 2005 and don't know if this work for .net framework 2.0, so I going to attempt to write my version of this debug console. I'm going to use singleton approach, because I just pick it up from Head First Desig n Pattern.
I welcome comment and tips associated with this subject. Is there way of fixing thread safe issue? Once done, I hope to release it to community via CodeProject webpage.
I also considering adding text colour as well, so that it get attention if there is bad data or need alerting.
>It's not so much what you have to learn if you accept weird theories, it's what you have to unlearn. (Isaac Asimov)
>Life's journey is not to arrive at the grave safely in a well preserved body,but rather to skid in sideways, totally worn out, shouting "...holy sh*t...what a ride! [Riscy]
|
|
|
|
|
|
Hi Riscy
How could I get your code?
Thx
Wolfgang
|
|
|
|
|
Hi, I wait for a week and if I hear nothing from author, I try release it on CodeProject.
>It's not so much what you have to learn if you accept weird theories, it's what you have to unlearn. (Isaac Asimov)
>Life's journey is not to arrive at the grave safely in a well preserved body,but rather to skid in sideways, totally worn out, shouting "...holy sh*t...what a ride! [Riscy]
|
|
|
|
|
Hi Riscy, have you published it, yet, or did I overlook something?
Thanks Marcus
|
|
|
|
|
I noticed that you said you incorporated changes several times throughout the posts; however, it looks like you never updated the source files here. Is there any way you can upload the latests files?
Thanks
|
|
|
|
|
I run a Virtual PC to test my C#, and it naturally does not have the Visual Studio debugger. This has saved me hours.
To reiterate what another user mentioned, having a UI thread to ensure updates are done asynchronously would be ideal.
Thanks,
Brad
|
|
|
|
|
nice work
for multithreaded programs debug messages might be launched by off-ui-threads as well, but only the ui thread is allowed to change the interface objects directly according to ms tech docs
so you should check for InvokeRequired in your UpdateCurrentRow and reestabslish a ui thread safe call call via Invoke( new UpdateCurrentRowDelegate( UpdateCurrentRow ), new object [] { CreateRowNextTime ) )
greetz steve
|
|
|
|
|
Thanks, it saves me cluttering my console output. I made one small change, as I am running a few apps at the same time, I added a Caption functionality, to make it easier to know which DebugConsole belongs to which App. I overloaded the Init method with the following:
public void Init(bool UseDebugOutput, bool UseCrForWriteLine, string Caption){<br />
DebugForm.Text=Caption;<br />
Init (UseDebugOutput, UseCrForWriteLine);<br />
}<br />
Being in a minority of one, doesn't make you insane George Orwell However, in my case it does
|
|
|
|
|
Hi there,
Great work, I'm just playing with it at the moment.
The method you use to create a singleton seems like a pretty elegant one line solution - do you know if it's thread safe? I wrote an article for Singletons using C++, and am still no way near as proficient in C# - but using a static variable like this would cause thread safety issues in C++.
If I make any changes that take this into account (if it matters) I'll give you the source with changes.
Also I noticed that you plonked the DebugConsole class in the System.Diagnostics namespace. I think you should reconsider using your own namespace just incase MS decide to extend the language and put their own DebugConsole class in the System.Diagnostics namespace.
Cheers,
Paul
/**********************************
Paul Evans, Dorset, UK.
Personal Homepage "EnjoySoftware" @
http://www.enjoysoftware.co.uk/
**********************************/
|
|
|
|
|
I couldn't see a way of e-mailing you, so here's some changes I made, incase u are interested.
Thanks for this great starting point!
Paul
#define TRACE
#if (!(TRACELISTENER))
#define TRACELISTENER
#endif
namespace PDE.Debug
{
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Diagnostics;
sealed class DebugConsole : TraceListener
{
public static readonly DebugConsole Instance = new DebugConsole();
private DebugConsoleWrapper DebugForm = new DebugConsoleWrapper();
private bool UseCrWl = true;
private bool m_bInit = false;
private DebugConsole()
{
}
public void Init(bool UseDebugOutput, bool UseCrForWriteLine)
{
if (UseDebugOutput==true)
Debug.Listeners.Add(this);
else
Trace.Listeners.Add(this);
this.UseCrWl = UseCrForWriteLine;
DebugForm.Show();
this.m_bInit=true;
Trace.WriteLine("--- DEBUG CONSOLE LOG INITIALISED ---");
}
public void Init(bool UseDebugOutput)
{
Init(UseDebugOutput, true);
}
public void Init()
{
#if (DEBUG)
Init(true,true);
Trace.WriteLine("--- (Initialised using defaults (Debug mode, writing new lines) ---");
#else
Init(false,true);
Trace.WriteLine("--- (Initialised using defaults (Trace mode, writing new lines) ---");
#endif
}
override public void Write(string message)
{
if (!m_bInit)
{
Init();
}
DebugForm.Buffer.Append(message);
DebugForm.UpdateCurrentRow(false);
}
override public void WriteLine(string message)
{
if (!m_bInit)
{
Init();
}
if (this.UseCrWl==true)
{
DebugForm.CreateEventRow();
DebugForm.Buffer=new StringBuilder();
}
DebugForm.Buffer.Append(message);
DebugForm.UpdateCurrentRow(true);
DebugForm.Buffer.Length = 0;
}
private class DebugConsoleWrapper : System.Windows.Forms.Form
{
private System.Windows.Forms.Button BtnSave;
private System.Windows.Forms.Button BtnClear;
private System.Windows.Forms.SaveFileDialog SaveFileDlg;
private System.Windows.Forms.CheckBox CheckScroll;
private System.Diagnostics.DefaultTraceListener Tracer = new System.Diagnostics.DefaultTraceListener();
private System.Windows.Forms.ColumnHeader Col1;
private System.Windows.Forms.ColumnHeader Col2;
private System.Windows.Forms.ListView OutputView;
private System.Windows.Forms.CheckBox CheckTop;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.ColumnHeader Col3;
private ListViewItem.ListViewSubItem CurrentMsgItem = null;
private int EventCounter=0;
public StringBuilder Buffer = new StringBuilder();
private System.ComponentModel.Container components = null;
public DebugConsoleWrapper()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.BtnSave = new System.Windows.Forms.Button();
this.BtnClear = new System.Windows.Forms.Button();
this.SaveFileDlg = new System.Windows.Forms.SaveFileDialog();
this.CheckScroll = new System.Windows.Forms.CheckBox();
this.OutputView = new System.Windows.Forms.ListView();
this.Col1 = new System.Windows.Forms.ColumnHeader();
this.Col2 = new System.Windows.Forms.ColumnHeader();
this.Col3 = new System.Windows.Forms.ColumnHeader();
this.CheckTop = new System.Windows.Forms.CheckBox();
this.panel2 = new System.Windows.Forms.Panel();
this.panel2.SuspendLayout();
this.SuspendLayout();
this.BtnSave.Location = new System.Drawing.Point(8, 16);
this.BtnSave.Name = "BtnSave";
this.BtnSave.Size = new System.Drawing.Size(64, 24);
this.BtnSave.TabIndex = 8;
this.BtnSave.Text = "Save";
this.BtnSave.Click += new System.EventHandler(this.BtnSave_Click);
this.BtnClear.Location = new System.Drawing.Point(80, 16);
this.BtnClear.Name = "BtnClear";
this.BtnClear.Size = new System.Drawing.Size(64, 24);
this.BtnClear.TabIndex = 8;
this.BtnClear.Text = "Clear";
this.BtnClear.Click += new System.EventHandler(this.BtnClear_Click);
this.CheckScroll.Checked = true;
this.CheckScroll.CheckState = System.Windows.Forms.CheckState.Checked;
this.CheckScroll.Location = new System.Drawing.Point(152, 16);
this.CheckScroll.Name = "CheckScroll";
this.CheckScroll.Size = new System.Drawing.Size(80, 16);
this.CheckScroll.TabIndex = 8;
this.CheckScroll.Text = "autoscroll";
this.CheckScroll.CheckedChanged += new System.EventHandler(this.CheckScroll_CheckedChanged);
this.OutputView.AutoArrange = false;
this.OutputView.BackColor = System.Drawing.Color.MediumAquamarine;
this.OutputView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.Col1,
this.Col2,
this.Col3});
this.OutputView.Dock = System.Windows.Forms.DockStyle.Fill;
this.OutputView.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.OutputView.ForeColor = System.Drawing.Color.Black;
this.OutputView.Name = "OutputView";
this.OutputView.Size = new System.Drawing.Size(400, 200);
this.OutputView.TabIndex = 7;
this.OutputView.View = System.Windows.Forms.View.Details;
this.Col1.Text = "#";
this.Col1.Width = 40;
this.Col2.Text = "Time";
this.Col2.Width = 100;
this.Col3.Text = "Message";
this.Col3.Width = 400;
this.CheckTop.Location = new System.Drawing.Point(240, 16);
this.CheckTop.Name = "CheckTop";
this.CheckTop.Size = new System.Drawing.Size(96, 16);
this.CheckTop.TabIndex = 8;
this.CheckTop.Text = "always on top";
this.CheckTop.CheckedChanged += new System.EventHandler(this.CheckTop_CheckedChanged);
this.panel2.Controls.AddRange(new System.Windows.Forms.Control[] {
this.BtnSave,
this.BtnClear,
this.CheckScroll,
this.CheckTop});
this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel2.Location = new System.Drawing.Point(0, 200);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(400, 48);
this.panel2.TabIndex = 8;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(400, 248);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.OutputView,
this.panel2});
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MinimumSize = new System.Drawing.Size(450, 160);
this.Name = "DebugConsoleWrapper";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Debug Console";
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
public void CreateEventRow()
{
DateTime d=DateTime.Now;
string msg1 = (++EventCounter).ToString();
string msg2 = d.ToLongTimeString();
ListViewItem elem = new ListViewItem(msg1);
elem.SubItems.Add(msg2);
elem.SubItems.Add("");
this.OutputView.Items.Add(elem);
CurrentMsgItem=elem.SubItems[2];
}
public void UpdateCurrentRow(bool CreateRowNextTime)
{
if (CurrentMsgItem==null) CreateEventRow();
CurrentMsgItem.Text=Buffer.ToString();
if (CreateRowNextTime==true) CurrentMsgItem=null;
if (this.CheckScroll.CheckState == CheckState.Checked)
{
this.OutputView.EnsureVisible(this.OutputView.Items.Count-1);
}
}
private void BtnSave_Click(object sender, System.EventArgs e)
{
this.SaveFileDlg.Filter="Text file (*.txt)|*.txt|All files (*.*)|*.*" ;
this.SaveFileDlg.FileName="log.txt";
this.SaveFileDlg.ShowDialog();
FileInfo fileInfo = new FileInfo(SaveFileDlg.FileName);
StreamWriter s = fileInfo.CreateText();
for (int i=0;i<this.OutputView.Items.Count;i++)
{
StringBuilder sb=new StringBuilder();
sb.Append(this.OutputView.Items[i].SubItems[0].Text);
sb.Append("\t");
sb.Append(this.OutputView.Items[i].SubItems[1].Text);
sb.Append("\t");
sb.Append(this.OutputView.Items[i].SubItems[2].Text);
s.WriteLine(sb.ToString());
}
s.Close();
}
private void BtnClear_Click(object sender, System.EventArgs e)
{
this.EventCounter=0;
this.CurrentMsgItem=null;
this.OutputView.Items.Clear();
this.Buffer.Length = 0;
}
private void CheckTop_CheckedChanged(object sender, System.EventArgs e)
{
if (this.CheckTop.CheckState == CheckState.Checked)
this.TopMost = true;
else
this.TopMost = false;
}
private void CheckScroll_CheckedChanged(object sender, System.EventArgs e)
{
if (this.CheckScroll.CheckState == CheckState.Checked)
this.OutputView.EnsureVisible(this.OutputView.Items.Count-1);
}
}
}
}
|
|
|
|
|
Why use a separate interface to log events to your debug window? Just have the DebugConsole class inherit from System.Diagnostics.TraceListener, add it to the Listeners collection for the Debug and Trace objects, and use the standard Debug.WriteLine , etc.:
using System.Diagnostics;
using System.Text;
...
sealed class DebugConsole : TraceListener
{
private static readonly DebugConsole Instance;
static DebugConsole() {
Instance = new DebugConsole();
Trace.Listeners.Add(Instance);
Debug.Listeners.Add(Instance);
}
private DebugConsoleWrapper DebugForm = new DebugConsoleWrapper();
private StringBuilder Buffer = new StringBuilder();
private DebugConsole() {
DebugForm.Show();
}
public void Write(string message) {
Buffer.Append(message);
}
public void WriteLine(string message) {
if (Buffer.Length == 0)
DebugForm.WriteLine(message);
else {
Buffer.Append(message);
DebugForm.WriteLine(Buffer.ToString());
Buffer = new StringBuilder();
}
}
}
Form1 would then become:
using System;
...
using System.Diagnostics;
namespace DebugConsoleSample
{
public class Form1 : System.Windows.Forms.Form
{
...
[STAThread]
static void Main()
{
Debug.WriteLine("Function call : Application.Run(new Form1())");
Application.Run(new Form1());
}
private void textBox1_TextChanged(object sender, System.EventArgs e)
{
Debug.WriteLine("Event : textBox1_TextChanged ");
}
private void button1_Click(object sender, System.EventArgs e)
{
Debug.WriteLine("Event : button1_Click - textBox1 string : "
+ textBox1.Text.ToString());
Debug.WriteLine("Start a loop...");
for (int i=0;i<5;i++)
Debug.WriteLine("Loop counter : " + i.ToString());
}
private void trackBar1_Scroll(object sender, System.EventArgs e)
{
Debug.WriteLine("Event : trackBar1_Scroll : "
+ trackBar1.Value.ToString());
}
}
}
|
|
|
|
|
greets to you I used part of your code in the new version.
|
|
|
|
|
Nice!
|
|
|
|
|
|
That right there is the best utility ever invented for debuggin'. Praise sysinternals. They have saved my butt more times than i care to count.
Joseph Dempsey
jdempsey@cox.rr.com
Joseph.Dempsey@thermobio.com
"Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning."
--anonymous
|
|
|
|
|
I didn't know this good looking util Have to try it and maybe this will be the source of inspiration for future versions
|
|
|
|
|
Why use OutputDebugString when the System.Diagnostics has the Trace & Debug classes with tons of static methods (including WriteLineIf and Assert for optional output) which means it requires even less work (no interop);)
|
|
|
|
|
yep, that's what the new version of my console is using. I have to look at the other methods of these classes and override them.
|
|
|
|
|