Click here to Skip to main content
15,883,901 members
Articles / Programming Languages / C#

VSEDebug - VS.NET Debugging Enhancement

Rate me:
Please Sign up or sign in to vote.
4.92/5 (37 votes)
25 Apr 20049 min read 169.3K   2.2K   58  
VSEDebug is a VS.NET debugger add-in that adds the ability to debug complex types in simpler form.
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace vsedebug
{
	//This class represent the user control that controls the This Window 
	public class VSEThis : System.Windows.Forms.UserControl
	{
		//The toolbar button that closes this window or opens it
		public static Microsoft.Office.Core.CommandBarButton ControlWindowButton;
		//The actual window that contains this user control
		public static EnvDTE.Window ControlWindow;
		//And the timer
		public static System.Threading.Timer UpdateTimer;
		public static System.Threading.TimerCallback TimerDelegate;

		private int CurrentElementsPerDivision;
		private SynapticEffect.Forms.TreeListView VSETree;
		private EnvDTE.DebuggerEvents DebuggerEvents;
		//This is the name of the current stack frame.  If the name of the current stack frame has changed, we need to redraw the tree
		private String StackFrameName;
		private System.Windows.Forms.ContextMenu ThisMenu;
		private System.Windows.Forms.MenuItem CollectDataMenuItem;
		//Boolean indicating whether the debugger is in it's first initial startup state
		private bool DebuggerIsStarting;

		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public VSEThis()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

			//initialize the debugger events
			DebuggerEvents = (EnvDTE.DebuggerEvents)VSEUtil.ApplicationObject.Events.DebuggerEvents;	
			//now initialize the event sinks
			DebuggerEvents.OnEnterBreakMode += new EnvDTE._dispDebuggerEvents_OnEnterBreakModeEventHandler(this.OnEnterBreakMode);
			DebuggerEvents.OnEnterDesignMode += new EnvDTE._dispDebuggerEvents_OnEnterDesignModeEventHandler(this.OnEnterDesignMode);
			DebuggerEvents.OnEnterRunMode += new EnvDTE._dispDebuggerEvents_OnEnterRunModeEventHandler(this.OnEnterRunMode);

			ControlWindowButton.Click += new Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(this.CommandBarButtonClick);

			DebuggerIsStarting = true;
			
			//and set the locally held copy of ElementsPerDivision
			CurrentElementsPerDivision = VSEUtil.ElementsPerDivision;
			
			TimerDelegate = new System.Threading.TimerCallback(this.OnTimerElapsed);
			UpdateTimer = new System.Threading.Timer(TimerDelegate, null, System.Threading.Timeout.Infinite, VSEUtil.UpdateDelay);

			//now, if we're in debug mode when the addin loads, make sure the addin loads properly
			if(VSEUtil.VSDebugger.CurrentMode != EnvDTE.dbgDebugMode.dbgDesignMode)
			{
				//Set the timer to enabled.  This ensures an initial update
				StartTimer(VSEUtil.UpdateDelay);
				//Set the toolbar button to visible
				ControlWindowButton.Visible = true;
			}
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			SynapticEffect.Forms.ToggleColumnHeader toggleColumnHeader1 = new SynapticEffect.Forms.ToggleColumnHeader();
			SynapticEffect.Forms.ToggleColumnHeader toggleColumnHeader2 = new SynapticEffect.Forms.ToggleColumnHeader();
			SynapticEffect.Forms.ToggleColumnHeader toggleColumnHeader3 = new SynapticEffect.Forms.ToggleColumnHeader();
			this.VSETree = new SynapticEffect.Forms.TreeListView();
			this.ThisMenu = new System.Windows.Forms.ContextMenu();
			this.CollectDataMenuItem = new System.Windows.Forms.MenuItem();
			this.SuspendLayout();
			// 
			// VSETree
			// 
			this.VSETree.AllowDrop = true;
			this.VSETree.BackColor = System.Drawing.SystemColors.Window;
			toggleColumnHeader1.Hovered = false;
			toggleColumnHeader1.Image = null;
			toggleColumnHeader1.Index = 0;
			toggleColumnHeader1.Pressed = false;
			toggleColumnHeader1.ScaleStyle = SynapticEffect.Forms.ColumnScaleStyle.Slide;
			toggleColumnHeader1.Selected = false;
			toggleColumnHeader1.Text = "Name";
			toggleColumnHeader1.TextAlign = System.Windows.Forms.HorizontalAlignment.Left;
			toggleColumnHeader1.Visible = true;
			toggleColumnHeader1.Width = 200;
			toggleColumnHeader2.Hovered = false;
			toggleColumnHeader2.Image = null;
			toggleColumnHeader2.Index = 0;
			toggleColumnHeader2.Pressed = false;
			toggleColumnHeader2.ScaleStyle = SynapticEffect.Forms.ColumnScaleStyle.Slide;
			toggleColumnHeader2.Selected = false;
			toggleColumnHeader2.Text = "Value";
			toggleColumnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Left;
			toggleColumnHeader2.Visible = true;
			toggleColumnHeader2.Width = 150;
			toggleColumnHeader3.Hovered = false;
			toggleColumnHeader3.Image = null;
			toggleColumnHeader3.Index = 0;
			toggleColumnHeader3.Pressed = false;
			toggleColumnHeader3.ScaleStyle = SynapticEffect.Forms.ColumnScaleStyle.Slide;
			toggleColumnHeader3.Selected = false;
			toggleColumnHeader3.Text = "Type";
			toggleColumnHeader3.TextAlign = System.Windows.Forms.HorizontalAlignment.Left;
			toggleColumnHeader3.Visible = true;
			toggleColumnHeader3.Width = 150;
			this.VSETree.Columns.AddRange(new SynapticEffect.Forms.ToggleColumnHeader[] {
																							toggleColumnHeader1,
																							toggleColumnHeader2,
																							toggleColumnHeader3});
			this.VSETree.ColumnSortColor = System.Drawing.Color.Gainsboro;
			this.VSETree.ColumnTrackColor = System.Drawing.Color.WhiteSmoke;
			this.VSETree.ContextMenu = this.ThisMenu;
			this.VSETree.Dock = System.Windows.Forms.DockStyle.Fill;
			this.VSETree.GridLineColor = System.Drawing.Color.WhiteSmoke;
			this.VSETree.GridLines = true;
			this.VSETree.HeaderMenu = null;
			this.VSETree.ItemHeight = 20;
			this.VSETree.ItemMenu = null;
			this.VSETree.LabelEdit = false;
			this.VSETree.Name = "VSETree";
			this.VSETree.RowSelectColor = System.Drawing.SystemColors.Highlight;
			this.VSETree.RowTrackColor = System.Drawing.Color.WhiteSmoke;
			this.VSETree.ShowLines = true;
			this.VSETree.Size = new System.Drawing.Size(150, 125);
			this.VSETree.SmallImageList = null;
			this.VSETree.StateImageList = null;
			this.VSETree.TabIndex = 0;
			this.VSETree.Text = "treeListView1";
			this.VSETree.KeyDown += new System.Windows.Forms.KeyEventHandler(this.VSETree_KeyDown);
			this.VSETree.Expand += new SynapticEffect.Forms.ExpandEventHandler(this.VSETree_Expand);
			this.VSETree.Scroll += new SynapticEffect.Forms.ScrollEventHandler(this.VSETree_Scroll);
			// 
			// ThisMenu
			// 
			this.ThisMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					 this.CollectDataMenuItem});
			// 
			// CollectDataMenuItem
			// 
			this.CollectDataMenuItem.Checked = true;
			this.CollectDataMenuItem.Index = 0;
			this.CollectDataMenuItem.Text = "Collect Info";
			this.CollectDataMenuItem.Click += new System.EventHandler(this.CollectDataMenuItem_Click);
			// 
			// VSEThis
			// 
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.VSETree});
			this.Name = "VSEThis";
			this.Size = new System.Drawing.Size(150, 125);
			this.ResumeLayout(false);

		}
		#endregion

		public void UpdateVariables()
		{
			//Stop the timer
			StopTimer();

			if(!ControlWindow.Visible || !CollectDataMenuItem.Checked)
			{
				return;
			}

			//we need to update the current local variables, and if the This changed entirely, clear this tree and add the new ones.
			//Remember I only need to deal with top level items
			EnvDTE.Expression newexpressions = GetThis();
			if(StackFrameName != VSEUtil.VSDebugger.CurrentStackFrame.FunctionName || CurrentElementsPerDivision != VSEUtil.ElementsPerDivision)
			{
				//remove all the nodes of the tree
				VSETree.Nodes.Clear();
				//and add the new ones
				//add a new node to the tree and set it's expression, then update it
				SynapticEffect.Forms.TreeListNode newnode = new SynapticEffect.Forms.TreeListNode(true);
				VSEDebugEvaluator.Evaluate(newexpressions.Name, newexpressions.Type, 
					newexpressions.Name, null, 
					newnode, VSEDebugEvaluator.EvaluateAction.evaluate);
				VSETree.Nodes.Add(newnode);
			}
			else
			{
				//no new expressions, just set the top level expression and call add update{
				SynapticEffect.Forms.TreeListNode cur = (SynapticEffect.Forms.TreeListNode)(VSETree.Nodes[0]);
				VSEDebugEvaluator.Evaluate(newexpressions.Name, newexpressions.Type, 
					newexpressions.Name, null, cur, VSEDebugEvaluator.EvaluateAction.evaluate);
			}
			//Now, set the current stack frame name so we can use it in future comparisons
			StackFrameName = VSEUtil.VSDebugger.CurrentStackFrame.FunctionName;
			CurrentElementsPerDivision = VSEUtil.ElementsPerDivision;

			//now render it
			VSETree.Refresh();
		}

		public void OnTimerElapsed(object senders)
		{
			//Make sure the current mode of the debugger is break mode, otherwise we could accidentally update while
			//code is executing
			if(VSEUtil.VSDebugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode)
			{
				UpdateVariables();
			}
		}

		//The event handler for the debugger break event.  Here, we look at the settings for the addin and perform certain operations
		//on the timer or do an update
		public void OnEnterBreakMode(EnvDTE.dbgEventReason reason, ref EnvDTE.dbgExecutionAction executionAction)
		{
			//Select the current mode that the addin is using for it's updates
			if(VSEUtil.delaymode == VSEUtil.DelayMode.delay)
			{
				//Delay mode.  Reset the timer to 0, so that a bunch of successive breaks will not cause the update cycle to run.  This
				//gives a good impression of the update cycle being very very fast
				StartTimer(VSEUtil.UpdateDelay);
			}
			else
			{
				if(VSEUtil.delaymode == VSEUtil.DelayMode.none)
				{
					//There is no delay on the update, simply make sure the timer is disabled and call update
					StopTimer();
					UpdateVariables();
				}
				else
				{
					//Manual update mode, set the timer to disabled.  This is neccesary because the scroll event will restart the timer
					StopTimer();
				}
			}
		}

		//This event handler is called when debugging ends and the IDE enters design mode
		public void OnEnterDesignMode(EnvDTE.dbgEventReason Reason)
		{
			//Clear the tree
			VSETree.Nodes.Clear();
			//Set the stack frame name to null so the next debugging session, regardless of where it starts, will do a clean update
			StackFrameName = null;
			//set the timer to disabled
			StopTimer();
			DebuggerIsStarting = true;
		}

		//This event handler is called when debugging starts or someone presses F5
		public void OnEnterRunMode(EnvDTE.dbgEventReason Reason)
		{
			//We only want to perform actions if the reason for debugging was a person started debugging, not on an F5 while debugging
			if(Reason == EnvDTE.dbgEventReason.dbgEventReasonGo)
			{
				if(DebuggerIsStarting)
				{
					//Set the timer to enabled.  This ensures an initial update
					StartTimerInitialDelay(VSEUtil.UpdateDelay, 2000);
				}
				//Set the toolbar button to visible
				ControlWindowButton.Visible = true;
				DebuggerIsStarting = false;
			}
		}

		//returns the This list
		private EnvDTE.Expression GetThis() 
		{
			EnvDTE.Expression vsexpr = VSEUtil.VSDebugger.GetExpression("this", false, -1);
			return vsexpr;
		}

		private void VSETree_Expand(object sender, SynapticEffect.Forms.CollapseExpandEventArgs e)
		{
			//First make sure we're collecting data
			if(!CollectDataMenuItem.Checked)
			{
				return;
			}
			
			//The parent node for this evaluate item is equal to the parentnode of the node that was expanded
			SynapticEffect.Forms.TreeListNode parentnode = (SynapticEffect.Forms.TreeListNode)e.NodeChanged.ParentNode();
			//But, if the expanded node was a top level node, set it to null instead. (It will end up pointing to the control
			//otherwise)
			if(e.NodeChanged.IsTopLevel)
			{
				parentnode = null;
			}
			//And call evaluate, but this time with the expand action.
			VSEDebugEvaluator.Evaluate(e.NodeChanged.ExpressionText, e.NodeChanged.ExpressionType,
				e.NodeChanged.Text, parentnode, e.NodeChanged, VSEDebugEvaluator.EvaluateAction.expand);
		}

		//This handler is called when the control is scrolled.  Since items that are not visible in the tree are not updated,
		//we need to update the tree if any new tems come into view
		private void VSETree_Scroll(object sender, System.EventArgs e)
		{
			
		}

		private void CollectDataMenuItem_Click(object sender, System.EventArgs e)
		{
			//Set the check markyeah
			CollectDataMenuItem.Checked = !CollectDataMenuItem.Checked;
			//If it is set to collect, update
			if(CollectDataMenuItem.Checked)
			{
				VSETree.Nodes.Clear();
				StackFrameName = null;
				UpdateVariables();
			}
		}

		//This is the event hadnler for keydown events.  This is sort of a hack around the problem where a window would steal
		//keyboard focus and prevent an f5.  The reason that it is a hack is that I assume the keys you're using for the various
		//debugging steps
		private void VSETree_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
					//The F2 key is the manual update key if and only if a debugger window has focus
				case Keys.F2:
					UpdateVariables();
					e.Handled = true;
					break;
					//F5 is the standard continue
				case Keys.F5:
					if(e.Shift != true)
					{
						VSEUtil.VSDebugger.Go(false);
					}
					else
					{
						VSEUtil.VSDebugger.Stop(false);
					}
					break;
					//F10 is the standard step
				case Keys.F10:
					VSEUtil.VSDebugger.StepOver(false);
					break;
					//F11 is the standard step out
				case Keys.F11:
					if(e.Shift != true)
					{
						VSEUtil.VSDebugger.StepInto(false);
					}
					else
					{
						VSEUtil.VSDebugger.StepOut(false);	
					}
					break;
			}
			//Set the message to handled
			e.Handled = true;
		}

		//The event handler for the toolbar button that shows and hides this window
		public void CommandBarButtonClick(Microsoft.Office.Core.CommandBarButton button, ref bool handled) 
		{
			//Set the window to the opposite of its previous state
			ControlWindow.Visible = !ControlWindow.Visible;
			//Now, if its is visible
			if(ControlWindow.Visible) 
			{
				//Do an update
				UpdateVariables();
				//And set the look of the button to indented
				ControlWindowButton.State = Microsoft.Office.Core.MsoButtonState.msoButtonDown;
			}
			else
			{
				//And set the button look
				ControlWindowButton.State = Microsoft.Office.Core.MsoButtonState.msoButtonUp;
			}
		}

		//Wrapper functions for the timer
		public static void StopTimer()
		{
			UpdateTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
		}
		public static void StartTimer(int interval)
		{
			UpdateTimer.Change(0, interval);
		}
		public static void StartTimerInitialDelay(int interval, int initialdelay)
		{
			UpdateTimer.Change(initialdelay, interval);
		}
		public static void SetIntervalNoRestart(int interval)
		{
			UpdateTimer.Change(System.Threading.Timeout.Infinite, interval);
		}
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I'm a student at the University of Florida studying computer engineering.

You may find additional information about vsedebug at http://vsedebug.sourceforge.net

Comments and Discussions