The content of the postings is owned by the respective author. vbfeeds.com is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on vbfeeds.com, please contact us at abuse@vbfeeds.com and we will remove it. Designated trademarks and brands are the property of their respective owners. vbfeeds.com Copyright © 2005 Serge Baranovsky. All rights reserved.
|
|
by
Robert Verpalen via
VB & .NET Blogs @ vbCity.com
on
2/22/2008 9:09:00 AM
using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; using System.ComponentModel; using System.IO; using Subro.Exceptions; namespace Subro.Interaction { /// <summary> /// A single feedback item. A feedback item can provide information /// on status, progress and datetimes of execution /// </summary> public class Feedback{ public Feedback( string Message): this() { this.message = Message; } public Feedback() {this.lastupdate = this.DateTime; } /// <summary> /// The time the feedback object was created /// </summary> public readonly DateTime DateTime = DateTime.Now; DateTime lastupdate; /// <summary> /// The last time the Feedback was updated (for example by updating the progress) /// </summary> public DateTime LastUpdate {get { return lastupdate; } }private void SetLastUpdate() { lastupdate = DateTime.Now; } /// <summary> /// The main information of this feedback /// </summary> string message; public string Message { get { return message; } set{ message = value; NotifyUpdate(); } }int progress; /// <summary> /// The progress of this item. The total progress is in relation /// to the the value set at <see cref="ProgressTarget"/> /// <seealso cref="ProgressTarget"/> /// <see cref="ProgressPercentage"/> /// </summary>             public int Progress { get { return progress; } set{ progress = value; IsProgressUpdate = true; NotifyUpdate(); } }public event EventHandler Updated; public void NotifyUpdate() { SetLastUpdate(); if (Updated != null) Updated(this, EventArgs.Empty); } int progresstarget = 100; bool progressinpercentage = true; /// <summary> /// Normal target of process is 100 as in 100 percent. Whatever value this /// is set to, progress relates to it. /// For example: if looping through a number of lines, /// you can set this target to the number of lines and <see cref="Progress"/> to /// the line being processed /// </summary> public int ProgressTarget { get { return progresstarget; } set{ progresstarget = value; progressinpercentage = false; NotifyUpdate(); } }/// <summary> /// The relative progress of this item. /// </summary> public float ProgressPercentage { get { if (!IsProgressUpdate) return 1; return (float)progress / progresstarget; } }public string GetProgressString() { if (progressinpercentage) return ProgressPercentage.ToString("###%"); return progress.ToString() + "/" + progresstarget.ToString(); }/// <summary> /// Increase the <see cref="Progress"/> by one /// </summary> public void Increase() { Progress++; } public void ShowAlive() { IsAliveIndicator = true; } public bool IsProgressUpdate = false; public int alivecount; public int AliveCount { get { return alivecount; } } public bool IsAliveIndicator { get { return alivecount > 0; } set{ if ( value) { if (++alivecount == 0) alivecount++; NotifyUpdate(); } elsealivecount = 0; } } public override string ToString() {return string.Format("{0}\t{1}", DateTime, message); }/// <summary> /// The level, this can be a custom level or set with <see cref="FeedbackLevel"/>. /// 0 = normal  negative value is not so important, positive value is more important /// </summary> public int Level = ( int) FeedBackLevel.Normal; public FeedBackLevel FeedbackLevel { get { try { return (FeedBackLevel)Level; } catch {return FeedBackLevel.Custom; } } set {Level = (int)value; } }public bool IsNormalLevel { get { return Level == (int)FeedBackLevel.Normal; } }
public virtual bool CanShowExtraInfo { get { return HasChildren; } } public void ShowExtraInfo() {ShowExtraInfo(null); } public virtual void ShowExtraInfo(IWin32Window Owner) {children.Show(Owner); } protected virtual void AppendExtraInfo(AppendInfoSettings info) {children.AppendFeedbackText(info); }public virtual FeedbackItemPainter GetPainter( FeedbackItemInfo info) { return new FeedbackItemPainter(info); }internal void AppendInfo( AppendInfoSettings info, int RowNumber) { var sb = info.StringBuilder; sb.Append( "<DIV>"); bool bold = Level > 0, smaller = Level < 0; if (bold) sb.Append( "<B>"); else if (smaller) sb.Append( "<span style='font-size:smaller'>"); sb.Append( "<span style='font-size:smaller;font-style:italic;'>").Append(DateTime) .Append("</span>"); if (IsProgressUpdate) { int p = ( int)(ProgressPercentage * 100); sb.Append( @"<span style=""border:'1 solid black';width:75px;margin-left:10;font-size:smaller"">") .Append("<span style='position:absolute;width:") .Append(p) .Append("%;background-color:green'></span>") .Append("<span style='position:relative;width:100%'>") .Append(GetProgressString()).Append("</span>") .Append("</span>"); } sb.Append("<span style='margin-left:10'>").Append(message).Append("</span>"); if (CanShowExtraInfo) { string ExtraInfoAnchor = "ExtraInfo" + RowNumber.ToString(); if (info.AllowScripts) { sb.Append(@"<A style='margin-left:5' href='javascript:' onclick="" this.index=1-this.index; varindex = this.index, modes=new Array('none',''), texts=new Array('Show details','Hide details'); this.nextSibling.style.display=modes[index]; this.innerText=texts[index];"" index=1>Hide details</A>");} sb.Append( "<div style='margin-left:20;background-color:lightblue;'>") .Append("<a name='").Append(ExtraInfoAnchor).Append("'></a>"); AppendExtraInfo(info); sb.Append( "</div>"); } if (bold) sb.Append("</B>"); else if (smaller) sb.Append("</span>"); sb.Append("</DIV>"); } FeedbackCollection children; /// <summary> /// Use this colleciton to add children. NB calling this function the first time will /// create the collection /// </summary> public FeedbackCollection Children { get { if (children == null) { children = new FeedbackCollection(); children.Parent = this; } return children; } }public bool HasChildren { get { return children != null && children.Count > 0; } }
#region static #region mail public delegate void SendMailDelegate( string Subject, string Body, bool IsHTML); static SendMailDelegate sendmail = delegate( string Subject, string Body, bool IsHTML) { System.Net.Mail.MailMessage sm = new System.Net.Mail.MailMessage(); sm.IsBodyHtml = IsHTML; sm.Body = Body; sm.Subject = Subject; System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient(); smtp.Send(sm); }; public static SendMailDelegate DefaultSendMailMethod { get { return sendmail; } set{ if ( value == null) throw new ArgumentNullException(); sendmail = value; } }#endregion #region Exceptions public delegate void ShowExceptionDelegate( Exception ex); static ShowExceptionDelegate showex = delegate( Exception ex) { string m = ex.Message + "\r\n----------------\r\nStack:\r\n"+ ex.StackTrace; MessageBox.Show(m); }; public static ShowExceptionDelegate DefaultShowExceptionMethod { get { return showex; } set{ if (value == null) throw new ArgumentNullException(); showex = value; } } #endregionpublic static implicit operator Feedback( string value) { return new Feedback(value); }#endregion } /// <summary> /// General settings when the text of feedback is obtained. /// <seealso cref="FeedbackCollection.GetTotalFeedbackText"/> /// <see cref="Feedback.AppendInfo"/> /// </summary> public class AppendInfoSettings{ public AppendInfoSettings(): this( new StringBuilder()) { } public AppendInfoSettings( StringBuilder sb) { this.StringBuilder = sb; } public readonly StringBuilder StringBuilder; public bool AllowScripts; public int MaxLines;public AppendInfoSettings Append( string Value) { StringBuilder.Append(Value); return this; } public AppendInfoSettings Append<T>(T Value) { StringBuilder.Append(Value); return this; } } /// <summary> /// A collection of <see cref="Feedback"/> items. Besides being a collection /// for different Feedback items, this class contains the methods to /// visualize the feedback /// </summary> public class FeedbackCollection : BindingList< Feedback> { public FeedbackCollection() { } public FeedbackCollection( int MaximumValues) { maxcount = MaximumValues; } /// <summary> /// This value can be set to indicate that this set is a childset of /// another Feedback item /// </summary> public Feedback Parent; #region Add/Update protected override void InsertItem( int index, Feedback fb) { base.InsertItem(index, fb); CheckMaxValues();             }protected override void OnListChanged( ListChangedEventArgs e) { base.OnListChanged(e); if (Parent != null) Parent.NotifyUpdate(); }
public Feedback Add( string Text) { Feedback fb = new Feedback(Text); Add(fb); return fb; } public Feedback Add(string Text, FeedBackLevel Level) {Feedback fb = new Feedback(Text); fb.FeedbackLevel = Level; Add(fb); return fb; } public void Add(Feedback fb, int Progress) {fb.Progress = Progress; Add(fb); }public ExceptionFeedback Add( Exception ex) { ExceptionFeedback ef = new ExceptionFeedback(ex); Add(ef); return ef; }#endregion #region Zero result /// <summary> /// By default this value is <c>false</c>, but the executing (batch) code /// can set this value to indicate that all in all nothing has happened. /// (eg, code to import file did not find a file) /// To include a reason, use <see cref="SetZeroResult"/> /// </summary> public bool ZeroResult { get { return lastzeroresult != null; } set{ if (ZeroResult != value) { if ( value) SetZeroResult("No reason specified"); else LastZeroResult = null; } } }ZeroResultFeedback lastzeroresult; internal ZeroResultFeedback LastZeroResult { get { return lastzeroresult; } set {if (lastzeroresult == value) return; /* if (Parent != null) {if (value != null) Parent.LastZeroResult = value; else if (Parent.lastzeroresult == lastzeroresult) Parent.LastZeroResult = null; }*/lastzeroresult = value; } }public class ZeroResultFeedback : Feedback{ public readonly string Reason; public ZeroResultFeedback( string reason) : base("Batch has zero result: [" + reason + "]") { this.FeedbackLevel = FeedBackLevel.Important; this.Reason = reason; } }
/// <summary> /// The reason that was indicated that the code where this feedback /// was used with did not actually do anything. /// This value can be set when using <see cref="SetZeroResult"/> /// </summary> public string ZeroResultReason { get { if (ZeroResult) return lastzeroresult.Reason; return null; } } public void SetZeroResult(string reason) {ZeroResultFeedback fb = new ZeroResultFeedback(reason); Add(fb); LastZeroResult = fb; } #endregion
public Feedback Last { get { return this[Count - 1]; } }public DateTime StartTime { get { if (Count == 0) return DateTime.MinValue; return this[0].DateTime; } }public DateTime EndTime { get { if (Count == 0) return DateTime.MinValue; return Last.LastUpdate; } }public TimeSpan Duration { get { return EndTime.Subtract(StartTime); } }private int maxcount; /// <summary> /// The maximum amount of entries allowed /// </summary> public int MaximumValues { get { return maxcount; } set{ maxcount = value; CheckMaxValues(); } }void CheckMaxValues() { if (maxcount > 0) { while (Count > maxcount) RemoveAt(0); } }#region Save/Mail /// <summary> /// Description is used when saving or mailing the list, so /// this should contain information about the contents /// </summary> public string Description = "Feedback list" public string Save() { string file = Application.CommonAppDataPath + @"\Feedback\" if (!Directory.Exists(file)) Directory.CreateDirectory(file); string notallowed = " /\\", descr = Description; if (descr != null) for ( int i = 0; i < notallowed.Length; i++) { descr = descr.Replace(notallowed[i], '_'); } elsedescr = "feedback" file += descr + ".htm" Save(file); return file; } public void Save( string file) { lock ( this) { StreamWriter sw = new StreamWriter(file, true, System.Text.Encoding.ASCII); sw.Write(GetTotalFeedBackText()); sw.Close(); Add("List saved to " + file); } }
public void Mail() { Mail( (ZeroResult ? "Zero result " : null) + "Batch Feedback '" + Description + "'"); }
public void Mail( string Subject) { Feedback.DefaultSendMailMethod(Subject,GetTotalFeedBackText(false),true); }public void MailExceptions() { Feedback.DefaultSendMailMethod( "Errors in " + Description, AppendFeedbackText( new AppendInfoSettings(),EnumerateAll<ExceptionFeedback>(true)).ToString(), true ); }public string GetTotalFeedBackText() { return GetTotalFeedBackText(true); } public string GetTotalFeedBackText(bool AllowScripts) {return AppendFeedbackText(new AppendInfoSettings { AllowScripts=AllowScripts}).ToString(); }        
public bool HasExceptions() { foreach ( Feedback fb in this) { if (fb.FeedbackLevel== FeedBackLevel.Exception) return true; if (fb.HasChildren && fb.Children.HasExceptions()) return true; } return false; }public IEnumerable< Feedback> EnumerateAll<FeedbackType>() where FeedbackType : Feedback {return EnumerateAll<FeedbackType>(true); } public IEnumerable<Feedback> EnumerateAll<FeedbackType>(bool IncludeChildren)where FeedbackType : Feedback {foreach ( Feedback fb in this) { if (fb is FeedbackType) yield return fb; if (IncludeChildren && fb.HasChildren) { foreach (FeedbackType f in fb.Children.EnumerateAll<FeedbackType>( true)) { yield return f; } } } }public StringBuilder AppendFeedbackText( StringBuilder sb, bool AllowScripts) { return AppendFeedbackText(new AppendInfoSettings(sb) { AllowScripts = AllowScripts }); } public StringBuilder AppendFeedbackText(AppendInfoSettings info) {return AppendFeedbackText(info, this); } public StringBuilder AppendFeedbackText(AppendInfoSettings info, IEnumerable<Feedback> list) {var sb = info.StringBuilder; sb.Append( "<DIV style=\"border='1px black solid'\">"); if (Parent == null) sb.Append("\tFeedback ") .Append(Description) .Append(". Info created on ").Append(DateTime.Now); if (ZeroResult) sb.Append( "<div style=\"border:'1 solid black';background-color:cyan\"><b>Finished with zero result: </b>") .Append(ZeroResultReason) .Append("</div>"); sb.Append( "<HR>"); int i = 0; foreach ( Feedback fb in list) { fb.AppendInfo(info, i++); if (info.MaxLines > 0 && i == info.MaxLines) break; } sb.Append( "</DIV>"); return sb; } public void ShowHTML() { if (System.Threading. Thread.CurrentThread.GetApartmentState() == System.Threading. ApartmentState.STA) { Form f = new Form(); WebBrowser wb = new WebBrowser(); wb.Dock = DockStyle.Fill; f.Controls.Add(wb); wb.DocumentText = GetTotalFeedBackText(); f.WindowState = FormWindowState.Maximized; f.Show(); } else//Unfortunately, using the webbrowser requires an STA //if this is not the case of the current thread, save and show with explorer System.Diagnostics.Process.Start(Save()); } #endregion
/// <summary> /// Creates a <see cref="FeedbackForm"/> with a <see cref="FeedbackControl"/> and shows it or /// shows an existing form if it was already created /// </summary> public FeedbackForm Show() { return Show((IWin32Window)null); } public FeedbackForm Show(IWin32Window Owner) {if (Owner is Control) return Show(Owner as Control); if (frm == null) { CreateFeedbackForm(); frm.Show(Owner); } elsefrm.Activate(); return frm; } delegate FeedbackForm show(Control owner); public FeedbackForm Show( Control Owner) { if (Owner == null) return Show(); if (Owner.InvokeRequired) return ( FeedbackForm) Owner.Invoke(new show(Show), Owner); if (frm == null) { CreateFeedbackForm(Owner); Form f = Owner.FindForm(); if (f != null) { if (f.IsMdiContainer) frm.MdiParent = f; else if (f.IsMdiChild) frm.MdiParent = f.MdiParent; }if (frm.MdiParent == null) frm.Show(Owner); else frm.Show(); } elsefrm.Activate(); return frm; } FeedbackForm frm;
public void ShowDialog() { ShowDialog(null); } public void ShowDialog( IWin32Window Owner) { CreateFeedbackForm().ShowDialog(Owner); } /// <summary> /// creates a feedbackform based on this collection /// </summary> /// <returns></returns> public FeedbackForm CreateFeedbackForm() { createForm(); return frm; } void createForm() { if (frm == null) frm = new FeedbackForm(this); } /// <summary> /// This overload of <see cref="CreateFeedbackForm()"/> makes sure /// the <see cref="FeedbackForm"/> is created on the same thread /// as the <c>ThreadControl</c> /// </summary> public FeedbackForm CreateFeedbackForm(Control ThreadControl) {if (ThreadControl == null || !ThreadControl.InvokeRequired) return CreateFeedbackForm(); CloseFeedbackForm(); ThreadControl.Invoke(new System.Threading.ThreadStart(createForm)); return frm; } /// <summary> /// If a <see cref="FeedbackForm"/> is shown, this method will close it /// </summary> public void CloseFeedbackForm() { if (frm != null) { try { if (frm.InvokeRequired) frm.Invoke(new System.Threading.ThreadStart(frm.Dispose)); else frm.Dispose(); } catch { } frm = null; } } /*internal void FormClosed() { frm = null; } * */} /// <summary> /// Basically just a form with a <see cref="FeedbackControl"/> /// </summary> public class FeedbackForm : Form{ FeedbackControl fc = new FeedbackControl();         public FeedbackForm() { Text = DefaultText;       KeyPreview = true;             fc.CollectionChanged += new EventHandler(fc_CollectionChanged); fc.Dock = DockStyle.Fill; Controls.Add(fc); } public FeedbackForm( FeedbackCollection collection) : this() { this.Collection = collection; }public Feedback Add( Feedback fb) { return fc.Add(fb); }public FeedbackCollection Collection { get { return fc.Collection; } set { fc.Collection = value; } }
void fc_CollectionChanged( object sender, EventArgs e) { SetText(); } const string DefaultText = "Feedback information" void SetText() { string text = DefaultText; if(Collection!= null && Collection.Description!= null) text = "[" + Collection.Description + "] " + text; Text = text; }public static Size DefaultFormSize = new Size(300, 300); protected override Size DefaultSize { get { return DefaultFormSize; } }protected override void OnKeyDown( KeyEventArgs e) { if (e.KeyCode == Keys.Escape) Close(); base.OnKeyDown(e); }protected override void OnClosed( EventArgs e) { Collection.CloseFeedbackForm(); } } /// <summary> /// A control containing a <see cref="FeedbackListBox"/> and /// some extra linklabels for extra gui options /// </summary> public class FeedbackControl : Control{ FeedbackListBox lb = new FeedbackListBox(); BottomPanel pnlBottom; public FeedbackControl() { lb.Dock = DockStyle.Fill; lb.CollectionChanged += new EventHandler(lb_DataSourceChanged); Controls.Add(lb); pnlBottom = new BottomPanel(); Controls.Add(pnlBottom); } void lb_DataSourceChanged( object sender, EventArgs e) { pnlBottom.Visible = Collection != null; } /// <summary> /// By Asigning a collection, the listbox will update /// itself when messages are received. /// NB: each collection can have only one FeedbackControl asigned to it. /// </summary>[ DefaultValue( null)] public FeedbackCollection Collection { get { return lb.Collection; } set{ lb.Collection = value; } }public event EventHandler CollectionChanged { add { lb.CollectionChanged += value; } remove { lb.CollectionChanged -= value; } }class BottomPanel : Panel{ LinkLabel llMail = new LinkLabel(), llSave = new LinkLabel(), llShow = new LinkLabel(); public BottomPanel() { Dock = DockStyle.Bottom; Padding = new Padding(1); Height = 20; AddLabel(llMail); AddLabel(llSave); AddLabel(llShow); llMail.Click += new EventHandler(llMail_Click); llMail.Text = "Mail list" llSave.Click += new EventHandler(llSave_Click); llSave.Text = "Save list" llShow.Click += new EventHandler(llShow_Click); llShow.Text = "Show overview" } void llShow_Click( object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(llShow_Click), sender, e); else coll.ShowHTML(); }new FeedbackControl Parent { get { return base.Parent as FeedbackControl; } } FeedbackCollection coll {get { return Parent.Collection; } } void llSave_Click(object sender, EventArgs e) {if (InvokeRequired) Invoke(new EventHandler(llSave_Click), sender, e); else try { SaveFileDialog sf = new SaveFileDialog(); sf.Filter = "HTML file|*.htm;*.html" if (sf.ShowDialog( this) == DialogResult.OK) { coll.Save(sf.FileName); } } catch ( Exception ex) { MessageBox.Show("Error saving list:\r\n" + ex.Message); }} void llMail_Click( object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(llMail_Click), sender, e); else try { coll.Mail(); } catch ( Exception ex) { MessageBox.Show("Error mailing list:\r\n" + ex.Message); } }void AddLabel( LinkLabel ll) { ll.Dock = DockStyle.Left; Controls.Add(ll); } } public Feedback Add( Feedback fb) { return lb.Add(fb); }public int Count { get { return lb.Count; } }public Feedback this[ int Index] { get { return lb[Index]; } }public class FeedbackAlivePanel : Panel{ PulseRectangle pr = new PulseRectangle(); public FeedbackAlivePanel() { SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); } protected override void OnResize( EventArgs eventargs) { pr.Bounds = Bounds; Increase(); } public void Increase() { Invalidate(); }
protected override void OnPaint( PaintEventArgs e) { pr.Paint(e.Graphics); } } } /// <summary> /// A listbox specifically for feedback items. The items are graphically shown to clearly /// outline their type of feedback /// </summary> public class FeedbackListBox : ListBox{ public FeedbackListBox() { DrawMode = DrawMode.OwnerDrawFixed; SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); items = new ItemCollection( this); } public Feedback this[ int index] { get { if (coll == null) throw new Exception("No collection has been started yet"); return coll[index]; } }[ Browsable( false)] [ DesignerSerializationVisibility( DesignerSerializationVisibility .Hidden)] public new ItemCollection Items { get { return items; } }ItemCollection items; public class ItemCollection{ public readonly FeedbackListBox Owner; public ItemCollection( FeedbackListBox Owner) { this.Owner = Owner; }public Feedback this[ int Index] { get { return Owner[Index]; } }public int Count { get { return Owner.Count; } } public void Add( Feedback fb) { Owner.Add(fb); } public int IndexOf( Feedback fb) { return Owner.IndexOf(fb); }} public Feedback Add( Feedback fb) { if (coll == null) Collection = new FeedbackCollection(); coll.Add(fb); return fb; }
void Collection_ListChanged( object sender, ListChangedEventArgs e) { switch (e.ListChangedType) { case ListChangedType.ItemAdded: case ListChangedType.ItemChanged: case ListChangedType.ItemDeleted: //this inbetween class is used because in threading environments //the collection can be updated before the invoke is finishedupdate( new updateinfo{ Index=e.NewIndex, Item=((FeedbackCollection)sender)[e.NewIndex], Type= e.ListChangedType }); break; }} class updateinfo{             public int Index; public Feedback Item; public ListChangedType Type; }Queue< updateinfo> updates = new Queue< updateinfo>(); volatile bool updating; void update( updateinfo info) { lock (updates) updates.Enqueue(info); try { if (!updating && !IsDisposed) BeginInvoke(( Void) delegate() { update(); }); } catch (Exception ex) {Console.WriteLine(ex.Message); }} void update() { if (updating) return; updating = true; updateinfo info ; while(updates.Count>0) { lock(updates) info = updates.Dequeue(); switch (info.Type) { case ListChangedType.ItemAdded: Init(info.Item, info.Index); break; case ListChangedType.ItemChanged: Update(info.Item, info.Index); break; case ListChangedType.ItemDeleted: Remove(info); break; } } Application.DoEvents(); updating = false; }
void Init( Feedback fb, int index) { fb.Updated += new EventHandler(Feedback_Updated);                         base.Items.Insert(index, fb); SetIndex(index); } void Remove( updateinfo inf) { inf.Item.Updated -= new EventHandler(Feedback_Updated); base.Items.RemoveAt(inf.Index); } void Feedback_Updated( object sender, EventArgs e) { var u = new updateinfo(); u.Item= (Feedback)sender; u.Index=IndexOf(u.Item); u.Type =u.Index==-1 ? ListChangedType.ItemDeleted : ListChangedType.ItemChanged; update(u); }delegate void Void(); void UpdateSummary() { } void Update(Feedback fb, int index) { if (fb.IsAliveIndicator) UpdatePulse(fb); //System.Diagnostics.Debug.Assert(index >= 0); if (index >= 0) { Rectangle r = GetRectangle(index); Invalidate(r); SetIndex(index); Application.DoEvents(); } elsefb.Updated -= new EventHandler(Feedback_Updated); }
Rectangle GetRectangle( int index) { Rectangle r = ClientRectangle; r.Height = this.ItemHeight; r.Y = r.Height*index; return r; } void SetIndex(int index) {SelectedIndex = index; }public int IndexOf( Feedback fb) { if (coll == null) return -1; return coll.IndexOf(fb); } public int Count {get { if (coll == null) return 0; return coll.Count;                 } }protected override void OnDrawItem( DrawItemEventArgs e) { object item = base.Items[e.Index]; Feedback fb = (Feedback)item ; FeedbackItemInfo inf = new FeedbackItemInfo(fb,e,coll.StartTime,this); fb.GetPainter(inf).Paint();
}
public event EventHandler CollectionChanged; FeedbackCollection coll; [ DefaultValue( null)] public FeedbackCollection Collection { get { return coll; } set {if (coll == value) return; BeginUpdate(); base.Items.Clear(); if(coll!= null) coll.ListChanged += new ListChangedEventHandler(Collection_ListChanged); //base.DataSource =  DataSource not used because of threading problems coll = value;                 if (coll != null) {                     coll.ListChanged += new ListChangedEventHandler(Collection_ListChanged); for ( int i = 0; i < coll.Count; i++) { Init(coll[i], i); }                     } EndUpdate(); if (CollectionChanged != null) CollectionChanged(this, EventArgs.Empty); } } [DesignerSerializationVisibility(DesignerSerializationVisibility .Hidden)] public new FeedbackCollection DataSource {get { return coll; } }Dictionary< Feedback, PulseRectangle> pulses; void UpdatePulse( Feedback fb) { GetPulse(fb).Update(); } public PulseRectangle GetPulse( Feedback fb) { if (pulses == null) pulses = new Dictionary<Feedback, PulseRectangle>(); PulseRectangle pr; if (!pulses.TryGetValue(fb, out pr)) { pr = new PulseRectangle(); pr.Count = 20; pulses.Add(fb, pr); } return pr; }protected override void OnClick( EventArgs e) { if (mouseonbutton != null) { mouseonbutton.ShowExtraInfo(this); } base.OnClick(e); }Feedback mouseonbutton; protected override void OnMouseMove( MouseEventArgs e) { Feedback onbutton = null; if (Count > 0 && FeedbackItemPainter.ExtraInfoButtonArea.Contains(e.X,e.Y)) { int index = IndexFromPoint(e.Location); if (index != -1 && index < Count) onbutton = this[index]; }if (mouseonbutton != onbutton) { if (onbutton != null && onbutton.CanShowExtraInfo) mouseonbutton = onbutton; else mouseonbutton = null; Cursor = mouseonbutton != null ? Cursors.Hand : null; }
} } public class FeedbackItemInfo{         public readonly DrawItemEventArgs DrawItemEventArgs; public readonly bool IsSelected; public readonly Feedback Item; public readonly int Index; public readonly Rectangle Bounds; public readonly DateTime CollectionStartTime; public readonly FeedbackListBox Owner; public FeedbackItemInfo( Feedback Item, DrawItemEventArgs e, DateTime CollectionStartTime, FeedbackListBox Owner) { this.Item = Item; this.DrawItemEventArgs = e;             this.IsSelected = (e.State & DrawItemState.Selected) > 0; this.Index = e.Index;             this.Owner = Owner; this.Bounds = e.Bounds; this.CollectionStartTime = CollectionStartTime; }} public class FeedbackItemPainter{ Rectangle r; public FontFamily FontFamily; public readonly Rectangle MessageArea; public readonly FeedbackItemInfo Info; public readonly Graphics Graphics; public FeedbackItemPainter( FeedbackItemInfo info) { this.Info = info; this.r = info.Bounds; this.FontFamily = info.DrawItemEventArgs.Font.FontFamily; MessageArea = new Rectangle(MessageOffset, r.Y, r.Width - MessageOffset, r.Height); this.Graphics = info.DrawItemEventArgs.Graphics; }public const intExtraInfoOffset = 75, ExtraInfoWidth = 10, ProgressWidth = 40, MessageOffset = 90, TimeOffset = 32; public static readonly Rectangle ExtraInfoButtonArea = new Rectangle(ExtraInfoOffset, 0, ExtraInfoWidth, int.MaxValue);
public void DrawDateTime() { DrawDateTime(Info.Index > 0,Brushes.Black); } public virtual void DrawDateTime( bool Relative, Brush b) { //clear background ClearDateTimeBackGround( Brushes.White); //draw text if (Relative) DrawRelativeTime(Info.Item.LastUpdate.Subtract(Info.CollectionStartTime),b); else DrawAbsoluteTime(b); }void DrawAbsoluteTime( Brush b) { Graphics.DrawString( Info.Item.DateTime.ToShortDateString() + " " + Info.Item.DateTime.ToString("hh:mm.ss"), GetFont(6.5f), b, r.Left, r.Top + 1); } void DrawRelativeTime( TimeSpan diff, Brush b) { Graphics.DrawString( string.Format( "+{0:00}:{1:00}.{2:00}.{3:000}", diff.Hours, diff.Minutes, diff.Seconds, diff.Milliseconds), GetFont(7.2f) , b, r.Left, r.Top); } public Font GetFont( float size) { return new Font(FontFamily, size); }public virtual void ClearDateTimeBackGround( Brush b) { Graphics.FillRectangle(b, DateTimeArea); } public Rectangle DateTimeArea { get { return new Rectangle(r.Left, r.Top, r.Left + ExtraInfoOffset, r.Height); } }public virtual Color GetBackgroundColor() { if(Info.IsSelected) return Color.Blue; return Color.White; }public void DrawSelectionRectangle() { DrawSelectionRectangle(GetBackgroundColor()); } public virtual void DrawSelectionRectangle( Color c) { Graphics.DrawRectangle(new Pen(c), r); ClearMessageBackGround(c); } public void ClearMessageBackGround( Color c) { Graphics.FillRectangle(new SolidBrush(c), MessageArea); } public void DrawExtraInfoButton() { Rectangle rectEI = ExtraInfoButtonArea; rectEI.Y = r.Y; rectEI.Height = r.Height; DrawExtraInfoButton(rectEI); }public virtual void DrawExtraInfoButton( Rectangle r) { ControlPaint.DrawButton(Graphics, r, ButtonState.Normal); r.X += 2; Graphics.DrawString("i", GetFont(7), Brushes.Blue, r); }public void DrawMessage() { DrawMessage(offset); } public void DrawMessage( int Offset) { Color c = Info.IsSelected ? Color.WhiteSmoke : Color.Black;             Rectangle r = MessageArea; r.X += Offset; r.Width -= Offset; DrawMessage(r, new SolidBrush(c)); } public virtual void DrawMessage(Rectangle r,Brush b) {            Font f = GetFont(7); if (Info.Item.Level > 0) f = new Font(f, FontStyle.Bold); Graphics.DrawString(Info.Item.Message, f, b, r); } int offset;
public virtual void PaintProgress() { RectangleF rectProg = GetProgressRectangle(); rectProg.X++; rectProg.Inflate(0, -1); Graphics.FillRectangle( Brushes.White, rectProg); Pen p = Pens.Black; Graphics.DrawRectangle(p, Rectangle.Round(rectProg)); float penwidth = .5f; rectProg.Inflate(-penwidth, -penwidth); rectProg.Width *= Info.Item.ProgressPercentage; Graphics.FillRectangle( Brushes.Green, rectProg); Font f = GetFont(7); Graphics.DrawString(Info.Item.GetProgressString(), f, Brushes.Navy, rectProg.Left + 1, rectProg.Top - 1); }public virtual void PaintAlive() { Rectangle r = GetProgressRectangle(); r.Inflate(0, -1); PulseRectangle pr = Info.Owner.GetPulse(Info.Item); pr.Bounds = r; pr.Paint(Graphics); }protected Rectangle GetProgressRectangle() { Rectangle r = MessageArea; r.X += offset; offset += ProgressWidth + 2; r.Width = ProgressWidth; return r; }protected void IncreaseOffset( int by) { offset += by; } public virtual void Paint() { DrawDateTime(); if (Info.Item.CanShowExtraInfo) { DrawExtraInfoButton(); } DrawSelectionRectangle(); //draw progress if (Info.Item.Progress > 0) { PaintProgress(); } //draw alive if (Info.Item.IsAliveIndicator) { PaintAlive(); } //draw message DrawMessage(); } } public class PulseRectangle{ private Rectangle bounds; public Rectangle Bounds { get { return bounds; } set{ if (bounds == value) return; Rectangle r = bounds; bounds = value; if (r.Width != value.Width || ps == null) Update(); else FillDrawPoints(); } }void transform( int x, int y) { if (x == 0 && y == 0) return; for ( int i = 0; i < ps.Length; i++) { ps[i].X -= x; ps[i].Y -= y; } }/// <summary> /// The number of points to draw. /// Call Update manually after setting this value /// </summary> public int Count = 200; private Point[] ps, drawpoints; public void Update() { Random r = new Random(); double step = ( double)bounds.Width / Count; ps = new Point[Count]; drawpoints = null; for ( int i = 0; i < Count; i++) ps[i] = new Point((int)(step * i), r.Next(100)); FillDrawPoints(); } /// <summary> /// The pen with which the pulses are drawn /// </summary> public Pen Pen = Pens.LightGreen;public Brush BackGroundBrush = Brushes.Black; void FillDrawPoints() { int left = bounds.Left, h = bounds.Height, top = bounds.Top; drawpoints = new Point[ps.Length]; for ( int i = 0; i < ps.Length; i++) { drawpoints[i].X = ps[i].X + left; drawpoints[i].Y = top + (int)(h * ps[i].Y / 100); } } public void Paint(Graphics g) {if (ps == null) Update(); g.FillRectangle(BackGroundBrush, bounds); g.DrawLines(Pen, drawpoints); } } /// <summary> /// The importance of the feedback /// </summary> public enum FeedBackLevel{ Custom = 100, Trivial = -100, Normal = 0, SuperNormal = 200, Exception = 500, Important = 1000 } public interface IFeedBackSupporter{ FeedbackCollection Feedback { get; } }#region Specific feedback public class FileReferenceFeedback : Feedback{ public readonly string File; public FileReferenceFeedback( string MessagePrefix, string File) : base( MessagePrefix + System.IO.Path.GetFileName(File) + " in " + System.IO.Path.GetDirectoryName(File)) { this.File = File; } public FileReferenceFeedback(string File): this("File: ", File) {} public override bool CanShowExtraInfo { }public override void ShowExtraInfo( IWin32Window Owner) { System.Diagnostics.Process.Start("Explorer", "/select," + File); } } public class ExceptionFeedback : Feedback{ public readonly Exception Exception; public ExceptionFeedback( Exception ex) { Exception = ex; FeedbackLevel = FeedBackLevel.Exception; System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(ex); Message = "Error '" + ex.Message + "' occured on " + stack.GetFrame(0); } public override bool CanShowExtraInfo { }           public override void ShowExtraInfo( IWin32Window Owner) { DefaultShowExceptionMethod(Exception);             } class ErrorPainter : FeedbackItemPainter{ public ErrorPainter( FeedbackItemInfo inf) : base(inf) { } public override void DrawMessage( Rectangle r, Brush b) { Rectangle ri = r; ri.Width = ri.Height; Graphics.DrawIcon(SystemIcons.Error, ri); r.X = ri.Right; base.DrawMessage(r,Info.IsSelected ? Brushes.Orange : Brushes.Red); }} public override FeedbackItemPainter GetPainter( FeedbackItemInfo info) { return new ErrorPainter(info); }protected override void AppendExtraInfo( AppendInfoSettings info) { new ExceptionInfo(Exception).AppendMessage(info.StringBuilder); }} #endregion }
. . .
Also needed: (this is used to show complete exception information in HTML, you could also strip the exception adding functionality, but I use this class a lot to quickly debug and get better stacktraces than the default .net block of code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Diagnostics; using System.Data.SqlClient; using System.IO; namespace Subro.Exceptions {
public class ExceptionInfo : IExtraInfo{
/// <summary> /// The original Exception where this info object is based on /// public readonly Exception Exception; public ExceptionInfo( Exception ex) {
this.Exception = ex;
//get stack trace information StackTrace st = new StackTrace(Exception, true); frames = new ErrorFrame[st.FrameCount]; for ( int i = 0; i < frames.Length; i++) {
frames[i] = new ErrorFrame(st.GetFrame(i)); if (usercodeindex == -1 && frames[i].IsUserCode)
usercodeindex = i; }
//determine total exception count while (ex != null) {
exceptioncount++; ex = ex.InnerException; }
}
#region General properties /// <summary> /// The date/time of the exception. This is not the time the exception /// was thrown, but rather when the ExceptionInfo object was created. /// For most debugging purposes that difference in time does not matter, but /// when the exact time is required, do not rely on this value! /// public readonly DateTime DateTime = DateTime.Now;
ErrorFrame[] frames; /// <summary> /// Gets the different StackFrames that led to this /// public ErrorFrame[] Frames {
get { return frames; } }
public int FrameCount {
get { return frames.Length; } }
int usercodeindex = -1; /// <summary> /// Returns the index of the first frame that occured in user code (and thus /// the first frame that is actually debuggable ;-) ) /// public int UserFrameIndex {
get { return usercodeindex; } }
/// <summary> /// Information about the last step before this error occured. The returned /// object contains information about that step, such as the method that was running. /// When running in debug, the pdb files also provide info on the original code filename and line number /// public ErrorFrame LastFrame {
get {
if (frames.Length > 0) return frames[0]; return null; } }
/// <summary> /// Where <see cref="LastFrame"/> returns the last step in general, this property /// returns the last step in non-system code /// <seealso cref="UserFrameIndex"/> /// public ErrorFrame LastUserFrame {
get {
if (usercodeindex == -1) return null; return frames[usercodeindex]; } }
/// <summary> /// The exception type /// public string Type {
get { return Exception.GetType().Name; } } #endregion
#region Mail NB: framework specific /// This mail section was created in .net 2.0, you might /// want to remove this part or alter it if using earlier versions
/// <summary> /// Sends an email to the specified address /// /// <param name="To"> public void Send( string To) {
System.Net.Mail.MailMessage m = GetExceptionMail(); m.To.Add(To); System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("mail"); client.Send(m); }
/// <summary> /// Send the exception info (in html format) to the addressee. /// NB, some presumtions were made when trying to create a quick and /// dirty send method. Safest way it to use <see cref="FillMailMessage"/> or /// <see cref="GetExceptionMail"/> and do the sending manually /// /// <param name="ex"> /// <param name="To"> public static void Send( Exception ex, string To) {
GetInfo(ex).Send(To); }
/// <summary> /// Gets a mailmessage instance containing the Exception info. /// The from address is tried to be set to a custom address. If this /// fails (or if you want to set a custom one), you'll have to set the From /// address manually. /// /// <returns> public static System.Net.Mail. MailMessage GetExceptionMail( Exception ex) {
return GetInfo(ex).GetExceptionMail(); } System.Net.Mail.MailMessage GetExceptionMail() {
System.Net.Mail. MailMessage m = new System.Net.Mail. MailMessage(); FillMailMessage(m); try{
m.From = new System.Net.Mail.MailAddress("Exceptions@" + Environment.UserDomainName); } catch { } return m; }
/// <summary> /// Sets the body of the message to hold the exeption info /// /// <param name="m"> public void FillMailMessage(System.Net.Mail. MailMessage m) {
m.IsBodyHtml = true; m.Body = GetTotalMessage(); } #endregion
#region ExtraInfo NB
/// <summary> /// This class can hold extra info for an <see cref="ExceptionInfo"/> /// Its main purpose is to output this extra info to the html text /// public abstract class ExtraInfo : IExtraInfo{
/// <summary> /// With this method, the extra info writes itself (in html format) to /// the stringbuilder /// /// <param name="sb"> public abstract void AppendHTML(StringBuilder sb, ExceptionInfo ei);
public static implicit operator ExtraInfo( string text) {
return new TextInfo(text); }
}
public class ValuesInfo : ExtraInfo{
List< ValueEntry> values = new List< ValueEntry>(); class ValueEntry{
public string Name, Text; } public string Header;
public void AddText( string text) {
AddValue(null, text); } public void AddValue( string Name, string Value) {
ValueEntry v = new ValueEntry(); v.Name = Name; v.Text = Value; values.Add(v); }
public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
ei.openBlock(1, 1); if (Header != null)
sb.Append("<b><i>").Append(Header).Append(":"); if (values.Count > 0) {
ei.openBlock(1, 1); foreach ( ValueEntry ve in values) {
if (ve.Name == null)
sb.Append(ve.Text); else
ei.appendInfo(ve.Name, ve.Text); } ei.closeBlock();} ei.closeBlock(); }
}
/// <summary> /// Adds plain text as extrainfo /// public class TextInfo : ExtraInfo{
public string Text; public TextInfo( string text) { Text = text; } public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
sb.Append(Text); } }
#region SQL extra info
/// <summary> /// Used to display the history of sql strings. /// public class SQlStringInfo : ExtraInfo {
List< string> list = new List< string>(); public void Add( string sql) {
list.Add(sql); }
public void AddRange( IEnumerable< string> values) {
list.AddRange(values); }
public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
ei.openBlock(1); sb.Append( "<B>Recent sql strings:"); ei.openBlock( "border:'gray 1 solid';margin-left:15"); int i = 0; foreach ( var sql in list) {
sb.Append( "<SPAN><B>--")
.Append(i++ + 1) .Append("--<span style='margin-left:10'>") .Append(sql) .Append("<BR>"); } ei.closeBlock(); ei.closeBlock(); }
}
/// <summary> /// Add one or more sql strings to the list to be displayed in the recent /// sql list. /// This is usefull when you keep a list of recent executed sql strings somewhere /// and want to include them in the exception output /// /// <param name="SQL"> public void AddSQL( IEnumerable< string> SQL) {
if (sqls == null) {
sqls = new SQlStringInfo(); AddExtraInfo(sqls); } sqls.AddRange(SQL); }
SQlStringInfo sqls; #endregion
/// <summary> /// Add <see cref="ExtraInfo"/>. You can also use a string as parameter /// /// <param name="ei"> public void AddExtraInfo( IExtraInfo ei) {
if (extrainfo == null) extrainfo = new List<IExtraInfo>(); extrainfo.Add(ei); }
List<IExtraInfo> extrainfo;
/// <summary> /// returns the amount of <see cref="ExtraInfo"/> objects added /// public int ExtraInfoCount {
get {
if (extrainfo == null) return 0; return extrainfo.Count; } }
#region HTML const string ExtraInfoAnchor = "ExtraInfo"; protected virtual void appendExtraInfo() {
if (extrainfo != null) {
appendBR(); appendHR(); openBlock(2); sb.Append( "<B><A name='").Append(ExtraInfoAnchor)
.Append("'>Extra Info<BR><HR>"); openBlock( "margin-left:10;font-size:smaller"); foreach ( IExtraInfo ei in extrainfo) {
ei.AppendHTML(sb, this); } closeBlock(); closeBlock(); } } #endregion
#endregion
#region Static /// <summary> /// Rather than creating a new instance manually, use /// this method to choose the proper exception object /// /// <param name="ex"> /// <returns> public static ExceptionInfo GetInfo( Exception ex) {
if (ex == null) return null; if (ex is SqlException) return new SqlExceptionInfo(ex as SqlException); if (ex is ReflectionTypeLoadException) return new ReflectionTypeLoadExceptionInfo(ex as ReflectionTypeLoadException); return new ExceptionInfo(ex); } public static implicit operator ExceptionInfo(Exception ex) {
return GetInfo(ex); } #endregion
#region Inner Exceptions int exceptioncount; /// <summary> /// returns the total number of exceptions (Main Exception + all inner exceptions) /// public int ExceptionCount {
get { return exceptioncount; } } #endregion
#region html information /// BEWARE: this code is manufactured to quickly create the html information /// and is not nicely constructed for reusability. /// A bit more friendly code for html is used in the AutoFormatter (http://blogs.vbcity.com/hotdog/archive/2005/12/30/5759.aspx) , /// but did not use that here to keep the code portable for fresh applications
/// <summary> /// Gets information about this exception and all inner exceptions in HTML format /// /// <returns> public string GetTotalMessage() {
return AppendMessage(new StringBuilder()).ToString(); }
/// <summary> /// This stringbuilder is only used in the html functions /// protected StringBuilder sb; protected void openBlock() {
openBlock(null); } protected void openBlock( int Border) {
openBlock(Border, 0); } protected void openBlock( int Border, int Indent) {
openBlock( "Border='" + Border + "px black solid'"
+ (Indent > 0 ? "margin-left=" + (Indent * 20) : null) ); } protected void openBlock( string Style) {
openclose(false, Style); } protected void closeBlock() {
openclose(true, null); } void openclose( bool close, string Style) {
sb.Append( "<"); if (close) sb.Append( "/"); sb.Append( "DIV"); if (Style != null)
sb.Append(" Style=\"").Append(Style).Append("\""); sb.Append( ">"); } public StringBuilder AppendMessage( StringBuilder builder) {
sb = builder; openBlock(2); appendTop(); appendInfo("Exception info created on", DateTime.ToString()); appendInfo("Application", AppDomain.CurrentDomain.FriendlyName); openBlock("font-size:smaller;margin-left:20"); appendHeaderBottom(); closeBlock(); Exception ex = Exception; int depth = 0; while (append(ex, depth++)) { ex = ex.InnerException; } appendExtraInfo(); appendBottom(); closeBlock(); sb = null; return builder; }
void appendMessage() { }
void IExtraInfo.AppendHTML( StringBuilder sb, ExceptionInfo ei) {
this.sb = sb; openBlock(2, 1); appendTitle( "Exception Info"); try{
AppendMessage(sb); } catch ( Exception ex) {
appendTitle("Error obtaining exception info: " + ex.Message); } closeBlock(); }
/// <summary> /// Gives inheriting classes the possibility to append html text at the top /// of the info block, inside the main border /// protected virtual void appendTop() { } /// <summary> /// Gives inheriting classes the possibility to append html inside the header /// of the info block. /// The base functionality adds links to the main and inner exceptions (if there are any) /// protected virtual void appendHeaderBottom() {
if (exceptioncount > 1) {
for ( int i = 0; i < exceptioncount; i++) {
openHeaderLink(AnchorNamePrefix + i); AppendExceptionTitle(i); sb.Append(""); } } if (extrainfo != null) {
openHeaderLink(ExtraInfoAnchor); sb.Append("Extra Info"); } }
/// <summary> /// Gives inheriting classes the possibility to append html inside the header /// of the Exception info block. /// protected virtual void appendExceptionHeader() { }
/// <summary> /// Used to add links in the header /// /// <param name="href"> /// <param name="name"> protected virtual void openHeaderLink( string href) {
sb.Append( "<A style='margin-left:15' href='#").Append(href)
.Append("'>"); }
/// <summary> /// Gives inheriting classes the possibility to append html text at the /// end of the exception info, but before the main block is closed /// protected virtual void appendBottom() { }
/// <summary> /// The prefix of the name that is added per depth so that code can /// point directly to one of the inner exceptions /// The Name for the first exception is ExDepth0 , the second ExDepth1 and so forth. /// Pointing to it in a href is subseqeuntly done with href = "#ExDepthX" where X is the inner exception index /// public const string AnchorNamePrefix = "ExDepth";
void AppendExceptionTitle( int depth) {
if (depth == 0)
sb.Append("Main Exception"); else
sb.Append("Inner Exception [").Append(depth).Append("]"); }
bool append( Exception ex, int depth) {
if (ex == null) return false; appendHR(); //add anchor informationsb.Append( "<A NAME=\"").Append(AnchorNamePrefix).Append(depth)
.Append("\" style='font-size:smaller'>"); AppendExceptionTitle(depth); sb.Append( ": <span style='background-color:#990000;color:white;font-weight:bolder;width:100%'>")
.Append(ex.Message) .Append(""); openBlock(1, 1); if (depth > 0) {
ExceptionInfo ei = GetInfo(ex); ei.sb = sb; ei.appendHeader(); try{
ei.appendExtraInfo(); } catch ( Exception ex2) {
appendTitle("Error writing extra info: " + ex2.Message); } } else
appendHeader(); closeBlock(); return true; }
const string
usercodecolor = "lightblue", systemcodecolor = "beige"; void appendHeader() {
openBlock( "border:'1 green solid';"); appendInfo( "Type", Type); if (usercodeindex >= 0) {
ErrorFrame ef = LastUserFrame; appendInfo("Last user code", ef.FullMethodSignature + " (Line " + ef.Line + " in '" + ef.FileName + "')"); } appendExceptionHeader(); if (frames.Length > 0) {
appendTitle( "Stack"); sb.Append( "<span style='margin-left:20;font-size:smaller'>Legend: "); for ( int i = 0; i < 2; i++) {
sb.Append( "<span style=\"background-color:")
.Append(i == 0 ? systemcodecolor : usercodecolor) .Append("border='1 black solid';margin-left:15\">") .Append(i == 0 ? "No debug information" : "With debug information") .Append(""); } sb.Append( ""); openBlock( "margin-left:40;font-size:smaller"); foreach ( ErrorFrame ef in frames) {
appendStack(ef); } closeBlock(); } else
sb.Append("--No StackTrace available--"); closeBlock();} protected void appendTitle( string Name) {
sb.Append("<B>").Append(Name).Append(": "); } protected void appendInfo( string Name, string Value) {
appendTitle(Name); sb.Append( "<SPAN style='position:relative;left:20'>")
.Append(Value).Append("<BR>"); } protected void appendHR() {
sb.Append("<HR>"); } protected void appendBR() {
sb.Append("<BR>"); }
void appendStack( ErrorFrame ef) {
openBlock( "border:'1 solid black';background-color:"
+ (ef.IsUserCode ? usercodecolor : systemcodecolor) ); sb.Append( "<span style='font-size:larger;font-style:italic;color:olive'><U>"); appendInfo( "Method", ef.MethodName); sb.Append( ""); appendInfo( "NameSpace", ef.NameSpace); appendInfo( "Full Signature", ef.MethodSignature);
if (ef.IsUserCode) {
appendTitle( "File"); sb.Append( "<A href='")
.Append(ef.FileName).Append("'>").Append(ef.FileName).Append("") .Append("<BR>"); appendInfo( "Line", ef.Line.ToString()); appendTitle( "Code snippet"); openBlock( "border='1 black dotted';margin-left=25"); ef.AppendCode(sb, CodeSnippetExtraLines); closeBlock(); } closeBlock();
}
/// <summary> /// Only applies when getting html text. This is the number of lines on /// each side of the offending line, that should be included in code snippets /// public int CodeSnippetExtraLines = 3; #endregion
public override string ToString() {
return Exception.ToString(); }
#region IO
/// <summary> /// Shows the exception in html format by outputting to a default file first /// and then opening it with the default browser. /// (a form could have been used to do this, but tried to keep this info /// class usable for Console applications as well) /// public void ShowException() {
ShowExceptionFile("default"); }
/// <summary> /// Shows the exception in a browser, see <see cref="ShowException()"/> for more info /// /// <param name="ex"> public static void ShowException( Exception ex) {
GetInfo(ex).ShowException(); }
/// <summary> /// First outputs the file (see <see cref="CreateExceptionFile"/>), then opens /// it with the default connected viewer. /// NB: the file is NOT appended. Existing info will be overwritten /// NB2: if no writing is necessary /// /// <param name="file"> public void ShowExceptionFile( string file) {
ShowExceptionFile(file, false); } /// <summary> /// Same as <see cref="ShowExceptionFile(string)"/>, but with the option /// to choose whether to append or not /// /// <param name="file"> /// <param name="append"> public void ShowExceptionFile( string file, bool append) {
CreateExceptionFile(file, append); ShowFile(file); }
/// <summary> /// All this does is run the file (using System.Diagnostics.Process.Start) /// If an exception file is to be shown, make sure it is created first, or /// use <see cref="ShowExceptionFile"/> to create and show the file instead /// of this method /// /// <param name="file"> public void ShowFile( string file) {
CheckFile(ref file); Process.Start(file); }
/// <summary> /// Appends complete directory information to a name (see code for details ;-) ) /// /// <param name="file"> public void CheckFile( ref string file) {
FileInfo fi = new FileInfo(file); if (fi.FullName.Length > file.Length) {
//no directory provided -> use application directory string dir =
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Exceptions" + "\\" + AppDomain.CurrentDomain.FriendlyName + "\\"; if (! Directory.Exists(dir))
Directory.CreateDirectory(dir); file = dir + file; } if (fi.Extension.Length == 0) file += ".htm"; }
/// <summary> /// Outputs the exception to the specified file. Make sure the extension can /// be read by explorer. If no extension is provided, ".htm" is used /// /// <param name="file"> /// <param name="append"> public void CreateExceptionFile( string file, bool append) {
CheckFile( ref file); using ( StreamWriter sw = new
StreamWriter(file, append, System.Text.Encoding.ASCII)) sw.Write(GetTotalMessage()); } #endregion
}
public class SqlExceptionInfo : ExceptionInfo{
public new readonly SqlException Exception; public SqlExceptionInfo( SqlException ex)
: base(ex) {
Exception = ex; }
#region HTML
const string AnchorSQL = "sqlinfo";
protected override void appendExceptionHeader() {
base.appendTop(); appendTitle( "SQL Errors (" + Exception.Errors.Count + ")"); openBlock( "border:'1 gray solid';font-size:0.6em;margin-left:40;margin-right:20"); for ( int i = 0; i < Exception.Errors.Count; i++) {
appendSQLError(i); } closeBlock(); } protected override void appendHeaderBottom() {
base.appendHeaderBottom(); openHeaderLink(AnchorSQL); sb.Append("SQL info").Append(""); }
void appendSQLError( int index) {
if (index > 0) appendHR(); SqlError se = Exception.Errors[index]; appendInfo("SQL class (severity)", se.Class.ToString()); appendInfo("Server", se.Server); appendInfo("T-sql line number:", se.LineNumber.ToString()); appendInfo("Message", se.Message); appendInfo("Procedure", se.Procedure); appendInfo("Source", se.Source); appendInfo("Sql error number", se.Number.ToString()); }
#endregion
}
public class ReflectionTypeLoadExceptionInfo : ExceptionInfo {
public readonly ReflectionTypeLoadException ReflectionTypeLoadException; public ReflectionTypeLoadExceptionInfo( ReflectionTypeLoadException Exception)
: base(Exception) {
this.ReflectionTypeLoadException = Exception; foreach ( Exception e in ReflectionTypeLoadException.LoaderExceptions) {
try {
AddExtraInfo(GetInfo(e)); } catch ( Exception ex) {
AddExtraInfo(new TextInfo("Error obtaining exception info: " + ex.Message)); AddExtraInfo(new TextInfo(e.Message)); } } }
}
/// <summary> /// Wrapper around a <see cref="StackFrame"/>. Not that much added functionality, but /// some properties instead of methods to be able to use easy databinding /// public class ErrorFrame{
public readonly StackFrame Base; public ErrorFrame( StackFrame frame) {
Base = frame; }
public int Line {
get { return Base.GetFileLineNumber(); } } public string FileName {
get { return Base.GetFileName(); } }
public string MethodName {
get { return Base.GetMethod().Name; } }
public string MethodSignature {
get { return Base.GetMethod().ToString(); } }
public string FullMethodSignature {
get { return NameSpace + " - " + MethodSignature; } }
public string NameSpace {
get { return Base.GetMethod().ReflectedType.FullName; } }
public bool IsUserCode {
get { return Line > 0; } }
public MethodBase GetMethod() {
return Base.GetMethod(); }
public string GetCode() {
return GetCode(2); } /// <summary> /// /// /// <param name="Lines">indicates the amount of lines before and after the Errorline to show public string GetCode(int Lines) {
if (!IsUserCode)
return "No code available for a system method"; string file = FileName; if (file == null || ! File.Exists(file))
return "File not found!";
try {
return AppendCode(new StringBuilder(), Lines).ToString(); } catch (Exception ex) {
return "Error obtaining code information: " + ex.Message; }
}
internal StringBuilder AppendCode( StringBuilder sb, int Lines) {
int line = Line, from = line - Lines, to = line + Lines, curline = 0; ; int len = sb.Length; const string space = " "; string tab = null; for ( int tabcount = 0; tabcount < 4; tabcount++) {
tab += space; } StreamReader sr = null; try{
sr = new StreamReader(FileName); string l; while ((l = sr.ReadLine()) != null) {
if (++curline >= from) {
if (curline > to) break; if (line == curline) sb.Append("<B>"); sb.Append(l.Replace("\t", tab).Replace(" ", space)); if (line == curline) sb.Append(""); sb.Append("<BR>"); } }} catch ( Exception ex) {
sb.Length = len; sb.Append( "Error obtaining code information: ")
.Append(ex.Message); } finally{
if (sr != null) sr.Close(); } return sb; }
}
public interface IExtraInfo{
void AppendHTML(StringBuilder sb, ExceptionInfo ei); } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Diagnostics; using System.Data.SqlClient; using System.IO;
namespace Subro.Exceptions {
public class ExceptionInfo : IExtraInfo{
/// <summary> /// The original Exception where this info object is based on /// public readonly Exception Exception; public ExceptionInfo( Exception ex) {
this.Exception = ex;
//get stack trace information StackTrace st = new StackTrace(Exception, true); frames = new ErrorFrame[st.FrameCount]; for ( int i = 0; i < frames.Length; i++) {
frames[i] = new ErrorFrame(st.GetFrame(i)); if (usercodeindex == -1 && frames[i].IsUserCode)
usercodeindex = i; }
//determine total exception count while (ex != null) {
exceptioncount++; ex = ex.InnerException; }
}
#region General properties /// <summary> /// The date/time of the exception. This is not the time the exception /// was thrown, but rather when the ExceptionInfo object was created. /// For most debugging purposes that difference in time does not matter, but /// when the exact time is required, do not rely on this value! /// public readonly DateTime DateTime = DateTime.Now;
ErrorFrame[] frames; /// <summary> /// Gets the different StackFrames that led to this /// public ErrorFrame[] Frames {
get { return frames; } }
public int FrameCount {
get { return frames.Length; } }
int usercodeindex = -1; /// <summary> /// Returns the index of the first frame that occured in user code (and thus /// the first frame that is actually debuggable ;-) ) /// public int UserFrameIndex {
get { return usercodeindex; } }
/// <summary> /// Information about the last step before this error occured. The returned /// object contains information about that step, such as the method that was running. /// When running in debug, the pdb files also provide info on the original code filename and line number /// public ErrorFrame LastFrame {
get {
if (frames.Length > 0) return frames[0]; return null; } }
/// <summary> /// Where <see cref="LastFrame"/> returns the last step in general, this property /// returns the last step in non-system code /// <seealso cref="UserFrameIndex"/> /// public ErrorFrame LastUserFrame {
get {
if (usercodeindex == -1) return null; return frames[usercodeindex]; } }
/// <summary> /// The exception type /// public string Type {
get { return Exception.GetType().Name; } } #endregion
#region Mail NB: framework specific /// This mail section was created in .net 2.0, you might /// want to remove this part or alter it if using earlier versions
/// <summary> /// Sends an email to the specified address /// /// <param name="To"> public void Send( string To) {
System.Net.Mail.MailMessage m = GetExceptionMail(); m.To.Add(To); System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("mail"); client.Send(m); }
/// <summary> /// Send the exception info (in html format) to the addressee. /// NB, some presumtions were made when trying to create a quick and /// dirty send method. Safest way it to use <see cref="FillMailMessage"/> or /// <see cref="GetExceptionMail"/> and do the sending manually /// /// <param name="ex"> /// <param name="To"> public static void Send( Exception ex, string To) {
GetInfo(ex).Send(To); }
/// <summary> /// Gets a mailmessage instance containing the Exception info. /// The from address is tried to be set to a custom address. If this /// fails (or if you want to set a custom one), you'll have to set the From /// address manually. /// /// <returns> public static System.Net.Mail. MailMessage GetExceptionMail( Exception ex) {
return GetInfo(ex).GetExceptionMail(); } System.Net.Mail.MailMessage GetExceptionMail() {
System.Net.Mail. MailMessage m = new System.Net.Mail. MailMessage(); FillMailMessage(m); try{
m.From = new System.Net.Mail.MailAddress("Exceptions@" + Environment.UserDomainName); } catch { } return m; }
/// <summary> /// Sets the body of the message to hold the exeption info /// /// <param name="m"> public void FillMailMessage(System.Net.Mail. MailMessage m) {
m.IsBodyHtml = true; m.Body = GetTotalMessage(); } #endregion
#region ExtraInfo NB
/// <summary> /// This class can hold extra info for an <see cref="ExceptionInfo"/> /// Its main purpose is to output this extra info to the html text /// public abstract class ExtraInfo : IExtraInfo{
/// <summary> /// With this method, the extra info writes itself (in html format) to /// the stringbuilder /// /// <param name="sb"> public abstract void AppendHTML(StringBuilder sb, ExceptionInfo ei);
public static implicit operator ExtraInfo( string text) {
return new TextInfo(text); }
}
public class ValuesInfo : ExtraInfo{
List< ValueEntry> values = new List< ValueEntry>(); class ValueEntry{
public string Name, Text; } public string Header;
public void AddText( string text) {
AddValue(null, text); } public void AddValue( string Name, string Value) {
ValueEntry v = new ValueEntry(); v.Name = Name; v.Text = Value; values.Add(v); }
public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
ei.openBlock(1, 1); if (Header != null)
sb.Append("<b><i>").Append(Header).Append(":"); if (values.Count > 0) {
ei.openBlock(1, 1); foreach ( ValueEntry ve in values) {
if (ve.Name == null)
sb.Append(ve.Text); else
ei.appendInfo(ve.Name, ve.Text); } ei.closeBlock();} ei.closeBlock(); }
}
/// <summary> /// Adds plain text as extrainfo /// public class TextInfo : ExtraInfo{
public string Text; public TextInfo( string text) { Text = text; } public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
sb.Append(Text); } }
#region SQL extra info
/// <summary> /// Used to display the history of sql strings. /// public class SQlStringInfo : ExtraInfo {
List< string> list = new List< string>(); public void Add( string sql) {
list.Add(sql); }
public void AddRange( IEnumerable< string> values) {
list.AddRange(values); }
public override void AppendHTML( StringBuilder sb, ExceptionInfo ei) {
ei.openBlock(1); sb.Append( "<B>Recent sql strings:"); ei.openBlock( "border:'gray 1 solid';margin-left:15"); int i = 0; foreach ( var sql in list) {
sb.Append( "<SPAN><B>--")
.Append(i++ + 1) .Append("--<span style='margin-left:10'>") .Append(sql) .Append("<BR>"); } ei.closeBlock(); ei.closeBlock(); }
}
/// <summary> /// Add one or more sql strings to the list to be displayed in the recent /// sql list. /// This is usefull when you keep a list of recent executed sql strings somewhere /// and want to include them in the exception output /// /// <param name="SQL"> public void AddSQL( IEnumerable< string> SQL) {
if (sqls == null) {
sqls = new SQlStringInfo(); AddExtraInfo(sqls); } sqls.AddRange(SQL); }
SQlStringInfo sqls; #endregion
/// <summary> /// Add <see cref="ExtraInfo"/>. You can also use a string as parameter /// /// <param name="ei"> public void AddExtraInfo( IExtraInfo ei) {
if (extrainfo == null) extrainfo = new List<IExtraInfo>(); extrainfo.Add(ei); }
List<IExtraInfo> extrainfo;
/// <summary> /// returns the amount of <see cref="ExtraInfo"/> objects added /// public int ExtraInfoCount {
get {
if (extrainfo == null) return 0; return extrainfo.Count; } }
#region HTML const string ExtraInfoAnchor = "ExtraInfo"; protected virtual void appendExtraInfo() {
if (extrainfo != null) {
appendBR(); appendHR(); openBlock(2); sb.Append( "<B><A name='").Append(ExtraInfoAnchor)
.Append("'>Extra Info<BR><HR>"); openBlock( "margin-left:10;font-size:smaller"); foreach ( IExtraInfo ei in extrainfo) {
ei.AppendHTML(sb, this); } closeBlock(); closeBlock(); } } #endregion
#endregion
#region Static /// <summary> /// Rather than creating a new instance manually, use /// this method to choose the proper exception object /// /// <param name="ex"> /// <returns> public static ExceptionInfo GetInfo( Exception ex) {
if (ex == null) return null; if (ex is SqlException) return new SqlExceptionInfo(ex as SqlException); if (ex is ReflectionTypeLoadException) return new ReflectionTypeLoadExceptionInfo(ex as ReflectionTypeLoadException); return new ExceptionInfo(ex); } public static implicit operator ExceptionInfo(Exception ex) {
return GetInfo(ex); } #endregion
#region Inner Exceptions int exceptioncount; /// <summary> /// returns the total number of exceptions (Main Exception + all inner exceptions) /// public int ExceptionCount {
get { return exceptioncount; } } #endregion
#region html information /// BEWARE: this code is manufactured to quickly create the html information /// and is not nicely constructed for reusability. /// A bit more friendly code for html is used in the AutoFormatter (http://blogs.vbcity.com/hotdog/archive/2005/12/30/5759.aspx) , /// but did not use that here to keep the code portable for fresh applications
/// <summary> /// Gets information about this exception and all inner exceptions in HTML format /// /// <returns> public string GetTotalMessage() {
return AppendMessage(new StringBuilder()).ToString(); }
/// <summary> /// This stringbuilder is only used in the html functions /// protected StringBuilder sb; protected void openBlock() {
openBlock(null); } protected void openBlock( int Border) {
openBlock(Border, 0); } protected void openBlock( int Border, int Indent) {
openBlock( "Border='" + Border + "px black solid'"
+ (Indent > 0 ? "margin-left=" + (Indent * 20) : null) ); } protected void openBlock( string Style) {
openclose(false, Style); } protected void closeBlock() {
openclose(true, null); } void openclose( bool close, string Style) {
sb.Append( "<"); if (close) sb.Append( "/"); sb.Append( "DIV"); if (Style != null)
sb.Append(" Style=\"").Append(Style).Append("\""); sb.Append( ">"); } public StringBuilder AppendMessage( StringBuilder builder) {
sb = builder; openBlock(2); appendTop(); appendInfo("Exception info created on", DateTime.ToString()); appendInfo("Application", AppDomain.CurrentDomain.FriendlyName); openBlock("font-size:smaller;margin-left:20"); appendHeaderBottom(); closeBlock(); Exception ex = Exception; int depth = 0; while (append(ex, depth++)) { ex = ex.InnerException; } appendExtraInfo(); appendBottom(); closeBlock(); sb = null; return builder; }
void appendMessage() { }
void IExtraInfo.AppendHTML( StringBuilder sb, ExceptionInfo ei) {
this.sb = sb; openBlock(2, 1); appendTitle( "Exception Info"); try{
AppendMessage(sb); } catch ( Exception ex) {
appendTitle("Error obtaining exception info: " + ex.Message); } closeBlock(); }
/// <summary> /// Gives inheriting classes the possibility to append html text at the top /// of the info block, inside the main border /// protected virtual void appendTop() { } /// <summary> /// Gives inheriting classes the possibility to append html inside the header /// of the info block. /// The base functionality adds links to the main and inner exceptions (if there are any) /// protected virtual void appendHeaderBottom() {
if (exceptioncount > 1) {
for ( int i = 0; i < exceptioncount; i++) {
openHeaderLink(AnchorNamePrefix + i); AppendExceptionTitle(i); sb.Append(""); } } if (extrainfo != null) {
openHeaderLink(ExtraInfoAnchor); sb.Append("Extra Info"); } }
/// <summary> /// Gives inheriting classes the possibility to append html inside the header /// of the Exception info block. /// protected virtual void appendExceptionHeader() { }
/// <summary> /// Used to add links in the header /// /// <param name="href"> /// <param name="name"> protected virtual void openHeaderLink( string href) {
sb.Append( "<A style='margin-left:15' href='#").Append(href)
.Append("'>"); }
/// <summary> /// Gives inheriting classes the possibility to append html text at the /// end of the exception info, but before the main block is closed /// protected virtual void appendBottom() { }
/// <summary> /// The prefix of the name that is added per depth so that code can /// point directly to one of the inner exceptions /// The Name for the first exception is ExDepth0 , the second ExDepth1 and so forth. /// Pointing to it in a href is subseqeuntly done with href = "#ExDepthX" where X is the inner exception index /// public const string AnchorNamePrefix = "ExDepth";
void AppendExceptionTitle( int depth) {
if (depth == 0)
sb.Append("Main Exception"); else
sb.Append("Inner Exception [").Append(depth).Append("]"); }
bool append( Exception ex, int depth) {
if (ex == null) return false; appendHR(); //add anchor informationsb.Append( "<A NAME=\"").Append(AnchorNamePrefix).Append(depth)
.Append("\" style='font-size:smaller'>"); AppendExceptionTitle(depth); sb.Append( ": <span style='background-color:#990000;color:white;font-weight:bolder;width:100%'>")
.Append(ex.Message) .Append(""); openBlock(1, 1); if (depth > 0) {
ExceptionInfo ei = GetInfo(ex); ei.sb = sb; ei.appendHeader(); try{
ei.appendExtraInfo(); } catch ( Exception ex2) {
appendTitle("Error writing extra info: " + ex2.Message); } } else
appendHeader(); closeBlock(); return true; }
const string
usercodecolor = "lightblue", systemcodecolor = "beige"; void appendHeader() {
openBlock( "border:'1 green solid';"); appendInfo( "Type", Type); if (usercodeindex >= 0) {
ErrorFrame ef = LastUserFrame; appendInfo("Last user code", ef.FullMethodSignature + " (Line " + ef.Line + " in '" + ef.FileName + "')"); } appendExceptionHeader(); if (frames.Length > 0) {
appendTitle( "Stack"); sb.Append( "<span style='margin-left:20;font-size:smaller'>Legend: "); for ( int i = 0; i < 2; i++) {
sb.Append( "<span style=\"background-color:")
.Append(i == 0 ? systemcodecolor : usercodecolor) .Append("border='1 black solid';margin-left:15\">") .Append(i == 0 ? "No debug information" : "With debug information") .Append(""); } sb.Append( ""); openBlock( "margin-left:40;font-size:smaller"); foreach ( ErrorFrame ef in frames) {
appendStack(ef); } closeBlock(); } else
sb.Append("--No StackTrace available--"); closeBlock();} protected void appendTitle( string Name) {
sb.Append("<B>").Append(Name).Append(": "); } protected void appendInfo( string Name, string Value) {
appendTitle(Name); sb.Append( "<SPAN style='position:relative;left:20'>")
.Append(Value).Append("<BR>"); } protected void appendHR() {
sb.Append("<HR>"); } protected void appendBR() {
sb.Append("<BR>"); }
void appendStack( ErrorFrame ef) {
openBlock( "border:'1 solid black';background-color:"
+ (ef.IsUserCode ? usercodecolor : systemcodecolor) ); sb.Append( "<span style='font-size:larger;font-style:italic;color:olive'><U>"); appendInfo( "Method", ef.MethodName); sb.Append( ""); appendInfo( "NameSpace", ef.NameSpace); appendInfo( "Full Signature", ef.MethodSignature);
if (ef.IsUserCode) {
appendTitle( "File"); sb.Append( "<A href='")
.Append(ef.FileName).Append("'>").Append(ef.FileName).Append("") .Append("<BR>"); appendInfo( "Line", ef.Line.ToString()); appendTitle( "Code snippet"); openBlock( "border='1 black dotted';margin-left=25"); ef.AppendCode(sb, CodeSnippetExtraLines); closeBlock(); } closeBlock();
}
/// <summary> /// Only applies when getting html text. This is the number of lines on /// each side of the offending line, that should be included in code snippets /// public int CodeSnippetExtraLines = 3; #endregion
public override string ToString() {
return Exception.ToString(); }
#region IO
/// <summary> /// Shows the exception in html format by outputting to a default file first /// and then opening it with the default browser. /// (a form could have been used to do this, but tried to keep this info /// class usable for Console applications as well) /// public void ShowException() {
ShowExceptionFile("default"); }
/// <summary> /// Shows the exception in a browser, see <see cref="ShowException()"/> for more info /// /// <param name="ex"> public static void ShowException( Exception ex) {
GetInfo(ex).ShowException(); }
/// <summary> /// First outputs the file (see <see cref="CreateExceptionFile"/>), then opens /// it with the default connected viewer. /// NB: the file is NOT appended. Existing info will be overwritten /// NB2: if no writing is necessary /// /// <param name="file"> public void ShowExceptionFile( string file) {
ShowExceptionFile(file, false); } /// <summary> /// Same as <see cref="ShowExceptionFile(string)"/>, but with the option /// to choose whether to append or not /// /// <param name="file"> /// <param name="append"> public void ShowExceptionFile( string file, bool append) {
CreateExceptionFile(file, append); ShowFile(file); }
/// <summary> /// All this does is run the file (using System.Diagnostics.Process.Start) /// If an exception file is to be shown, make sure it is created first, or /// use <see cref="ShowExceptionFile"/> to create and show the file instead /// of this method /// /// <param name="file"> public void ShowFile( string file) {
CheckFile(ref file); Process.Start(file); }
/// <summary> /// Appends complete directory information to a name (see code for details ;-) ) /// /// <param name="file"> public void CheckFile( ref string file) {
FileInfo fi = new FileInfo(file); if (fi.FullName.Length > file.Length) {
//no directory provided -> use application directory string dir =
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Exceptions" + "\\" + AppDomain.CurrentDomain.FriendlyName + "\\"; if (! Directory.Exists(dir))
Directory.CreateDirectory(dir); file = dir + file; } if (fi.Extension.Length == 0) file += ".htm"; }
/// <summary> /// Outputs the exception to the specified file. Make sure the extension can /// be read by explorer. If no extension is provided, ".htm" is used /// /// <param name="file"> /// <param name="append"> public void CreateExceptionFile( string file, bool append) {
CheckFile( ref file); using ( StreamWriter sw = new
StreamWriter(file, append, System.Text.Encoding.ASCII)) sw.Write(GetTotalMessage()); } #endregion
}
public class SqlExceptionInfo : ExceptionInfo{
public new readonly SqlException Exception; public SqlExceptionInfo( SqlException ex)
: base(ex) {
Exception = ex; }
#region HTML
const string AnchorSQL = "sqlinfo";
protected override void appendExceptionHeader() {
base.appendTop(); appendTitle( "SQL Errors (" + Exception.Errors.Count + ")"); openBlock( "border:'1 gray solid';font-size:0.6em;margin-left:40;margin-right:20"); for ( int i = 0; i < Exception.Errors.Count; i++) {
appendSQLError(i); } closeBlock(); } protected override void appendHeaderBottom() {
base.appendHeaderBottom(); openHeaderLink(AnchorSQL); sb.Append("SQL info").Append(""); }
void appendSQLError( int index) {
if (index > 0) appendHR(); SqlError se = Exception.Errors[index]; appendInfo("SQL class (severity)", se.Class.ToString()); appendInfo("Server", se.Server); appendInfo("T-sql line number:", se.LineNumber.ToString()); appendInfo("Message", se.Message); appendInfo("Procedure", se.Procedure); appendInfo("Source", se.Source); appendInfo("Sql error number", se.Number.ToString()); }
#endregion
}
public class ReflectionTypeLoadExceptionInfo : ExceptionInfo {
public readonly ReflectionTypeLoadException ReflectionTypeLoadException; public ReflectionTypeLoadExceptionInfo( ReflectionTypeLoadException Exception)
: base(Exception) {
this.ReflectionTypeLoadException = Exception; foreach ( Exception e in ReflectionTypeLoadException.LoaderExceptions) {
try {
AddExtraInfo(GetInfo(e)); } catch ( Exception ex) {
AddExtraInfo(new TextInfo("Error obtaining exception info: " + ex.Message)); AddExtraInfo(new TextInfo(e.Message)); } } }
}
/// <summary> /// Wrapper around a <see cref="StackFrame"/>. Not that much added functionality, but /// some properties instead of methods to be able to use easy databinding /// public class ErrorFrame{
public readonly StackFrame Base; public ErrorFrame( StackFrame frame) {
Base = frame; }
public int Line {
get { return Base.GetFileLineNumber(); } } public string FileName {
get { return Base.GetFileName(); } }
public string MethodName {
get { return Base.GetMethod().Name; } }
public string MethodSignature {
get { return Base.GetMethod().ToString(); } }
public string FullMethodSignature {
get { return NameSpace + " - " + MethodSignature; } }
public string NameSpace {
get { return Base.GetMethod().ReflectedType.FullName; } }
public bool IsUserCode {
get { return Line > 0; } }
public MethodBase GetMethod() {
return Base.GetMethod(); }
public string GetCode() {
return GetCode(2); } /// <summary> /// /// /// <param name="Lines">indicates the amount of lines before and after the Errorline to show public string GetCode(int Lines) {
if (!IsUserCode)
return "No code available for a system method"; string file = FileName; if (file == null || ! File.Exists(file))
return "File not found!";
try {
return AppendCode(new StringBuilder(), Lines).ToString(); } catch (Exception ex) {
return "Error obtaining code information: " + ex.Message; }
}
internal StringBuilder AppendCode( StringBuilder sb, int Lines) {
int line = Line, from = line - Lines, to = line + Lines, curline = 0; ; int len = sb.Length; const string space = " "; string tab = null; for ( int tabcount = 0; tabcount < 4; tabcount++) {
tab += space; } StreamReader sr = null; try{
sr = new StreamReader(FileName); string l; while ((l = sr.ReadLine()) != null) {
if (++curline >= from) {
if (curline > to) break; if (line == curline) sb.Append("<B>"); sb.Append(l.Replace("\t", tab).Replace(" ", space)); if (line == curline) sb.Append(""); sb.Append("<BR>"); } }} catch ( Exception ex) {
sb.Length = len; sb.Append( "Error obtaining code information: ")
.Append(ex.Message); } finally{
if (sr != null) sr.Close(); } return sb; }
}
public interface IExtraInfo{
void AppendHTML(StringBuilder sb, ExceptionInfo ei); } }
. . . Example Usage
Subro.Interaction. FeedbackCollection fbc = new Subro.Interaction. FeedbackCollection();
//A thread is not obligatory of course, but for batch usage the thread //should be thread safe, this example shows that the feedback visualizer //is thread safe Thread t = new Thread( delegate() {
//shows fbc.Add("Start");
//feedback with progress bar Subro.Interaction. Feedback fb = fbc.Add( "Working towards a known target"); int target = 150; fb.ProgressTarget = target; for ( int i = 0; i <= target; i++) {
Thread.Sleep(10); fb.Progress = i; }
fb = fbc.Add("busy with something"); for (int i = 0; i < 100; i++) {
//Show alive shows a 'pulse' line to indicate that //the process is still busy (used when the exact progress is unknown) fb.ShowAlive(); Thread.Sleep(10);
//Each feedback item in turn can have 'children' attached //this means that information you want to have accessible, //but not necessarily shown each time can be added easily. //each child item in turn of course can also posses child items //when the feedback is shown, an 'i' button will appear to //indicate there is extra information available fb.Children.Add("Proccessed " + i.ToString());
}
//special feedback items such as excpetions are available as //well. Exception example fbc.Add(new Exception("If something went wrong, you would find the exception info here"));
//of course custom feedback items (inheriting from Feedback) //could be added as well, where you can show your own custom //info, eg, you could show your own form when the information //button is clicked by overriding the ShowExtraInfo method
//instead of, or after, showing, the results could also be //saved (or mailed), eg for saving: fbc.Save(@"c:\temp\testresults.htm");
}); t.IsBackground = true; t.Start();
//if no feedbackform was created, the feedback would still //be gathered, without the (relatively small) overhead of the feedback fbc.ShowDialog(); //other options are non modal Show and ShowHTML //The latter outputs all feedback to a html page with the complete overview Subro.Interaction.FeedbackCollection fbc = new Subro.Interaction.FeedbackCollection();
//A thread is not obligatory of course, but for batch usage the thread //should be thread safe, this example shows that the feedback visualizer //is thread safe Thread t = new Thread( delegate() {
//shows fbc.Add("Start");
//feedback with progress bar Subro.Interaction. Feedback fb = fbc.Add( "Working towards a known target"); int target = 150; fb.ProgressTarget = target; for ( int i = 0; i <= target; i++) {
Thread.Sleep(10); fb.Progress = i; }
fb = fbc.Add("busy with something"); for (int i = 0; i < 100; i++) {
//Show alive shows a 'pulse' line to indicate that //the process is still busy (used when the exact progress is unknown) fb.ShowAlive(); Thread.Sleep(10);
//Each feedback item in turn can have 'children' attached //this means that information you want to have accessible, //but not necessarily shown each time can be added easily. //each child item in turn of course can also posses child items //when the feedback is shown, an 'i' button will appear to //indicate there is extra information available fb.Children.Add("Proccessed " + i.ToString());
}
//special feedback items such as excpetions are available as //well. Exception example fbc.Add(new Exception("If something went wrong, you would find the exception info here"));
//of course custom feedback items (inheriting from Feedback) //could be added as well, where you can show your own custom //info, eg, you could show your own form when the information //button is clicked by overriding the ShowExtraInfo method
//instead of, or after, showing, the results could also be //saved (or mailed), eg for saving: fbc.Save(@"c:\temp\testresults.htm");
}); t.IsBackground = true; t.Start();
//if no feedbackform was created, the feedback would still //be gathered, without the (relatively small) overhead of the feedback fbc.ShowDialog(); //other options are non modal Show and ShowHTML //The latter outputs all feedback to a html page with the complete overview
. . .
|
|