Visual Studio 2010 Addin: Setting a class breakpoint

Introduction


There comes a time in the life of every .NET developer when you need Visual Studio to do something that
can only be described as a "class breakpoint": a quick command to set a breakpoint on every access to a class.
Unfortunately, after googling this concept, you'll find out that there's no easy way to accomplish this in Visual Studio.
In this article I present an addin that I created, which adds this and another similar command to the debug menu of the
development environment:

ConsoleApplication1 - Microsoft Visual Studio (Administrator)_2011-08-03_20-40-57

When the command is activated, it sets a breakpoint on every function and property of every class in the current document:

ConsoleApplication1 - Microsoft Visual Studio (Administrator)_2011-08-03_20-38-46

 

This addin can be downloaded here: Installer | Source code


Also, note that this addin is language agnostic, meaning that it will work for C#, Visual Basic, and even native C++ applications.

In the rest of the article I'll show the basic steps to create a simple addin for Visual Studio 2010.

 

Creating an addin project

New Project_2011-08-03_20-48-32

Visual Studio makes it easy to create an addin project by providing a template. In the New project dialog,
select Other project types, extensibility, Visual Studio Add-In.
You'll see that a very simple project is created, with the core logic around a class named Connect. This class manages
the lifecycle of the addin through the methods OnConnection, OnDisconnection, etc.
The class field _applicationObject holds a DTE2 object through which we communicate with the environment.

 

Handling events

In this particular case we want to add a command to the Debug menu after a solution is loaded. Therefore, we will need
to wait until a solution is loaded. All the solution events are exposed through the DTE2.Events.SolutionEvents object:

    _solutionEvents = _applicationObject.Events.SolutionEvents;  
    _solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(OnSolutionOpened);
    _solutionEvents.AfterClosing += new _dispSolutionEvents_AfterClosingEventHandler(OnSolutionClosed);

There's a minor caveat here. I'm keeping the reference to the SolutionEvents object in a field of the Connect class.
If I didn't do this, the SolutionEvents object would be deleted by the garbage collector, and the events would never
be raised.

Adding commands

Once that we handle the opening event, we need to add the command to the user interface:

    object[] contextGUIDS = new object[] { };  
    Commands2 commands = (Commands2)_applicationObject.Commands;
    string debugMenuName = "Debug";
    //Place the command on the debug menu.  
    //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:

    Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar =
        ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];
    //Find the Debug command bar on the MenuBar command bar:   
    CommandBarControl debugControl = menuBarCommandBar.Controls[debugMenuName];
    CommandBarPopup debugPopup = (CommandBarPopup)debugControl;
    _command = 
        commands.AddNamedCommand2
        (
            _addInInstance,
            "CommandName",
            "Text to show in the menu",
            "Description of the command",
            true,
            Type.Missing,
            ref contextGUIDS,
            (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled,
            (int)vsCommandStyle.vsCommandStylePictAndText,
            vsCommandControlType.vsCommandControlTypeButton
        );
      
    _command.AddControl(debugPopup.CommandBar, 1);

This code grabs the Debug menu and adds the command with the specified parameters. This code should be wrapped in a
try-catch block to handle cases when the command already exists in the menu.
We can also add a keyboard shortcut to the command in the following way:

    _command.Bindings = "Text Editor::ctrl+d, z"; 

Here "Text Editor" defines the scope of the shortcut. For more information see http://msdn.microsoft.com/en-us/library/envdte.command.bindings.aspx

 

Browsing the code

Visual Studio automatically parses the current document and exposes a nice interface to browse the code. A code document
contains a tree of code elements. Each code element can be a namespace, a class, a method, etc, and it contains a
collection of child code elements in it. The root code elements can be accessed in this way:

    CodeElements elementsInDocument = this._applicationObject.ActiveDocument.ProjectItem.FileCodeModel.CodeElements 

   
To show the browsing algorithm, here's a recursive method that shows how to get all the classes in the current document:
   

    private static void RecursiveClassSearch(CodeElements elements, List<CodeClass> foundClasses) 
    {
        foreach (CodeElement codeElement in elements)
        {
            if (codeElement is CodeClass)
            {
                foundClasses.Add(codeElement as CodeClass);
            }
            RecursiveClassSearch(codeElement.Children, foundClasses);
        }
    }
   

 

Managing breakpoints

Managing breakpoints is very straighforward. The interface exposed through this._applicationObject.Debugger.Breakpoints
is pretty self descriptive, and it contains functionally to add, remove and browse through breakpoints.

 

Installer

Once you finished you addin, the best way to distribute it is to use a Visual Studio Installer project. An addin consists
of only two files: an *.AddIn xml file and a dll. The easiest way to distribute them is to install them in the same
directory, anywhere on the target machine (might be in ProgramFiles), and to add that directory to the addins directories
of Visual Studio. The latter can be done easily with a registry key: In the registry editor window of your installation
project, add a string key at "HKLM\Software\Microsoft\VisualStudio\10.0\AutomationOptions\LookInFolders" with the name
[TARGETDIR] and a descriptive name in the value. The installer will resolve the [TARGETDIR] placeholder at runtime.

 

Download

This addin can be downloaded here: Installer | Source code

 

CodePlex project

Here's the CodePlex site for this project: http://breakall.codeplex.com

 

Conclusions

In this article I presented a useful addin for Visual Studio and I also showed how to create customs addins. For more information on creating addins you can visit the MSDN: http://msdn.microsoft.com/en-us/vstudio/ff677564. I hope you find the addin useful as I do (maybe I'll publish a second version in the future) and I hope to see your great addins soon!

Thanks for reading!
Alfonso Cora


No Comments