Microsoft Visual Studio .NET offers a powerful development environment right out of the box. However, you can make it even more powerful by creating custom Add- ins to extend its features. This chapter examines how to create and deploy Add-ins for Visual Studio .NET.
During a development project, you might find yourself creating assorted utilities to perform various coding or resource management tasks. Often, these utilities end up as stand-alone applications that require you to leave the Microsoft Visual Studio integrated development environment (IDE) to use them. Add-ins provide a powerful way for you to incorporate custom functionality into the IDE. The Add-ins can even interact with and modify the IDE.
Application #4: Try/Catch/Finally
Application #7: Object-Oriented Features
Visual Studio .NET provides a set of classes that allow you to automate and extend the IDE’s features. The EnvDTE and Extensibility assemblies expose namespaces by the same names that contain the class interfaces necessary for creating Visual Studio .NET Add-ins.
The EnvDTE and Extensibility documentation is part of the Visual Studio documentation, not the .NET Framework SDK.
The IDTExtensibility2 Interface
The IDTExtensibility2 interface defines the methods that allow your Add-in to respond when Visual Studio loads and unloads. The interface exposes five methods: OnConnection, OnDisconnection, OnAddInsUpdate, OnStartupComplete, and OnBeginShutdown. OnConnection fires when the Add-in is loaded, and OnDisconnection fires when it is unloaded. OnAddInsUpdate fires in both cases. OnStartupComplete fires when Visual Studio is done starting up and OnBeginShutdown fires when Visual Studio is closing. The most important of these is OnConnection, as this is where your Add-in is initialized and can modify toolbars and menus to make itself accessible to the user.
The IDTCommandTarget Interface
The IDCommandTarget interface allows your Add-in to receive named commands from Visual Studio. The interface exposes two methods, QueryStatus and Exec. The environment uses QueryStatus to determine what commands are valid on an Add-in. For a simple Add-in, all commands might always be valid, but for more complex Add-ins, you can enable and disable commands as necessary. For example, a particular command might be valid only if text is currently selected in a code window. Visual Studio calls the Exec method in response to a user action and passes in the name of the command to execute. Your Add-in can then perform its work.
The DTE Class
The hosting environment passes in an EnvDTE.DTE instance when it calls your Add- in’s OnConnection method. This reference serves as the entry point to the Visual Studio automation object model. Your Add-in code uses this reference to affect the design-time environment. The DTE class exposes aspects of the environment through properties such as Documents, Commands, Solution, and StatusBar. After learning the two interfaces just discussed, you should focus on learning the DTE object model.
Debugging and Installation
Once you have implemented the necessary interfaces, you’ll start debugging your Add-in. When you run your project, a new instance of Visual Studio will launch and your Add-in will load, depending on the settings you chose in the Add-In Creation wizard. Invoke your Add-in through the commands defined, and debug your code as normal.
The Add-In Creation wizard creates a setup project for installing your Add-in. The installer copies the Add-in’s assembly to the target machine and edits the registry to expose the Add-in to Visual Studio. Anytime you make a change to your Add-in that would affect the registry—such as modifying code that affects the IDE’s command bars—you should rebuild and reinstall the Add-in. This will ensure that the registry is kept up to date with your Add-in.
If your Add-in’s buttons are not appearing as expected, you should close all instances of Visual Studio and run the Add-in’s installer from the file system. This will ensure that all the necessary registry settings are set correctly.
The sample application demonstrates how to use the Add-In Creation wizard and implement the IDTExtensibility2 and IDTCommandTarget interfaces to create a Visual Studio .NET Add-in. The Add-in provides an interface for modifying text selected in the code editor.
The Add-In Wizard
If you want to create your own Add-in from scratch, you can create the starting point for the sample Add-in by creating a new Add-in project. The Visual Studio .NET Add-in type is in the Other Projects/Extensibility Projects folder. On page one of the wizard, select Visual Basic. On page two, uncheck Microsoft VSMacros IDE, as this Add-in will run only in Visual Studio. On page three, enter a user-friendly name and description. This information will appear in the Visual Studio Add-In Manager. On page four, check the check box for creating a Tools menu item. Accept the defaults for the rest of the wizard.
Using the IDTExtensibility2 Interface
The sample application Add-in builds upon the code generated by the Extensibility wizard. Visual Studio passes in a reference to itself in the application parameter of OnConnect. This reference provides the entry point back into Visual Studio that your code uses to modify the environment. The OnConnect method starts by storing this reference in a module-level variable named applicationObject. A reference to the environment’s AddIn object is also stored in the addInInstance variable.
The next line identifies when this Add-in is being launched by comparing the value of the connectMode parameter with some values from the Extensibility.ext_ConnectMode enumeration. You can use this comparison, for example, to determine whether the Add-in is being launched through the command line.
The Add-in next deletes any existing commands that the environment might have installed for it by looping through the applicationObject Commands collection. Call the GetEnumerator method to retrieve this collections enumerator.
DimcollAsIEnumerator=_ applicationObject.Commands.GetEnumerator() DoWhilecoll.MoveNext CommandObj=CType(coll.Current,Command) IfCommandObj.Name=_ "101Utilities.Connect.ChangeCase"Then CommandObj.Delete() EndIf Loop
Adding a command to the environment is a two-step process. First you define a Command object using the AddNamedCommand method of the EnvDTE Commands property. This method requires the name of the command, a reference to the AddIn to execute the command on, and numerous display options such as button text, ToolTips, and image. The last parameter determines whether the command will be enabled by default when the Add-in is loaded. This Add-in specifies that the command should be disabled by default.
CommandObj=applicationObject.Commands.AddNamedCommand(objAddIn,_ "ChangeCase","ChangeCase",_ "Executesthecommandfor101Utilities",True,59,Nothing,_ vsCommandStatus.vsCommandStatusUnsupported)
The second step is to add a control to the interface for the newly created command. The following code adds a control to the Tools menu of the IDE:
Using the IDTCommandTarget Interface
Visual Studio passes in the name of a command to the QueryStatus method of the IDTCommandTarget interface. Your code should evaluate whether that command is valid at that time and assign an appropriate value from the EnvDTE.vsCommandStatus enumeration. The sample Add-in’s 101Utilities.Connect.ChangeCase command is valid only if text is selected in the editor. First, try to get a TextSelection reference.
DimtsSelectedTextAsTextSelection Try tsSelectedText=CType(applicationObject.ActiveDocument.Selectio n,_ TextSelection) Catch statusOption=vsCommandStatus.vsCommandStatusUnsupported EndTry
Now that we have a TextSelection, check to make sure there is text in it, as it’s possible to have a TextSelection with a length of zero. If there is text selected, enable the command by indicating that it’s both supported and enabled.
IftsSelectedText.Text.Length>0Then statusOption=CType(vsCommandStatus.vsCommandStatusEnabled+_ vsCommandStatus.vsCommandStatusSupported,vsCommandStatus) EndIf
The environment calls the Exec method to invoke a command on an Add-in. This method retrieves the currently selected text and passes it to an instance of the MainForm dialog.
DimtsSelectedTextAsTextSelection Try tsSelectedText=CType(applicationObject.ActiveDocument.Selectio n,_ TextSelection) CatchxAsException MessageBox.Show("Youmusthaveselectedtexttoperformthisact ion.") ExitSub EndTry DimfrmMainAsNewMainForm() frmMain.StringValue=tsSelectedText.Text
The MainForm form provides a visual interface for a user to convert the supplied text to an uppercase or lowercase format. If the user clicks OK, the edited text is supplied to the TextSelection in the IDE.
IffrmMain.ShowDialog()=DialogResult.OKThen tsSelectedText.Text=frmMain.StringValue EndIf
Visual Studio .NET provides a rich extensibility framework and automation API. You can use these features to create powerful extensions to the IDE that are easily distributable with the included setup project.