Installer provides you with an easy way to reach a large number of potential customers or to manage
your own suite of enterprise applications.
PART XI
customizing and extending
Visual studio
. chaPter 50: The Automation Model
. chaPter 51: Add-Ins
. chaPter 52: Macros
. chaPter 53: Managed Extensibility Framework (MEF)
50 50
The automation Model
what’s in this chaPter?
.
Understanding the Visual Studio extensibility options
.
Working with the Visual Studio automation model
Often you will find yourself performing repetitive tasks when working in Visual Studio,
and wish you could bundle all those tasks into a single automated task, streamlining your
workflow, decreasing your frustration at doing the same thing repeatedly, and consequently
increasing your productivity. Alternatively, perhaps you want to add functionality to
Visual Studio to share with other developers in your company (or even around the world).
Fortunately, Visual Studio has been designed to be very extensible — in fact, many features
that you may have thought were built into Visual Studio are actually extensions themselves!
This extensibility is exposed to make it very easy to add the functionality to Visual Studio
that suits your requirements. Extensibility points include automating tasks, adding new tool
windows, adding features to the code editor, adding your own menu items (including items
to the code editor’s context menu), creating debug visualizers, creating your own wizards,
extending existing dialogs, and even adding your own editors/designers and programming
languages! This chapter looks at the options available for extending Visual Studio, and takes a
look at the automation model used by both macros and add-ins.
Visual studio extensibility oPtions
Unfortunately, the extensibility story in Visual Studio is a bit murky, because a number of
different means exist to extend Visual Studio and it can be hard to determine which method
you should use for what you want to achieve. Here are the various extensibility options
available for Visual Studio, and the context in which it is most appropriate to use each:
1002 .
chaPter 50 The AuTomATion model
.
Macros are the easiest way to automate Visual Studio, and can be thought of as a scripting
language for the IDE. Macros are best suited to quickly automating a task (such as manipulating
the text in the code editor, or automating a repeated set of tasks in the IDE). Macros are rather
limited in their capabilities, and suited only to simple automation tasks. Macros must be written
in VB — no other language is supported. Macros can be shared between developers, but require
sharing macro project files (including their source code). Chapter 52 covers how to develop
macros for Visual Studio 2010.
.
Add-ins are more powerful than macros (despite both working against the Visual Studio
automation model), enabling you to also create tool windows and wizards, and integrate
other features seamlessly within the IDE itself. Add-ins are compiled projects (in your favorite
.NET language or Visual C++), enabling you to ship a binary to other developers rather than
the code itself. Chapter 51 covers how to develop add-ins for Visual Studio 2010.
.
VSPackages are a part of the Visual Studio SDK (a separate download and install), and
provide even more power than add-ins. VSPackages enable you to access the core internal
interfaces in Visual Studio, and thus are ideally suited to integrating your own editors,
designers, and programming languages into Visual Studio. Coverage of VSPackages,
however, is beyond the scope of this book. More information of VSPackages can be found
in the book Professional Visual Studio Extensibility by Keyvan Nayyeri.
.
Managed Extensibility Framework (MEF) component parts enable you to extend the
new WPF-based code editor in Visual Studio 2010 in order to change its appearance and
behavior. If you want to add features to the code editor, this is the best option for your
need. Chapter 53 covers how to develop code editor extensions for Visual Studio 2010.
The next few chapters take you through some of the various ways in which you can extend Visual
Studio, including using add-ins, macros, and the Managed Extensibility Framework (MEF).
However, we continue in this chapter by looking at the core Visual Studio 2010 automation model
that both macros and add-ins rely upon to interact with Visual Studio.
the Visual studio autoMation Model
The Visual Studio automation model, also known as Development Tools Extensibility (abbreviated
as DTE, which you will see used in the automation model), is an object model exposed by Visual
Studio that you can program against to interact with the IDE. This object model allows you to
perform many actions in Visual Studio to achieve a required behavior, handle events raised by
Visual Studio (such as when a command has been activated), and various other functions such as
displaying a custom dockable tool window within the Visual Studio IDE.
This object model is the means by which both add-ins and macros interact with the Visual Studio
IDE, so this section takes a deeper look at its structure and the functionality that it exposes.
an overview of the automation Model
The Visual Studio automation model (DTE) is a COM-based object model that has been added to
with each new version of Visual Studio, over time making it somewhat confusing and messy.
The Visual studio automation Model .
1003
DTE consists of various COM interfaces and their associated implementations covering the facets
of functionality in Visual Studio. Because the concrete classes mostly implement a corresponding
interface, you can expect to see lots of pair classes: an interface and its implementation. For
example, the root object is the DTE class, which implements the _DTE interface.
By their very nature, interfaces don’t support extensibility and should never be changed, because any
change in their structure breaks the structure of any class that implements the original interface. As
Visual Studio matured and new versions were released (each requiring new functionality to be added
to the existing classes in the object model), this created a problem. Microsoft couldn’t update the
existing interfaces or it would cause problems with existing add-ins, so instead it decided to create new
versions of the interfaces with each new Visual Studio version by deriving from the previous version
and adding the new requirements to it. These new interfaces were suffixed with a revision number so
they didn’t have the same name as their predecessor, thus creating the messy and unfriendly model we
have today where multiple interfaces/classes represent the same part of the object model.
For example, you can check out the Debugger, Debugger2, Debugger3, Debugger4, and
Debugger5 interfaces. The Debugger interface was a part of Visual Studio 2003 and was the
original interface. Debugger2 is an updated version of Debugger for Visual Studio 2005, Debugger3
came with Visual Studio 2008, Debugger4 came with Visual Studio 2008 SP1, and Debugger5 came
with Visual Studio 2010. The root DTE interface also has a revision called DTE2, and you will
normally use this rather than its predecessor.
What this means in practical terms is that navigating the object model hierarchy isn’t
straightforward. The model will expose the methods on the classes in the early manifestation of
the model, but you will need to cast the object to a more recent interface to access the functions it
exposes. For example, the first iteration of the Solution object didn’t provide the ability to create a
solution folder — this didn’t come until later where the AddSolutionFolder method was exposed
on the object by the Solution2 interface. So the following macro code will not work:
Public Sub AddSolutionFolder()
DTE.Solution.AddSolutionFolder("TestFolder") ’Will not work
End Sub
but this macro code will:
Public Sub AddSolutionFolder()
Dim solution As Solution2 = DirectCast(DTE.Solution, Solution2)
solution.AddSolutionFolder("TestFolder")
End Sub
As you can see, this makes using the automation model difficult with it commonly necessary to cast
objects to interfaces, also creating somewhat messy code.
Because the underlying automation model is COM-based and we are using managed code to interact
with it, we need to use interop assemblies to provide the bridge between our managed code and the
COM object model. Unfortunately, like the object model itself, these are somewhat messy too. An
additional interop assembly has been added with each version of Visual Studio, so your project will
need to reference each interop assembly, from the base interop assembly up to the one released with
the lowest version of Visual Studio that your add-in or macro will support. For example, add-ins
1004 . chaPter 50 The AuTomATion model
or macros that support only Visual Studio 2010 will need to have references to your project
to EnvDTE.dll (from Visual Studio 2003), EnvDTE80.dll (from Visual Studio 2005), EnvDTE90
.dll (from Visual Studio 2008), EnvDTE90a.dll (from Visual Studio 2008 SP1), and EnvDTE100.dll
(from Visual Studio 2010).
It ’ s worth noting that the Visual Studio SDK is now somewhat taking the place
of the Visual Studio automation model going forward, with fewer new features in
Visual Studio being added to the automation model and more focus and emphasis
being placed on using VSPackages instead (in the Visual Studio SDK). However,
despite its fl aws, the Visual Studio automation model is still very functional and
able to perform most common tasks when integrating with Visual Studio.
Let ’ s now take a look at some of the various functional areas of Visual Studio that the automation
model exposes to us, including solutions and projects, documents and windows, commands,
debuggers, and events. All of these exist under the root DTE object (which should be cast to DTE2 to
expose the more recent revision of this object).
Code examples are macro code, with macros discussed in detail in Chapter 52 .
Most examples output information using the Debug.Print command, which you
can view in the Output window in the Macros IDE.
solutions and Projects
The DTE.Solutions object enables you to automate the currently open solution, such as enumerate the
projects that it contains, create a new project in the solution (or remove a project), add a solution folder,
get/update solution confi guration and properties, get/update its build confi guration, or even open a new
solution in the Visual Studio IDE and work with that. The following code demonstrates enumerating
the projects in a solution, printing the project names and the number of project items in each project
to the Output window:
Public Sub EnumerateProjects()
For Each project As Project In DTE.Solution.Projects
Debug.Print(project.Name & " contains " & _
project.ProjectItems.Count.ToString() & " project items")
Next project
End Sub
Note that you can also enumerate the projects in the active solution using the
DTE.ActiveSolutionProjects collection.
The Visual studio automation Model .
1005
You can also automate the projects in the solution. This includes enumerating the project items in a
project, and the files it contains. You can also get/update the project’s configuration and properties,
and add or remove items from the project:
Public Sub EnumerateProjectsItems()
Dim project As Project = DTE.Solution.Projects.Item(1) ’Get first project
For Each projectItem As ProjectItem In project.ProjectItems
Debug.Print(projectItem.Name)
Next projectItem
End Sub
windows and documents
Windows in Visual Studio are either tool windows (such as the Solution Explorer, Tasks window,
and so on) or document windows (files open in the code editor or a designer). Working with all
types of windows is relatively simple.
You can enumerate through all the open windows and get details of each window as follows:
Public Sub EnumerateOpenWindows()
’This includes both tool windows and document windows
For Each window As Window2 In DTE.Windows
Debug.Print(window.Caption & " | State = " & window.WindowState.ToString())
Next window
End Sub
Next, take a look at how to work with tool windows. Use the following code to get a reference to
a window (whether or not it’s open) and interact with the window itself (activating it, showing it,
hiding it, collapsing it, pinning it, and so on):
Public Sub ShowandDockTaskListWindow()
Dim window As Window2 = DTE.Windows.Item(Constants.vsWindowKindTaskList)
window.Visible = True ’Show it
window.IsFloating = False ’Dock it
window.AutoHides = False ’Pin it
window.Activate()
End Sub
You can get a reference to a specific tool window (such as the Task List), and interact with its
functionality (such as adding tasks to the Task List):
Public Sub AddTaskToTaskList()
Dim tasksWindow As TaskList = DTE.ToolWindows.TaskList
tasksWindow.TaskItems.Add("", "", "Created by a macro")
End Sub
As you can see, working with the tool windows is fairly straightforward. Now look at how to work
with document windows. You can get a reference to the active window in the IDE like so:
Dim window As Window2 = DTE.ActiveWindow
1006 .
chaPter 50 The AuTomATion model
You can even obtain a reference to the Visual Studio IDE window itself to manipulate:
Dim window As Window2 = DTE.MainWindow
It’s possible to automate the document windows in Visual Studio, including opening or closing
a document window, activating it, and getting the project item object opened in the document
window. The following example enumerates through the open document windows, printing the
filename and its path to the output window:
Public Sub EnumerateOpenDocuments()
For Each document As Document In DTE.Documents
Debug.Print(document.Name & ", Path=" & document.Path)