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);
}
}
}