Professional Visual Studio 2005 (2006) [eng]
.pdf
Chapter 32
Figure 32-1
Using the Add-in Manager, you can also deactivate add-ins that may be performing abnormally or causing the IDE to function improperly even if they were loaded at startup.
You can also bypass all add-in loading when the IDE starts by holding the left Shift key down when starting Visual Studio.
Types of Add-Ins
Although using third-party add-ins is fine, the real power of Visual Studio’s extensibility model lies in the capability it provides for creating your own custom-built additions to the Visual Studio 2005 IDE and command-line processes. When you create an add-in project, two options are available to you: Visual Studio add-ins and shared add-ins.
Visual Studio add-ins harness the additional functionality exposed by Visual Studio 2005’s own extensibility model and are not compatible with other application hosts such as Microsoft Word. This is because in previous versions of Visual Studio, the add-in functionality was partially provided by the Microsoft.Office.Core namespace (particularly access to the command bars such as menus and toolbars). Visual Studio 2005 introduces a new assembly, Microsoft.VisualStudio.CommandBars, which is used instead, and has additional features specific to the Visual Studio IDE.
If you are building add-ins for other applications, you should choose the Shared Add-In project template. When Visual Studio walks you through the associated wizard process, you can choose which application hosts should be able to use your add-in. This list is derived from your own system setup, so make sure you have already installed the application for which you intend to program the add-in.
Visual Studio add-ins can be targeted specifically at the main Visual Studio 2005 IDE or also include the independent Macros IDE.
426
Add-Ins
Creating a Simple Add-In with the Wizard
The best way to illustrate the ease of setting up an Add-In project is to walk through the project wizard creation process. This wizard enables you to include common requirements for add-ins, such as an entry on the Tools menu, and to customize where the add-in can be used.
The following exercise creates an add-in, with Visual Basic as the core language, that performs the ageold Hello World functionality when executed from the Tools menu:
1.To create an add-in, start Visual Studio 2005 and create a new project. Assuming you have the Visual Basic environmental set up, use File New Project; otherwise, it will be File New Project. When the New Project dialog is displayed, navigate to the Other Project Types Extensibility project group and select the Visual Studio Add-in project type. Set the project name and location as you would for any other project and click OK to have Visual Studio launch the Visual Studio Add-in Wizard.
You are presented with a welcome screen explaining what the wizard will do; simply click Next to get to the first interactive screen of the wizard.
2.The first choice you have to make is which programming language to use. For Visual Studio add-ins, you can use either managed code or native C++/ATL code, as shown in Figure 32-2. For this sample, choose “Create an Add-in using Visual Basic” and click Next to continue.
Figure 32-2
Shared add-ins, used for application hosts other than Visual Studio, don’t have as many language options, with only Visual Basic, C#, and C++/ATL available.
3.Once you set the language, the next step is to choose where the add-in will be available. This step enables you to select from either the main Visual Studio 2005 IDE, the Macros IDE, or both environments to host the add-in’s functionality (see Figure 32-3). Click Next after making your selection.
427
Chapter 32
Figure 32-3
4.Step 3 of the wizard enables you to set the name and description for your add-in. These two pieces of text are displayed when configuring the add-in via the Add-in Manager dialog. By default, the name consists of the registered name of the user who installed Visual Studio 2005, along with the project name.
This name will appear in the list of add-ins in the Add-in Manager dialog, while the description text appears in the lower half of the window so users can determine what general functionality your add-in performs.
5.The next step of the wizard is the most useful. If you accept the wizard’s default settings, the project template will only create the most basic routine stubs necessary for registration of your add-in to the hosts you nominated in step 2. However, you can automate the generation of additional code by checking the options on page 4 of the wizard (see Figure 32-4).
The first option creates the code necessary for your add-in to be added to the Tools menu. As you’ll see later in this chapter, you can easily change this to any other menu within the IDE, but the code to actually hook into any menu initially is not necessarily straightforward, and it’s useful to have this initialization code generated for you.
Choosing the second option enables you to have your add-in automatically loaded when the host application is started. Again, the code necessary to have this feature implemented is automatically created for you, leaving you to concentrate on the functionality you wish to implement in the add-in itself.
Finally, you can also indicate whether your add-in will use any modal user interface elements such as modal dialog windows. As explained in the wizard screen, modal dialogs can interfere with unattended application execution or when you’re using Visual Studio from the command line to build solutions.
If you do have modal dialogs, you can always customize the way the add-in is loaded depending on how Visual Studio itself is loaded. The OnConnection method of the add-in includes a parameter, connectMode, that indicates what mode Visual Studio is in. For instance, a connectMode value of ext_ConnectMode.ext_cm_CommandLine indicates that Visual Studio is running in commandline mode, so it does not expect any user interface elements to be loaded.
428
Add-Ins
Figure 32-4
6.The next step of the wizard enables you to specify the inclusion of information for the About dialog box for the add-in. You can specify an extended block of text to direct the users of your add-in to technical support and other information (see Figure 32-5).
Figure 32-5
This information will be displayed in the About dialog of Visual Studio 2005 along with any other installed add-ins and products. The icon is displayed alongside the text in the Product Details pane of the About window when a user selects your add-in. You can exclude your addin from appearing in the About dialog by unchecking the checkbox in this step.
429
Chapter 32
7.Once you’ve entered your details for the About dialog, click Next to view a summary page detailing everything the wizard is going to do in creating your add-in. Review the settings to ensure all of the following: your add-in will be created with the Visual Basic language, it will be registered for both Visual Studio 2005 and Visual Studio 2005 Macros, the Tools menu item will be created, the add-in will be loaded when the host application starts up, and About dialog box information will be included in the add-in file.
When you’re satisfied with the settings, click Finish to have the wizard generate all the associated code and configuration files to implement this functionality.
8.Without doing any customization to the project, run it by pressing F5. Because of the default debug settings, a new instance of the Visual Studio 2005 IDE will be launched with the add-in loaded automatically.
You can ensure that your add-in has loaded by looking for its entry in the Tools menu. The default entry appears at the top of the Tools menu and will have a smiley face icon associated with it (see Figure 32-6).
Figure 32-6
You can also confirm that the About dialog box information was also included by displaying the main About box (Help About) and scrolling the products list until you locate your add-in. Select the entry and review the product details pane to confirm that your information was included.
9.The add-in doesn’t do anything at the moment, so close this extra instance of the Visual Studio IDE and return to the add-in project. Reviewing the files in the Solution Explorer, you can see that the wizard created the standard Connect.vb module containing the main processing of an add-in, as well as two .AddIn files used for deploying the add-in in either debugging or real modes.
430
Add-Ins
You can customize many of the settings that were created for you in the wizard process by editing this file directly. The following listing shows a sample .AddIn file for a typical add-in. Note how multiple valid host applications are defined to the add-in, as well as the informational data for the component, such as the name and description. The LoadBehavior property controls when the add-in will be loaded, with a value of 1 representing load-at-startup functionality:
<?xml version=”1.0” encoding=”UTF-16” standalone=”no”?>
<Extensibility xmlns=”http://schemas.microsoft.com/AutomationExtensibility”> <HostApplication>
<Name>Microsoft Visual Studio Macros</Name> <Version>8.0</Version>
</HostApplication>
<HostApplication>
<Name>Microsoft Visual Studio</Name> <Version>8.0</Version>
</HostApplication>
<Addin>
<FriendlyName>MyAddin1 - Andrew Parsons</FriendlyName>
<Description>MyAddin1 - Testing Add-ins</Description>
<AboutBoxDetails>For more information about GAMEparents, see the GAMEparents website at\r\nhttp://www.gameparents.com\r\nFor customer support, call 1-800-xxx-xxxx.\r\nCopyright (c) 2006 Parsons Designs Pty Ltd.</AboutBoxDetails>
<AboutIconData>0000010006002020100000000000E80200006600000010101000000000
...
FE3F0000FE3F0000FE3F0000FFFF0000</AboutIconData>
<Assembly>MyAddin1.dll</Assembly>
<FullClassName>MyAddin1.Connect</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>
10.Open the Connect.vb code module to add the simple “Hello World” functionality. Whenever a user clicks the menu item that corresponds to your add-in on the Tools menu, it will invoke the Exec method of the add-in, so locate the Exec routine and add a single line to display a message box like so:
Public Sub Exec(ByVal commandName As String, ByVal executeOption As _ vsCommandExecOption, ByRef varIn As Object, ByRef varOut As Object, _ ByRef handled As Boolean) Implements IDTCommandTarget.Exec
handled = False
If executeOption = vsCommandExecOption.vsCommandExecOptionDoDefault Then If commandName = “MyAddin1.Connect.MyAddin1” Then
Msgbox(“hello”) handled = True Exit Sub
End If End If
End Sub
Note how the Exec method works: A commandName is passed in to indicate which command the user is trying to perform. This name will equate to the value you equated with the menu item in the OnConnection startup routine, enabling you to code complex add-ins with multiple commands for your users.
431
Chapter 32
You should always set the handled flag to True once you’ve processed the command so Visual Studio knows the menu item was processed and will stop trying to process it any further.
11.While you’re editing the Connect.vb code, you can also change the icon to display on the Tools menu by modifying the OnConnection routine. Locate the statement that is used to add the item to the menu by searching for the AddNamedCommand2 method. The sixth parameter to the function call specifies the index of the icon to use. By default, the wizard generates a value of 59, which equates to the smiley face icon.
Change this index value to match a more suitable icon for your add-in. For instance, if your add-in were somehow related to table formatting, you might use a value of 203, which displays a small table grid icon. The statement should now appear like the following:
Dim command As Command = commands.AddNamedCommand2(_addInInstance, “MyAddin1”, _ “MyAddin1”, “Executes the command for MyAddin1”, True, 203, Nothing, _ CType(vsCommandStatus.vsCommandStatusSupported, Integer) + _ CType(vsCommandStatus.vsCommandStatusEnabled, Integer), _ vsCommandStyle.vsCommandStylePictAndText, _ vsCommandControlType.vsCommandControlTypeButton)
12.Run the add-in again. This time, the icon in the Tools menu will be the table grid icon, and when you select the command from the menu, a message box is displayed with the words “Hello World.”
Common Classes, Objects, and Methods
The extensibility model that accompanies Visual Studio 2005 is comprehensive, with many classes and methods available to you as you create add-ins. However, you will tend to use some members more often than others. The next few sections provide a summary of these more common members and explain how they can be used to implement the functionality you might require in your add-in.
IDTExtensibility2
The IDTExtensibility2 interface is the core implementation of add-in projects. Every project that is defined as an add-in will have this interface included in its definition. IDTExtensibility2 contains all of the events you can implement in your add-in that Visual Studio will raise. When you create an add-in using the Add-in Wizard, the association with this interface will be automatically included, along with the stubs for all necessary methods, such as OnConnection.
When added to a class in Visual Basic, the IDE will automatically generate the stubs for required methods, so simply adding the Implements statement and pressing Enter will provide you with the routines you must implement. However, for C# projects, you must manually create the subroutines that implement the different required members of the interface (although even here Visual Studio helps you by providing a smart tag action to implement the interface automatically). The included routines are as follows:
OnAddInsUpdate
OnBeginShutdown
OnConnection
432
Add-Ins
OnDisconnection
OnStartupComplete
Of these, only OnConnection is essential to initialize how your add-in will interact with the IDE and the user.
OnConnection is invoked when the hosting application adds the add-in to its collection of components. When invoked, OnConnection includes parameters identifying the application that is hosting the addin along with how the hosting application is connecting to the add-in. This was mentioned earlier for controlling your add-in’s functionality depending on whether Visual Studio was started in UI mode or command-line mode.
You should always keep a local reference to the application object and the instance of the add-in itself in module-level object variables. Again, the Add-in Wizard will generate this code for you automatically to ensure that you have the objects for later program execution. A minimal OnConnection routine would look like this:
Public Sub OnConnection(ByVal application As Object, _
ByVal connectMode As ext_ConnectMode, ByVal addInInst As Object, _ ByRef custom As Array) Implements IDTExtensibility2.OnConnection
_applicationObject = CType(application, DTE2)
_addInInstance = CType(addInInst, AddIn)
End Sub
IDTCommandTarget
If you want to integrate your add-in with the user interface of Visual Studio itself, you’ll need to implement an additional interface called IDTCommandTarget. This interface belongs to the EnvDTE namespace and provides the members you will need to intercept user events and uniquely identify any commands in the add-in.
Once implemented, named commands can then be used throughout the Visual Studio IDE, including assigning keyboard shortcuts and Command Window execution. Most important, named commands can also be added to toolbars and menus.
QueryStatus
The QueryStatus method of IDTCommandTarget retrieves the current status for the specific named command. It should be used to reject any commands unknown to your add-in to avoid improper execution. The function requires the name of the command accompanied by a parameter that indicates what kind of query is to be performed. You should return a status value indicating whether or not the command is supported and whether it’s even enabled (under some circumstances you may want to temporarily disable a particular command while retaining its supported status).
A typical QueryStatus method definition is shown in the following listing. This example uses the neededText property to first determine whether the calling application should retrieve the availability of the specified command:
433
Chapter 32
Public Sub QueryStatus(ByVal commandName As String, _ ByVal neededText As vsCommandStatusTextWanted, _ ByRef status As vsCommandStatus, _
ByRef commandText As Object) Implements IDTCommandTarget.QueryStatus
If neededText = vsCommandStatusTextWanted.vsCommandStatusTextWantedNone Then If commandName = “MyAddin3.Connect.MyAddin3” Then
status = CType(vsCommandStatus.vsCommandStatusEnabled + _ vsCommandStatus.vsCommandStatusSupported, vsCommandStatus)
Else
status = vsCommandStatus.vsCommandStatusUnsupported End If
End If End Sub
Once QueryStatus determines that the command should be analyzed, it checks the commandName parameter value against the known named commands. If it is a match, QueryStatus returns the status value, identifying that the command is supported, along with a hard-coded enabled value as well, which dictates that the command is always available.
This commandName value directly correlates to the name you specify when creating a named command with the AddNamedCommand2 method (shown later in this chapter). The set of status values you can use is as follows:
enabled: The command is enabled and available for use.
invisible: The command is invisible. It is up to the calling application to determine whether the command can still be executed if it is currently hidden.
supported: The command is supported in the current context. You must include this value if the command is supported by your application, regardless of any other status values you return.
unsupported: The command name passed to your add-in is unknown to your add-in code and is not supported. You can also use this value to indicate that the command is not supported in the particular context; for instance, a command might only be supported at design time, so while debugging you could return this value so the command is not processed.
Exec
The Exec method is used to execute the specified command. Once the hosting application determines via the QueryStatus function that a particular command is supported and available to be executed, the Exec command will be invoked with the named command.
You should always set the handled flag to True once you’ve processed a command so Visual Studio will know that the command was successfully executed according to the add-in. You can use the execute Option parameter to customize how the command should be performed, but Visual Studio will always call the method with a value of vsCommandExecOptionDoDefault, which will leave it up to the add-in to decide whether or not to use user interface elements in the processing of the specified command.
Like the QueryStatus function, you should use the commandName parameter to determine which command is being executed. In simple add-ins that only have one known command (such as the sample you walked through earlier in this chapter), it is still advisable to ensure that the Exec method is called with your defined command so you can avoid malfunctioning code.
434
Add-Ins
AddNamedCommand2
To create named commands, use the AddNamedCommand2 method. Any commands made known to Visual Studio via this method will be available even if the add-in itself is not loaded when the hosting application starts, meaning you can still reference the command in keyboard shortcuts and the Command Window.
Once created, a named command can then be assigned to a menu or toolbar. The following code listing is a snippet from code automatically generated after a developer inserts a newly created add-in into the Tools menu:
Dim commands As Commands2 = CType(_applicationObject.Commands, Commands2) Dim toolsMenuName As String
Try
Dim resourceManager As System.Resources.ResourceManager = _
New System.Resources.ResourceManager(“MyAddin3.CommandBar”, _ System.Reflection.Assembly.GetExecutingAssembly())
Dim cultureInfo As System.Globalization.CultureInfo = _
New System.Globalization.CultureInfo(_applicationObject.LocaleID) toolsMenuName = _ resourceManager.GetString(String.Concat(cultureInfo.TwoLetterISOLanguageName, _
“Tools”)) Catch e As Exception
toolsMenuName = “Tools” End Try
Dim commandBars As CommandBars = CType(_applicationObject.CommandBars, CommandBars) Dim menuBarCommandBar As CommandBar = commandBars.Item(“MenuBar”)
Dim toolsControl As CommandBarControl = _ menuBarCommandBar.Controls.Item(toolsMenuName)
Dim toolsPopup As CommandBarPopup = CType(toolsControl, CommandBarPopup) Try
Dim command As Command = commands.AddNamedCommand2(_addInInstance, “MyAddin3”, _ “MyAddin3”, “Executes the command for MyAddin3”, True, 59, Nothing, _ CType(vsCommandStatus.vsCommandStatusSupported, Integer) + _ CType(vsCommandStatus.vsCommandStatusEnabled, Integer), _ vsCommandStyle.vsCommandStylePictAndText, _ vsCommandControlType.vsCommandControlTypeButton)
command.AddControl(toolsPopup.CommandBar, 1) Catch argumentException As System.ArgumentException End Try
The AddNamedCommand2 method is used to create the MyAddin3 named command for the MyAddin3 add-in project. It includes the description of the command and an icon index, and defines the command as being both supported and enabled. The commandStyle parameter dictates how the command should appear by default, while the commandControlType indicates what kind of control should be used for this command’s implementation.
To add this to the Tools menu, the code first uses the Resource Manager to find the current culture string representation of the Tools menu name and then uses the hosting application object’s CommandBars collection to locate the menu bar that corresponds to that name.
435
