Click here to Skip to main content
15,897,704 members
Articles / Programming Languages / C#

Reduce debugging time using the .NET StackTrace class

Rate me:
Please Sign up or sign in to vote.
4.64/5 (10 votes)
18 Nov 2011CPOL5 min read 50.7K   751   31  
Use the .NET StackTrace class to position yourself in the right place of source code that threw an Exception.
#if DEBUG
#pragma warning disable 1591 //- Disabling “Missing XML comment for publicly visible type or member XX”
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Diagnostics;


namespace StackTraceDumperForm {
    public partial class StackTraceDumperForm : Form {
        private const string _Name_ = "StackTraceDumperForm";

        private EnvDTE.DTE _dte = null;
        public EnvDTE.DTE VisualStudio {
            get {
                if( null == _dte )
                    GetVisualStudioInstance();
                return _dte; 
            }
            set { _dte = value; }
        }

        private bool GetVisualStudioInstance() {
            // Get an instance of the currently running Visual Studio IDE
            string sDTENameVS2010 = "VisualStudio.DTE.10.0";//- For Visual Studio 2010
            string sDTENameVS2008 = "VisualStudio.DTE.9.0";//- For Visual Studio 2008
            string sDTENameVS2005 = "VisualStudio.DTE.8.0";//- For Visual Studio 2005
            string sDTENameVS2003 = "VisualStudio.DTE.7.1";//- For Visual Studio.NET 2003
            string sDTENameVS2002 = "VisualStudio.DTE.7";  //- For Visual Studio.NET 2002
            string[] dtes = new string[] { sDTENameVS2010, sDTENameVS2008, sDTENameVS2005, sDTENameVS2003, sDTENameVS2002 };


            bool found = false;
            foreach( var it in dtes ) {
                try {
                    _dte = (EnvDTE.DTE) System.Runtime.InteropServices.Marshal.GetActiveObject( it );
                    if( null != _dte ) {
                        found = true;
                        break;
                    }
                }//end_try
                catch( Exception ex ) {
                    //- try another VS
                    Debug.WriteLine( ex.Message );
                    MessageBox.Show( "Sorry, not able to detect the current Visual Studio version!\r\nThe Button to link the source code won't work!", _Name_ + " Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning );
                }
            }//end_foreach
            return found;
        }

        
        public System.Diagnostics.StackTrace StackTrace { get; private set; }

        //- ctor
        public StackTraceDumperForm( Dictionary<string, string> debugInfos, string exMessage, System.Diagnostics.StackTrace trace ) {
            InitializeComponent();
            StackTrace = trace;
            splitter.SplitterDistance = (int)Math.Round( this.Width * 0.6);

             
            //- fill in the debugInfos Textbox
            txtDebugInfos.Text = "Exception Message: " + exMessage + "\r\n\r\n";
            StringBuilder sb = new StringBuilder( "DEBUG infos:\r\n" );
            if( null != debugInfos && debugInfos.Keys.Count > 0 ) {
                int MAXLengthForKey = debugInfos.Keys.Select( c => c.Length ).Max();
                //int MAXLengthForValue = debugInfos.Values.Select( c => c.Length ).Max();
                foreach( var it in debugInfos ) 
                    sb.AppendFormat( "\t{0}:{1}\r\n", it.Key.PadRight( MAXLengthForKey+ 10 ,' ' ), it.Value );
            }
            else {
                sb.AppendFormat( "\tunavailable!\r\n");
            }
            txtDebugInfos.Text += sb.ToString();
        }
        
       

        private void StackTraceDumperForm_Load( object sender, EventArgs e ) {
            if( null == this.StackTrace )
                return;

            try {
                System.Diagnostics.StackFrame[] frames = this.StackTrace.GetFrames();
                this.Text += "  (number of frames: " + frames.Count() + ")";
                CreateStackTraceControls( frames );
            }
            catch( Exception  exx) {
                Debug.WriteLine( exx.Message );
                if( null != exx.InnerException )
                    Debug.WriteLine( exx.InnerException.Message );
                throw;
            }
        }

        private void CreateStackTraceControls( System.Diagnostics.StackFrame[] frames ) {
            var lq = from frame in frames
                     let isCodeInsideNET = string.IsNullOrEmpty( frame.GetFileName() ) 
                     select new
                     {
                         IsCodeInsideNET = isCodeInsideNET,
                         ClassName = frame.GetMethod().DeclaringType.Name,
                         MethodName = frame.GetMethod().Name,
                         FileName = frame.GetFileName(),
                         Line = (int)frame.GetFileLineNumber(),
                         Column = (int)frame.GetFileColumnNumber(),
                     };


            int panelHeight = 30;
            Font labelFont = new System.Drawing.Font( "Microsoft Sans Serif", 10.2F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)) );
            string labelHeader = string.Empty;


            int linecounter = -1;
            foreach( var it in lq.Reverse() ) {
                linecounter++;


                if( linecounter != 0 )
                    labelHeader = new String( ' ', linecounter * 4 ) + "'-->";


                string filename = null;
                Color backgroundColor;
                if (it.IsCodeInsideNET) {
                    //- .NET core class scope
                    filename = ".NET code";
                    backgroundColor = Color.LightGray;
                }//end_if
                else{
                    //- BB class scope
                    filename = Path.GetFileName( it.FileName );
                    backgroundColor = Color.YellowGreen ;
                }//end_else
                    

                     
               

                //- on the left side
                //- the host panel
                Panel pnlLeft = new Panel();
                pnlLeft.Parent = splitter.Panel1;
                pnlLeft.Size = new Size( pnlLeft.Parent.Size.Width, panelHeight );
                pnlLeft.Location = new Point( 0, linecounter * panelHeight );
                pnlLeft.Anchor = ((System.Windows.Forms.AnchorStyles) (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
                //- the label
                Label lbl = new Label();
                lbl.Font = labelFont;
                lbl.BorderStyle = BorderStyle.FixedSingle;
                lbl.Text = string.Format( "{0}{1}::{2}", labelHeader, it.ClassName, it.MethodName );
                lbl.BackColor = backgroundColor;
                lbl.Dock = DockStyle.Fill;
                lbl.Parent = pnlLeft;



                //- on the right side
                //- the host panel
                Panel pnlRight = new Panel();
                pnlRight.Parent = splitter.Panel2;
                pnlRight.Size = new Size( pnlRight.Parent.Width, panelHeight );
                pnlRight.Location = pnlLeft.Location;
                pnlRight.Anchor = ((System.Windows.Forms.AnchorStyles) (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
                
                if(  it.IsCodeInsideNET ) {
                    //- the label
                    Label lblNumber = new Label();
                    lblNumber.BorderStyle = BorderStyle.FixedSingle;
                    lblNumber.Text = string.Format( "line:{0} in {1}", it.Line, filename );
                    lblNumber.BackColor = System.Drawing.SystemColors.Control;
                    lblNumber.Dock = DockStyle.Fill;
                    lblNumber.Parent = pnlRight; ;
                }
                else{
                    //- the button
                    Button btnLink = new Button();
                    btnLink.Text = "&Go to" + string.Format( " line:{0} in {1}", it.Line, filename );
                    btnLink.TextAlign = ContentAlignment.MiddleCenter;
                    btnLink.Image = StackTraceDumper.Properties.Resources.vs2010;
                    btnLink.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
                    btnLink.Dock = DockStyle.Fill;
                    btnLink.Parent = pnlRight; ;
                    btnLink.Tag = new FILE_LIN_COL()
                    {
                        FileName = it.FileName,
                        Line = it.Line,
                        Column = it.Column,
                    };
                    btnLink.Click += ( s, a ) =>
                    {
                        Button btn = s as Button;
                        string sFileName = ((FILE_LIN_COL) btn.Tag).FileName;
                        if( string.IsNullOrEmpty( sFileName ) )
                            return;

                        if( null != this.VisualStudio )
                            this.SelectLineInsideVisualStudio( sFileName, ((FILE_LIN_COL) btn.Tag).Line, ((FILE_LIN_COL) btn.Tag).Column );
                    };
                }//end_if
            }//end_foreach
        }

        private class FILE_LIN_COL {
            public string FileName { get; set; }
            public int Line { get; set; }
            public int Column { get; set; }
        }
    


        private void ClearVisualStudioOutputWindow() {
            try {
                _dte.ExecuteCommand( "Edit.ClearOutputWindow", "" );
            }//end_try
            catch( Exception exx ) {
                Debug.WriteLine( exx.Message );
                if( null != exx.InnerException )
                    Debug.WriteLine( exx.InnerException.Message );
                throw;
            }//end_catch
        }

        private void SelectLineInsideVisualStudio(string sourceCodeFileFullPath, int line , int column) {
            try {
                //var MacrosDTE2 = dte.MacrosIDE;
                EnvDTE.ProjectItem ptojItem = _dte.Solution.FindProjectItem( sourceCodeFileFullPath );
                if( null != ptojItem ) {
                    ptojItem.Open( EnvDTE.Constants.vsViewKindCode ).Activate();
                    EnvDTE.TextSelection textSelection = (EnvDTE.TextSelection) _dte.ActiveDocument.Selection;
                    textSelection.MoveToLineAndOffset( line, column, true );
                    textSelection.SelectLine();
                }
            }//end_try
            catch( Exception exx ) {
                Debug.WriteLine( exx.Message );
                if( null != exx.InnerException )
                    Debug.WriteLine( exx.InnerException.Message );
                throw;
            }//end_catch
        }

        private void btnClearVisualStudioOutputWindow_Click( object sender, EventArgs e ) {
            if( null != this.VisualStudio )
                this.ClearVisualStudioOutputWindow();
        }

    }//end_class
}
#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Italy Italy
I started coding in 1984 on a ZX Spectrum. From the Sinclair Basic I jumped straight to C++ (which I use since 1992). I wrote code in assembly 386, ANSI C++ (I love it) and VisualBasic 6.0 (urgh!!!); nowadays I write code in C# 4.0!

I was born in 1968 in Italy and I live in the north west of my country (near Venice). I work as a Project Leader/Senior Developer.

Computer Science interests: I like coding 3D-GameEngine and OpenGL applications.

Sport and hobbies: Martial Arts, Latino Dance, travels, reading and writing novels.

Comments and Discussions