Visual Studio 6.0 Extensibility Fundamentals

Kemp Brown
Microsoft Corporation

January 1999

Summary: Discusses extensibility in Microsoft® Visual Studio® version 6.0. (11 printed pages) Describes how to create tools, such as add-ins and wizards, to customize your development environment.

Introduction

Extensibility is the capacity to extend, or stretch, the functionality of the Microsoft Visual Studio integrated development environment (IDE). The IDE provides you with a programming interface known as the Extensibility object model, a set of powerful interfaces for customizing the environment. It allows you to hook into the IDE to create extensions known as add-ins.

There are two types of extensibility: built-in and user-designed.

  1. Built-in extensibility features are already contained in the development environment. They're conveniences such as keyboard rebinding, customizable command bars, docked and tab-linked window configurations that you can name and return to at any time, new templates that you can create for the Template Wizard, and so forth.

  2. User-designed extensibility features are constructed with code. They take two different forms: add-ins and wizards.
    1. Add-ins are time- and labor-saving Automation tools that snap into the IDE. These tools are Microsoft ActiveX® servers that communicate with the IDE through the Extensibility object model and are accessed through the IDTExtensibility2 interface.

    2. Wizards are variations of add-ins that lead a user step-by-step through a series of actions to accomplish a complex or difficult task. Wizards are created using the IDTWizard interface.

The Extensibility object model consists of four related groups of individual code objects that control major facets of the common IDE. Each functional group enables you to create tools to customize the development environment and help you accomplish your programming tasks more quickly and easily. These groups are:

  1. Window manipulation objects

  2. Event objects

  3. Add-in management objects

  4. Project manipulation objects

Each group consists of one or more collections and objects that contribute to a particular purpose. For example, the primary function of the events object group is to provide access to events occurring in the IDE. One object in this group is the CommandBarEvents object, which allows you to respond to events occurring on menus and toolbars. Another is the BuildEvents object, which exposes events occurring at the beginning and end of solution builds.

Creating and Using Add-Ins

You can create add-ins two different ways: with the aid of a wizard or manually.

Using the Add-In Wizard

The best and easiest way to create an add-in (if you're using a common shell language such as Microsoft Visual J++™) is to use a tool called the Add-In Wizard.

To run the Add-In Wizard

  1. Download the Visual J++ Add-In Wizard from http://msdn.microsoft.com/visualj/downloads/samples.asp. The downloaded file includes a Help file informing you where to install the wizard.

  2. Copy the file into your Windows system directory.

  3. Run regsvr32.exe on the DLL.

  4. From the Visual Studio add-ins in the New Project dialog box, select Java Add-In to launch the wizard.

The Add-In Wizard creates a basic add-in framework that you can run immediately after you finish. The wizard lets you supply a display name for the add-in (which appears in the Add-In Manager dialog box) and a description of the add-in. You can optionally choose to have the wizard generate the code to add a command to the Tools menu for invoking the add-in once it is loaded into the IDE. When the wizard is done, you will have a new project with a single item.

Note   If you are using the Visual J++ development system for the Java language, and you don't install it in the default location, you must modify the add-in's project settings to correct the pathname for the alternate launch program, devenv.exe. If you launch devenv.exe as the alternate launch program for your add-in project, the second instance runs in debug mode. This allows you to step through your add-in code in the first IDE instance and watch the effects of the add-in in the second IDE instance.

Manually Creating Add-Ins

Although it's highly recommended that you use the Add-In Wizard to create your add-ins, you can also create an add-in from scratch.

To manually create an add-in in Visual J++

  1. Import the requisite extensibility type libraries msaddndr, office97, and dte:
    import com.ms.vstudio6.dte.*;
    import com.ms.vstudio6.msaddndr.*;
    import com.ms.office97.*;


  1. Implement the IDTExtensiblity2 interface:
    Public Class MyAddin implements IDTExtensibility2

    When you implement the IDTExtensibility2 interface in your code and register the resulting DLL after building the code, the IDE can create an add-in server and call its exposed methods.

    Method name Description
    OnConnection Called when an add-in is connected
    OnDisconnection Called when an add-in is disconnected
    OnAddInsUpdate Called when the Addins collection is updated
    OnStartupComplete Called when the system has completed starting
    OnBeginShutdown Called when the system is being shut down

    Using the Object Browser, you can view the following type libraries to examine objects in the Extensibility object model.

    Type library name File name Description
    Visual Studio 6.0 Extensibility dte.olb The DTE add-in object model
    Microsoft Add-In Designer msaddndr.olb The IDTExtensibility interface
    Microsoft Office 8.0 Object Library office97.olb Command bars
    VJExt vjext.olb Visual J++ project and code models

  2. Create void procedures for the previously mentioned interface methods. For example, create this procedure:
    public void OnConnection(Object VSInst, int ConnectMode, Object 
    AddInInst, SafeArray custom){
    }
    
  3. In the Project Properties dialog box, select the add-in to register it.

    When you select the add-in, code is added to your add-in to identify its class ID, globally unique identifier (GUID), and type library ID. The compiler's preprocessor uses this information in conjunction with a method called @com.register to register your add-in. A registration string looks like this:

    /**
    * @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
    typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000)
    */
    

    Note   These numbers are unique to each add-in.

  4. Once these IDs are added to your code, take note of the name listed in the Project Properties dialog box, such as MyAddin.Connect. Use this name to add a program ID to the newly generated registration string. Using the example in step 4, your code would look like this:
    /**
    * @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
    typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyAddin.Connect")
    */
    

Here's how it looks when it's all put together:

import com.ms.vstudio6.dte.*;
import com.ms.vstudio6.msaddndr.*;
import com.ms.office97.*;

/**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyAddin.Connect")
*/

public class MyAddin implements _IDTExtensibility2 {
   public void OnConnection(Object VSInst, int ConnectMode, Object AddInInst, SafeArray custom) {
   }
   public void OnDisconnection(int RemoveMode, SafeArray custom) {
   }
   public void OnAddInsUpdate(SafeArray custom) {
   }
   public void OnStartupComplete(SafeArray custom) {
   }
   public void OnBeginShutdown(SafeArray custom) {
   }
}

Note that this code serves only as a basic framework for common shell add-ins. From this point, you would add code to place the add-in as a command in a menu or command bar, create unique functionality for the add-in, and so forth.

Registering Add-Ins

Add-ins, whether created manually or with the Add-In Wizard, must be registered with Microsoft Windows®, just as any other ActiveX server. Once registered, the IDE recognizes your add-in and lists it in the Add-In Manager. If you are using Microsoft Visual Basic®, the Add-In Designer prompts you for this information and automatically performs all required add-in registration tasks. If you manually create the add-in in Visual J++, however, you must add a public method named OnCOMRegister to reveal the add-in to the Add-In Manager. (This is done for you if you use the Add-In Wizard.) For example, this code is required:

public static void onCOMRegister (boolean reg) {
   if (reg == true) { //Register
      try {
         RegKey regKey 
            = new RegKey(RegKey.getRootKey(RegKey.USER_ROOT),
            "Software\\Microsoft\\VisualStudio\\6.0\\Addins\\ 
            MyAddin.Connect ", RegKey.KEYOPEN_CREATE);
         regKey.setValue("Description", "Addin description goes 
      here.");    
         regKey.setValue("FriendlyName", "Friendly Add-In Name");
         regKey.setValue("LoadBehavior", 0);
         regKey.close();
      }
      catch (RegKeyException rke) {
         }
   }
   else {    //Unregister
      try {
         RegKey regKey 
            = new RegKey(RegKey.USER_ROOT,
            "Software\\Microsoft\\VisualStudio\\6.0\\Addins",
            RegKey.KEYOPEN_WRITE);
         regKey.deleteSubKey("MyAddin.Connect");
         regKey.close();
      }
      catch (RegKeyException rke) {
      }
   }
}

Alternatively, you can manually create your own .reg file. A typical .reg file for an add-in might look like this:

REGEDIT4
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\6.0\Addins\
   ProjectName.Classname
"Description"="A description of the add-in."
"FriendlyName"="My new addin"
"LoadBehavior"=dword:00000000

For information about creating add-ins in Visual Basic, see "Using Add-In Designer" in the Visual Basic documentation. Also see the sample add-in written in Java in the Visual J++ Samples directory.

Creating and Using Wizards

Wizards are a type of add-in that leads a user through a sequence of actions, generates projects and project items, and then goes away. They implement the IDTWizard interface, which has only a single method, Execute.

Although they can be constructed any way you like, Microsoft implements its wizards in a specific manner. A Microsoft wizard consists of a number of frames. Each frame contains an image in the upper-left corner, a label description to the right of the image that may also contain instructions, and an optional area near the bottom in which other controls can be placed, as in the following example.

The process of creating a wizard is very similar to creating a regular add-in.

How To Create a Wizard

  1. Import the requisite extensibility type libraries msaddndr, office97, and dte:
    import com.ms.office97.*
    import com.ms.vstudio6.msaddndr.*
    import com.ms.vstudio6.dte.*


  1. Implement the IDTWizard interface:
    Public Class MyAddin implements IDTWizard
    

    When you create a wizard, you gain access to the Wizard Extensibility model using an interface called IDTWizard. When you implement the IDTWizard interface in your code and create a .vsz file in one of the appropriate directories, the IDE can create a wizard and call the Execute method.

  2. Create a void procedure for the Execute method. For example, create this procedure:
    public void Execute(int RemoveMode, SafeArray custom)
      {
       }
    
  3. In the Project Properties dialog box, select the wizard to register it.

    When you select the wizard, code is added to your add-in to identify its class ID, globally unique identifier (GUID), and type library ID. The compiler's preprocessor uses this information in conjunction with a method called @com.register to register your wizard. The registration string looks like this:

    /**
    * @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
    typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000)
    */
    

    Note   These numbers are unique to each wizard. Also, you don't need to register your wizard with the IDE with the onCOMRegister procedure as you do with ordinary add-ins.

  4. Once these IDs are added to your code, take note of the name listed in the Project Properties dialog box, such as MyWizard.Connect. Use this name to add a program ID to the newly generated registration string. Using the example in step 4, your code would look like this:
    /**
    * @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
    typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyWizard.Connect")
    */
    

    Here's how it looks when it's all put together:

    import com.ms.office97.*
    import com.ms.vstudio6.msaddndr.*
    import com.ms.vstudio6.dte.*
    
    /**
    * @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
       typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, 
       progid="MyWizard.Connect")
    */
    
    public class MyAddin implements _IDTWizard {
       public void Execute(Object VSInst, int ConnectMode, Object 
          AddInInst, SafeArray custom) {
       }
    }
  1. Next, to expose the wizard in the IDE, create a .vsz file. A .vsz file is a small text file that identifies the wizard, where its executable is located, plus any parameters the wizard requires. Here is a sample of a .vsz file:
    VSWizard 6.0
    Wizard=VIDWizard.CblankSiteWizard OR GUID
    Param=test1
    Param=test2


  1. Place the .vsz file in a directory that contains new files or projects.

    The wizard then appears in the Add Item and Add Project dialog boxes as a new item that can be added to a solution. You can also refer to the .vsz file in a .vsdir file to control how and where it displays in the Add Item and Add Project dialog boxes.

Using Wizards in a Solution

You can place files in the projects and items directories and use them in a Visual Studio solution. To customize which items are displayed in the Add Project and Add Item dialog boxes, as well as the order in which they are displayed, you can use .vsdir files. (Note, however, that .vsdir files are not part of the Automation model.)

The IDE looks for files with a .vsdir extension in the directories that contain new items and projects. .Vsdir files provide a sort order for items displayed in the Add Project and Add Item dialog boxes. There can be multiple .vsdir files at each level in the directories that contain new projects and items. The IDE reads all of them at a given level and builds an overall order for displaying items. Items not listed in .vsdir files appear after all items listed in a .vsdir file. For more information about how to construct .vsdir files, see the section "Creating .Vsdir Files" later in this article.

While regular add-ins cannot be used in the projects and items directories, you can, however, place wizards in these directories and have them activate if they have an associated .vsz file. For information about creating .vsz files, see the next section.

The IDE also provides a Template Wizard that can be used to prompt users for information and merge that information with template sources to produce new project items. Users can add a new template to the Add Item dialog box by creating a new .vsz file that refers to the Template Wizard and the sources for the template. For more information on this, see "Creating Custom Templates" in the Visual Studio documentation.

Creating .Vsz Files

A .vsz file represents items that appear in the Add Item and New Project dialog boxes as new items and projects that can be added to your solution. The .vsz file contains information that identifies a wizard and provides custom information for it. The custom information allows you to invoke the same wizard with different data from different .vsz files. This allows a single wizard to perform seemingly different functions.

The following shows the format of a typical .vsz file:

  VSVersion=6.0
  ClassName=MyProgID.Info
  Param=c:\users\fizzle.java
  Param="another custom parameter"
Part Meaning
VSVersion Identifies the wizard interface the wizard will be expected to implement.
Classname The ProgID of the wizard.
Param Custom parameters that get passed to the wizard upon execution. You can have any number of custom parameters.

Each parameter is contained on a separate line. The quotes shown in the preceding example are optional.


Creating .Vsdir Files

You can optionally create a .vsdir file for your wizard, or modify an existing file to contain information about the wizard. A .vsdir file is a text file that is used to provide information to the Add Item and New Project dialog boxes. The .vsdir file provides information about the items these dialog boxes display, such as the sort order and the icon to display for each item.

You can have multiple .vsdir files in a single directory; however, one .vsdir file is generally used to contain information for multiple wizards, folders, or templates. An example of a typical .vsdir file is shown here:

Form.java|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36201|10|#36202|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#253|0|Form
Control.java|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36203|20|#36204|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#258|0|Control
DATAFORM.VSZ|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36220|40|#36221|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#262|0|DataForm
Form|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36056|10
Class|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36054|20

.Vsdir files contain a single record per file or folder template, with fields separated by a pipe (|) character. Any nonrequired field for which there is no meaningful data should contain a zero (0) as a placeholder. The following fields can be specified for a given record.

Field Meaning
RelPathName Name of the .vsz file for the wizard.
{clsidPackage} Optional GUID representing a product (for example, Visual J++) that has a DLL containing localized resources.
LocalizedName Optional name of the wizard as it will appear in the Add Item dialog box—can be a string or a resource identifier of the form "#ResID."
SortPriority An integer representing the relative priority of the wizard, with 1 being highest. Determines the sort order of items.
Description Localizable description of the template or wizard as it will appear in the Add Item dialog box—can be a string or a resource identifier of the form "#ResID."
DLLPath or {clsidPackage} Used to load an icon for the wizard from a .dll or .exe file. Specify either the full path to the .dll or .exe, or a GUID of a product that has a .dll file containing localized resources.
IconResourceId Optional resource identifier within the .dll file that determines the icon to display. If no icon is defined, the shell substitutes the default icon for a file with the same extension as the item.
Flags A group of flags that disable or enable the Name and Location fields on the Add Item dialog box. The flags can have the following values:

Space (" "): enable file name

8192: disable file name

SuggestedBaseName The default name for the wizard—either a string or a resource identifier of the form "#ResID." If necessary, the shell appends the base value with an integer to make the name unique. For example, the shell might change MyFile.asp to MyFile1.asp.

This default name is shown in the Name field in the dialog box.


Taking Advantage of Extensibility

There are many ways you can use add-ins and wizards to enhance, extend, and automate the IDE:

  1. Manipulate environment-wide properties such as those found in the Options dialog box from the Tools menu.

  2. Navigate the solution and project structure by adding and removing projects and project items, opening project items in one of several views (for example, designer or source), modifying project items, and so on.

  3. Add and remove commands and get events when commands fire.

  4. Access and control all the windows in the environment: close them, dock them, hide them, set the focus, and even create new tool windows that are dockable and behave like built-in tool windows. You can also access the text buffer for source editor windows.

  5. Sink events that occur when solutions are opened and closed, builds begin and end, projects and project items are added or removed, or when the selection changes.

  6. Access the Visual J++ code model from any Visual J++ project's .java item to navigate and manipulate the code in that file. You can also access the code model from the root of the add-in object model to access a particular package, class, or file by name.

  7. With .java files, access objects from the code model object for the package into which the code is compiled, the collection of imports at the top of the file, and a collection of classes in the file.

  8. Add or remove imports.

  9. With class objects, access or modify a collection of interfaces implemented by the class, a collection of members, a collection of superclasses, a collection of subclasses, and the modifiers for the class.

  10. With members, distinguish fields from methods, get type information for fields and signature information for methods, return to the class object, get the initializer expression for a field, access the modifiers, access the statements in methods, and add and remove members, as well as modify these properties.

  11. With statements, query their type (for example, if, for, sync, and so on) and add or remove statements from blocks of code.

  12. For source elements (classes and members), access their JavaDoc comments as an object. This object lets you get the comment as a synopsis, a short description, or the entire text. You can index the object by name for various tags. Some standard tags that are likely to occur repeatedly in a single comment are available also in special collections. For example, you can index @param tags by the name of the parameter to get the text of the tag.

These are just a few ideas. Add-ins give you the freedom to create custom programming solutions to save you time and help you become more productive. Just think of the possibilities!