Visual Studio .NET provides an incredible leap forward from its predecessor in terms of functionality, but eventually, every developer finds a sought-after feature that just seems overlooked.
VS .NET provides great features and capabilities, from intelligent wizards to very useful drag-and-drop functionality such as that provided by the Server Explorer tool window. If there's a problem, it could be one of too much success. A simple stroll through the IDE reveals feature after productivity-inducing feature, providing the developer with a high set of expectations. Eventually, you may find that an expected feature is missing. Fortunately, Microsoft included a very powerful extensibility model, allowing integration of new, custom features directly into the IDE.
Microsoft has provided extensibility and automation object models for most of their products for as long as one can remember. For example, the automation model exposed by Microsoft Office provides the foundation of the entire VBA community. When you work within the world of the developer, the strength and power of the developer's tools will dictate productivity. The ceiling on productivity achievable by a developer bears a high-correlation with the number and usefulness of the features provided by the IDE. If you make the IDE better, developers will rejoice. Herein lays a major problem. For all the R&D money and general efforts placed into the design and development of it's products, Microsoft understands that it can never anticipate all the features the entire developer community might need or want. If they attempted to achieve that goal, the natural result would invariably be to occasionally actually ship an IDE?maybe once a decade or so.
Compiling add-ins into assemblies highlights a fundamental difference: macros run out-of-process with the IDE, while a DLL containing an add-in class achieves better performance in running within the IDE.
This makes me think of the analogy of the fisherman. Give a man a fish, and he will eat for a day. Teach him to fish, and he will eat for a lifetime. In similar fashion, if you trade fish for features, the moral of the story stands. It's better to give the developer the tools to create new, custom features, than to try and anticipate all the features that developers need. This is where the extensibility model of Visual Studio .NET, found in the EnvDTE and Extensibility namespaces, plays a role. Using these namespaces as a primary toolkit, the power developer will create extended capability and features, in the form of add-ins, that evolve the IDE into the customized power tool that can address all of the developer's needs. In essence, if the developer plays the role of the fisherman, than the extensibility model is the net in .NET.
A Brief Comparison to Macros
Before jumping into the guts of what an add-in entails, or how the architecture of the IDE interacts with these special classes, let me explain the relationship that macros and add-ins have to one another. Macros play a special role by providing the developer with a quick and simple mechanism for defining custom behavior and functionality. Macros allow the development of custom functionality, but carry significant limits in terms of deployment, performance, and functionality constraints. Both fundamentally use the same extensibility API, EnvDTE, to perform all of their fancy footwork. Similarities tend to end there. For example, the language used to write macros within the IDE is limited to Visual Basic .NET. Since macros tend to exist to perform simpler, more straight-forward tasks of an automation nature within the IDE, using Visual Basic .NET as the macro language provides a more natural fit. This approach mirrors the “Great for simplicity” strategy, but consequently falls short of providing an ideal situation for the predominantly C# developer.
VS .NET saves macros in files with a .vsmacros extension, while add-ins are .NET classes compiled into DLL assemblies. Compiling add-ins into assemblies highlights a fundamental difference: macros run out-of-process with the IDE, while a DLL containing an add-in class achieves better performance by running within the IDE. Macros run within a vsmsvr.exe process, in comparison to the IDE, or devenv.exe. You can see this in Figure 1 where I recorded and then ran a macro while I had the Task Manager open. Compiling add-ins into assemblies also provides a layer of intellectual property protection (security) that is not afforded to macros, which are essentially distributed along with their source code.
Macros and add-ins also leverage completely separate design environments. You write macros within their own macros IDE, or vsaenv.exe. Macro development most commonly begins by initiating a macro recording session. You can tweak the resulting code auto-generated by the macro recorder into what best suites your current needs. Contrasting this with add-in development, the Add-In wizard will be the normal starting point for most developers developing an Add-In. When you select the Visual Studio .NET Add-In project type shown in Figure 3 to add a new project to a solution, the Add-In wizard will be invoked.
While the Add-In wizard provides an adequate starting point, it basically creates a minimal shell?it doesn't generate any significant implementation code. Additionally, while you might write macros completely from scratch, without aid of the macro recorder, it is rare that you would create an add-in without using the Add-In wizard as a starting point. Although this is the case, there is no technical reason why you must use the Add-In wizard. Later I'll discuss the tasks performed for you by the Add-In wizard in more detail, but first let's explore the basic architecture of a Visual Studio .NET add-in.
Basic Add-In Architecture
The team responsible for designing the extensibility API in Visual Studio .NET stipulated few requirements for a .NET class to have the ability to function as an add-in. The Visual Studio .NET Add-In Project type provides a wizard to generate a great deal of basic coding elements that have the tendency to hide the bare essence of what is actually required. Later I'll show you the results of using this Project type as a starting point for your add-in, but for now, let's stick just to the bare facts.
Common Requirements
The immediate requirement is that you should with a Class Library project. You should add a single class to this project. You will need to reference three primary assemblies that provide interfaces and UI elements used by most add-ins. With the Add Reference wizard, you should add references for:
- extensibility.dll
- envdte.dll
- office.dll
These assemblies will provide the core interfaces you will need to implement and the access to the primary interop assembly for the Office menu controls, allowing use by your add-in of the core set of UI controls used by Visual Studio .NET for the command bars, context menus, and menu items.
Visual Studio .NET requires an add-in compiled as a .NET assembly to be registered as a COM object.
Most add-in projects will greatly benefit from the use of a core set of imports statements at the top of the code file.
Imports Microsoft.Office.Core
imports Extensibility
imports System.Runtime.InteropServices
Imports EnvDTE
Meeting the Three Primary Requirements
The public assembly extensibility.dll, shown in Figure 4, contains a single element, the IDTExtensibility2 interface. In order to write a class to function as an add-in, you should first start by implementing IDTExtensibility2 and its five methods: OnConnection, OnDisconnection, OnAddInsUpdate, OnStartupComplete, and OnBeginShutdown. As far as coding goes, this is the only requirement. Two more requirements exist in order to allow Visual Studio .NET to be aware of and load your type as an add-in.
The most surprising requirement for a .NET class to function as an add-in seems to be a contradiction to everything that is managed in nature. Visual Studio .NET still contains a great deal of functionality that is based on unmanaged code. This extends to the ability to load .NET add-ins. Visual Studio .NET requires an add-in compiled as a .NET assembly to be registered as a COM object. The IDE expects to instantiate a COM object for each add-in. Since .NET provides a very robust COM interoperability set of technologies built-in, you will need to do nothing beyond ensuring that you've registered your .NET class in the registry as a COM object.
You can create the required registry keys needed for a .NET class to register as a COM class by using the .NET Framework SDK utility regasm.exe.
Regasm /codebase SnippetLoader.dll
The Regasm utility will ensure the correct registry entries exist. Using Regasm is a manual process and not suitable for all situations. The most common and best way to ensure this is accomplished is to create a new Class Library project and set the project properties to register for COM interoperability.
Visual Studio .NET is made aware of add-ins by the presence of a registry entry similar to the following: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\AddIns<ProgID*>.* The ProgID used in this registry key name is the value used when the .NET class was registered for COM interoperability.
The team responsible for designing the extensibility API in Visual Studio .NET stipulated few requirements for a .NET class to have the ability to function as an add-in.
You should place several values (shown in Figure 5) in this registry entry to control the behavior of an add-in. Placing this key in HKEY_LOCAL_MACHINE classifies the add-in as an administrator add-in. To make an add-in available to only the current user as a user add-in, HKEY_LOCAL_MACHINE should be replaced with HKEY_CURRENT_USER. Table 1 lists the optional name/value pairs that you can use in this registry entry.
You need to understand some of the idiosyncrasies of the values you can assign to these named-value pairs. Failing to provide a FriendlyName named-value, Visual Studio .NET will substitute the <ProgID> used for the .NET class for COM Interoperability. Two values, AboutBoxDetails and AboutBoxIcon, are required in order to receive recognition in the Installed Products listing on the About dialog box for Visual Studio .NET shown in Figure 6. If you didn't provide a value for FriendlyName, .NET will use the <ProgId> used for COM interoperability as the FriendlyName in both the Installed Products list shown on the About dialog and in the Add-in Manager dialog box.
Understanding the Preload Process
The behavior involved in preloading add-ins will not seem intuitive at first. If you follow the SDK documentation regarding the use and behavior of CommandPreload, you will likely be left scratching your head. Two key registry entries work together to provide both Administrator and User add-ins to accomplish a single preload event for any add-in requiring UI or general add-in initialization upon first load.
The entire decision logic tree followed by Visual Studio .NET is shown in Figure 7. The preload logic requires the use of the user-specific registry key PreloadAddinState. The path for this key is HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\7.1\AddIns. Within this key you should place DWORD values with names matching the ProgID of your add-in. The value will be used as a flag to track whether an add-in requiring preloading has been initialized.
If you create an administrator add-in available to all users and registered in HKEY_LOCAL_MACHINE, the value of CommandPreload should generally be set to 0x1. Setting this value to 0x0 has the effect of doing nothing and results in the same behavior as if the named-value was completely missing. For an administrator, Visual Studio .NET will never change this value. Changing to 0x0 or 0x2, from 0x0, would result in future users loading the IDE failing to have the add-in initialized for them. This would not be good. Instead, the state of preloading for each user is tracked in the PreloadAddinState key.
Visual Studio .NET will treat add-ins installed as user add-ins differently. It will be made aware of the available add-in directly by a dedicated subkey in the \Addins key under the HKEY_CURRENT_USER tree. For user add-ins, the state of previous preloading will be tracked by changing the value of CommandPreload directly from 0x1 to 0x2.
Useful Attributes to Use
COM interoperability plays a big role in the IDE's add-in architecture. Instead of relying on the ProgID and GUID chosen for you by the IDE, you should use a few attributes to take control of that process. Apply the ProgIdAttribute and GuidAttribute classes to your add-in type to set the values used.
<GuidAttribute("3D2A136E-...-5A1B22C90627"), _
ProgIdAttribute("FromScratch.MyAddin")> _
Public Class MyAddIn
Implements Extensibility.IDTExtensibility2
The ProgIdAttribute value you supply will naturally be used for the custom subkey under \Addins and, in the case of a user add-in, the name of the DWORD value added automatically by Visual Studio .NET under the PreloadAddinState key in the HKEY_CURRENT_USER tree. You should also leverage the GuidAttribute to maintain better control over the GUID used when registering your .NET class as a COM class.
Remember to change the \AddIns subkey for your add-in any time you modify the ProgIDAttribute that you apply. Changing this attribute and solely rebuilding will not automatically change the name of the subkey you created to notify Visual Studio .NET of the existence of your add-in.
Breaking Down the Interfaces
Writing add-ins for Visual Studio .NET will involve two interfaces: IDTExtensibility2, contained with the Extensibility namespace provided by Extensibility.dll, and IDTCommandTarget, contained with the EnvDTE namespace provided by EnvDTE.dll. The members exposed by these interfaces will expose your add-in's primary communication channel with the IDE during setup, and if you are declaring named commands, during runtime upon invocation of your named commands.
Providing Core IDE Communication
The IDTExtensibility2 interface implemented by an add-in is the same interface you used to write COM add-ins for Office XP. For some readers this will provide a welcome feeling of familiarity. You use this interface to provide a communication mechanism between the IDE and your code. This interface contains five method members, with two primary methods used to signal the loading and unloading of your add-in code.
You'll start this communication with the OnConnection method. Within this method, Visual Studio .NET will provide an object reference to the IDE itself. You should cast the initial object parameter passed into the method into the core type provided by the EnvDTE namespace, DTE.
Dim IDE As EnvDTE.DTE
Public Sub OnConnection( _
ByVal Application As Object, _
ByVal ConnectMode As ext_ConnectMode, _
ByVal AddInInst As Object, _
ByRef custom As System.Array) _
Implements IDTExtensibility2.OnConnection
IDE = CType(Application, EnvDTE.DTE)
The primary actions performed by most add-ins involve access and manipulation of objects contained within the EnvDTE namespace. The instance of the DTE object passed into OnConnection will provide you this ability. The second parameter will provide instruction to your add-in regarding the reason the add-in was loaded. The second parameter uses the enumeration type ext_ConnectMode found in the Extensibility namespace. You should use either an If..Then or Select statement to provide behavior appropriate for each add-in loading scenario. For a simple add-in, you will most likely limit the code in OnConnection to setting up your initial UI. The third parameter provides an object reference to your add-in's own object instance, of type EnvDTE.AddIn, and the final parameter will generally be of limited value to you. Currently, the IDE passes an empty array for the final parameter of the OnConnection method.
The second most important method is the OnDisconnection method. This method indicates the end of an add-in's life. The add-in architecture in Visual Studio .NET will result in the add-in class being loaded at least twice. The removeMode parameter uses the enumeration type ext_DisconnectMode found in the Extensibility namespace. You should use the removeMode parameter to respond appropriately for the scenario in which your add-in was unloaded. You should use this method for any required clean up proceedings.
The final three methods of the IDTExtensibility2 interface will normally be of limited use. Use OnStartupComplete when your initialization code, normally placed in the OnConnection method, needs to access IDE components. The IDE automation model objects you access, found in the EnvDTE namespace, may not yet be available; therefore you should place initialization code in OnStartupComplete to compensate for IDE component dependencies in your add-in initialization routines.
The OnAddInsUpdate method will interrupt your add-in to inform your code that another add-in has either been loaded or unloaded. Use this method to respond to a changing add-in landscape. Do not get your hopes up about the direct usability of this method to adjust to add-in dependency scenarios; only one parameter is provided and this parameter provides no information as to the source or nature of the change in add-in landscape.
The OnBeginShutdown is called only if Visual Studio .NET attempts to shutdown while an add-in is running. Shutdown at this point is irreversible and this method should be used only to clean up items prior to the inevitable IDE shutdown.
Implementing Named Commands
Use the IDTCommandTarget interface only if you intend to provide one or more custom named commands. A named command with Visual Studio .NET is merely a piece of functional code that is assigned a well-known name within the context of the entire collection of commands (or macros) programmatically accessible, or also invoked directly through the IDE's Command window. Almost every action a developer can initiate within the IDE is made available in the form of a named command. To see this in action, open the Command window and type View.FullScreen. This will change your IDE configuration to the Full Screen mode.
The benefit of exposing add-in functionality as a named command will be two-fold. As a convenience, or for programmatic accessibility of your add-in's functionality, a named command is the standard mechanism the developer will use. The second benefit will stem from the ease of adding menu items to context menus or command bars through use of the AddControl method of the EnvDTE.Command object.
Dim oAI As AddIn = CType(addInInst, AddIn)
Dim oCmd As Command
Try
oCmd = IDE.Commands.AddNamedCommand( _
oAI, "CmdName", "ButtonText", _
"Tooltip", True, 59, Nothing, _
1 + 2)
oCmd.AddControl( _
IDE.CommandBars.Item("Tools"))
Catch e As System.Exception
End Try
The IDTCommandTarget interface exposes two method members, Exec and QueryStatus, which you will implement. You will need to implement both so that you can respond properly to the IDE when Visual Studio .NET invokes your named commands. The QueryStatus method implementation provided by your add-in will be called in three scenarios:
- Your named command is manually entered into the Command window
- The ExecuteCommand method of the core EnvDTE.DTE object is used to request your named command
- The IDE needs to draw a menu item associated with your named command through the original call to AddControl.
You should assign a value from the vsCommandStatus enumeration to the statusOption parameter being passed by reference. Your value should indicate the command is both available (vsCommandStatusEnabled) and supported (vsCommandStatusSupported). This will instruct the IDE to invoke the Exec method, providing your add-in the chance to handle the command request.
StatusOption = CType( _
vsCommandStatus.vsCommandStatusEnabled + _
vsCommandStatus.vsCommandStatusSupported, _
vsCommandStatus)
You should place the necessary named command implementation code within the Exec method. You need a logic check to guarantee that the proper code is executed for the command being requested.
If cmdName = "FromScratch.MyAddIn.CmdName" Then
handled = True
Exit Sub
End If
The actual name of your command will be a combination of the ProgID set with the ProgIdAttribute class applied to your add-in class and the second parameter passed into the AddNamedCommand.
Using the Add-in Wizard
Developing your first add-in from scratch will be a very useful exercise. Visual Studio .NET provides a Visual Studio .NET Add-In Project type shown in Figure 3. This project type will invoke a wizard that will ask you several common questions about the nature of the add-in you are about to develop. The questions are basic and will be used to customize the most common elements an add-in will need. The nicest thing about this wizard is that it will also create a Visual Studio .NET Setup Project that includes all the necessary registry entries shown in Figure 5.
Several pages of the wizard will collect simple data points that map directly to assorted registry entries. Page 1 of this wizard will ask which language you need to use, impacting the final output of this wizard. Page 2 will ask if your add-in should be available to Visual Studio .NET and/or the Macros IDE. Your answer on this page will impact only the registry path the \AddIns subkey modifications will impact. You should modify these settings post-wizard by changing the registry settings the generated Setup Project will add to the target system upon installation. Page 3 will ask for a name and description for your add-in. Visual Studio will use these values to populate the FriendlyName and Description values for your add-in. Page 5 will provide you an option to include an About box information. Choosing yes will result in AboutBoxIcon and AboutBoxDetails values being included in your add-in's registry subkey.
The only slightly interesting options will be presented on Page 4 of the Add-In wizard shown in Figure 8. You will be given four options on Page 4. Check the option, “Yes, create a Tool's menu item,” if you want the wizard to provide base implementation code for the IDTCommandTarget interface and have a named command declared by your add-in by code placed in the IDTExtensibility2.OnConnection method. You should check this option, as it will provide you a good starting point that you can tweak to modify command bars other than “Tools.”
Check “My Add-in will never put up a modal UI….” if you want to add the CommandLineSafe value to the registry settings for your add-in. The option, “I would like my add-in to load when the host application starts,” maps directly to the value used for the registry value LoadBehavior. The final option on Page 4, under the section called “Setting up access privileges,” will determine whether the registry entries for your add-in will be placed under the HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER trees.
The wizard sets up the most common and simplest of elements for your add-in, but the result of the add-in provides you with the bare essence of a starting point.
The implementation code generated by the wizard will go to extremes to check on conditions, settings, and enumeration values. Many of these checks are unnecessary, but no harm will result if you leave them unchanged. In general, you should leave them alone.
Summary
The extensibility model provided by Visual Studio .NET provides a powerful way to build custom value-add features that can integrate seamlessly into the IDE. You can develop an add-in class by hand, with little effort, but you will need to develop a little hands-on experience to learn the idiosyncrasies of add-in architecture. A few elements of the architecture work differently in Visual Studio .NET 2002 and do not coincide with the documentation. Most of these quirks were corrected in Visual Studio .NET 2003. Independent of the minor quirks you will find here and there, the model is accessible, usable, powerful, and opens the door to development of that crucial piece of functionality that lands at the top of your wish list, but currently does not exist “out-of-the-box.”
This article covered the purpose, usage, and members of the IDTExtensibility2 interface comprising the sole contents of the extensibility.dll assembly. The primary methods OnConnection and OnDisconnection were covered. The concept of named commands was covered, including the usage and implementation details of the IDTCommandTarget interface, provided by the core IDE extensibility namespace EnvDTE contained within the EnvDTE.dll assembly.
In the space afforded a magazine article, you can barely scratch the surface of what you can code an add-in to accomplish. The EnvDTE automation object model for Visual Studio .NET provides over 140 different objects for working with and manipulating the IDE and close to anything within it. I have mentioned many of the common changes an add-in might create to impact the IDE, such as multi-level menu item hierarchies, modifying the UI without using named commands, interaction directly with project files or individual tool windows, or even the ability to hook IDE events and respond to user interactions with the IDE itself. All together, an enormous world of IDE customization awaits you. In the process of deconstructing the basic add-in architecture, you have been handed the keys….time to take the IDE out for that 200 mph test drive all developers deserve.
Michael Lane Thomas - The .NET Cowboy
Listing 1: Sample add-in with minimized code listings that defines and handles a single named command
Imports EnvDTE
Imports Microsoft.Office.Core
Imports System.Runtime.InteropServices
Imports System.Reflection
Imports Extensibility
<GuidAttribute("3D2A136E-AE04-49d0-80B1-5A1B22C90627"), _
ProgIdAttribute("MLT.SnippetLoader")> _
Public Class MyAddIn
Implements IDTExtensibility2, IDTCommandTarget
Dim IDE As EnvDTE.DTE
Dim oAI As EnvDTE.AddIn
Dim oCmd As Command
Shared Snippets As Hashtable
Public Sub OnConnection(ByVal Application As Object, _
ByVal ConnectMode As ext_ConnectMode, _
ByVal AddInInst As Object, _
ByRef custom As System.Array) _
Implements IDTExtensibility2.OnConnection
IDE = CType(Application, EnvDTE.DTE)
If ConnectMode = ext_ConnectMode.ext_cm_UISetup Then
oAI = CType(AddInInst, AddIn)
Try
oCmd = IDE.Commands.AddNamedCommand( _
oAI, "Toggle", "Toggle Snippets", _
"Toggles loading of Custom Snippets", _
True, 59, Nothing, 1 + 2)
oCmd.AddControl(IDE.CommandBars.Item("Tools"))
CacheSnippets()
Catch e As System.Exception
End Try
End If
End Sub
Private Sub CacheSnippets()
Snippets = New Hashtable
Dim cfgFile As String
cfgFile = [Assembly].GetExecutingAssembly.Location + _
".tabs"
Try
If System.IO.File.Exists(cfgFile) Then
Dim tabs As New Xml.XmlDocument
tabs.Load(cfgFile)
Dim snip As Xml.XmlNode
For Each snip In _
tabs.SelectNodes("/snippets/snippet")
Snippets.Add(snip.Attributes("name").Value, _
snip.InnerText)
Next
End If
Catch ex As Exception
End Try
End Sub
Public Sub OnDisconnection( _
ByVal RemoveMode As ext_DisconnectMode, _
ByRef custom As System.Array) _
Implements IDTExtensibility2.OnDisconnection
End Sub
Public Sub OnStartupComplete(ByRef custom As System.Array) _
Implements IDTExtensibility2.OnStartupComplete
End Sub
Public Sub OnAddInsUpdate(ByRef custom As System.Array) _
Implements IDTExtensibility2.OnAddInsUpdate
End Sub
Public Sub OnBeginShutdown(ByRef custom As System.Array) _
Implements IDTExtensibility2.OnBeginShutdown
End Sub
Public Sub Exec(ByVal CmdName As String, _
ByVal ExecuteOption As vsCommandExecOption, _
ByRef VariantIn As Object, _
ByRef VariantOut As Object, _
ByRef handled As Boolean) _
Implements IDTCommandTarget.Exec
Dim wTbx As Window = _
IDE.Windows.Item(Constants.vsWindowKindToolbox)
Dim oTbx As ToolBox = wTbx.Object
Dim tab, myTab As ToolBoxTab
For Each tab In oTbx.ToolBoxTabs
If tab.Name = "SnippetLoader" Then
myTab = oTbx.ToolBoxTabs.Item("SnippetLoader")
Exit For
End If
Next
Dim snip As Object
If myTab Is Nothing Then
myTab = oTbx.ToolBoxTabs.Add("SnippetLoader")
For Each snip In Snippets.Keys
myTab.ToolBoxItems.Add( _
snip.ToString, _
Snippets(snip).ToString, _
vsToolBoxItemFormat.vsToolBoxItemFormatText)
Next
Else
myTab.Delete()
End If
handled = True
End Sub
Public Sub QueryStatus( _
ByVal CmdName As String, _
ByVal NeededText As vsCommandStatusTextWanted, _
ByRef StatusOption As vsCommandStatus, _
ByRef CommandText As Object) _
Implements IDTCommandTarget.QueryStatus
StatusOption = _
CType(vsCommandStatus.vsCommandStatusEnabled + _
vsCommandStatus.vsCommandStatusSupported, _
vsCommandStatus)
End Sub
End Class
Listing 2: Sample XML configuration file (.tabs) used by add-in
<?xml version="1.0" encoding="utf-8" ?>
<snippets>
<snippet name="Copyright">
' Michael Lane Thomas Copyright 2003
</snippet>
<snippet name=".NET Cowboy URL">
' <a href="http://blogs.gotdotnet.com/mlthomas">http://blogs.gotdotnet.com/mlthomas</a>
</snippet>
</snippets>
Table 1: Optional name/value pairs used in the add-in registry key.
Registry Values | Description |
---|---|
AboutBoxDetails | A description string displayed in the Visual Studio .NET About dialog box. |
AboutBoxIcon | A flexibility format value used as the Icon data. This named-value must be present, but can be essentially blank. Allowed formats for the icon data include:String presenting the path to an icon file.A comma-separated string of a path to an executable file and the resource ID of an icon file in the executable file.A string file representing the resource ID of an icon found in the add-ins satellite DLL. Binary data representing the icon file. |
CommandLineSafe | An optional value for informational purposes only that indicates whether the add-in was designed to be safe for command-line usage. |
CommandPreload | A type DWORD bit field. Value is optional. Used to direct Visual Studio .NET to attempt a preload of an add-in for the purpose of setting up initial UI or configuration elements needed by an add-in. Possible values include:0x0 ? No preloading of add-in needed. Same impact as if value did not exist in registry.0x1 ? If a user add-in (HKEY_CURRENT_USER), this indicates need to preload add-in once to allow UI or add-in initialization. If administrative add-in (HKEY_LOCAL_MACHINE), this is the standard value used.0x2 ? Indicates a user add-in (HKEY_CURRENT_USER), that has been preloaded once. |
Description | The description string displayed at the bottom of the Add-Ins Manager when the add-in is selected. Value is optional. If not provided, no description will be available in the Add-Ins Manager. |
FriendlyName | The name that will appear in the Add-Ins Manager list of available add-ins. Value is optional. |
LoadBehavior | A type DWORD bit field. Value is optional, but failing to provide a value will result in the name/value pair being added with a value of 0 upon first enumeration of available add-ins by Visual Studio .NET on next load. Possible values include the following:0x0 ? IDE is not loaded, and will not be loaded.0x1 ? Add-in should be loaded whenever the IDE is loaded.0x2 ? Add-in is currently loaded.0x4 ? Add-in should load when the IDE is started via command-line with a build switch being used. |
SatelliteDllPath | Used to specify the path, ending in a backslash (\), for a satellite DLL to be used when localizing the FriendlyName or Description values. |
SatelliteDllName | Specifies the name of the satellite DLL found at the location specified in the SatelliteDllPath value. |