Microsoft & .NET.NETExtending Visual Studio 2005

Extending Visual Studio 2005

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Visual Studio 2005 Integrated Development Environment (IDE) has an abundance of features. These include support for all of your favorite .Net languages, out—of—the box solutions (i.e., web sites, windows forms, mobile applications, etc.), built in refactoring and code snippet support, Intellisense, a sophisticated compiler, among other elements.

What really sets Visual Studio 2005 apart from its predecessors (and its competitors) is the advanced extensibility options. As a developer, you can build macros to bind to key stroke combinations or toolbar buttons, develop Add—ins to enable the addition of new features, or build VS Packages through the Visual Studio Industry Partner Program to enable the integration of entire products or advance customizations into the IDE.

Although the primary focus of this article will be the fundamentals of the VS Package, the other three extensibility levels will also be discussed. Additionally, I will walk you through the development of a managed Visual Studio 2005 Package, explore some of the Managed Package Framework (MPF), and take a look at the Visual Studio SDK Interop assemblies.

Levels of Extensibility

Visual Studio 2005 (VS 2005) provides you with three levels of extensibility, with each level increasing with power and functionality. First, the easiest way to extend Visual Studio is to record a macro. Recording a macro is extremely simple and provides you with the ability to record almost all commands and keystrokes. Additionally, macros give you access to nearly all of the Visual Studio automation object model, which contains more than 140 objects and provides you with full access to the .Net framework.

Macros

Macros are not new. Office developers have been using them for thousands of years and they have been an integral aspect of most Microsoft automation models.

Unfortunately, for all of their functionality, Macros do have some legitimate drawbacks. To start with, they can only be written in the Visual Basic language, limiting their usability to a somewhat narrow audience. Next, Macros cannot be used to implement new tool windows, commands, or Tools Options pages. Finally, macros are not compiled, which means you have less control when trying to protect your intellectual property.

Add—ins

The next level in the hierarchy is a VS 2005 add—in. Add—ins provide you with full control over the Visual Studio automation object model . This ability facilitates the interaction with most of the tools and functionality in the VS 2005 IDE. This includes but is not limited to the Text Editor, Output Window, Task List, and Code Model. Additionally, there are very few barriers to developing an add—in because there is an out of the box Add—in Solution and Wizard in VS 2005.

Unlike Macros, Add—ins can be written in any language that supports COM including Visual C#, VB.Net, and Visual C++. Add—ins are compiled as DLL’s which also means you have greater flexibility in protecting your intellectual property. Furthermore, unlike Macros, Add—ins can implement new Commands, Tools Options pages and Tool windows. However, Add—ins cannot implement new document types, new project types, new debug engines, etc. which all require the next level of extensibility.

Packages

Packages, are the third a final level of extensibility in the Visual Studio 2005 hierarchy of extensibility. Packages in previous versions of Visual Studio.Net would have required some investment from the developer, but with the new VSIP program for VS 2005, Microsoft has added a free participation level. Packages are on the top of the pyramid; they will provide you with more flexibility than is available through an Add—in or a Macro.

When developing a package you can use C# or C++ to access the Visual Studio SDK. At this point, it’s worth stating that developing a VS Package is not for the faint of heart.

Although you can get by developing a Package without any knowledge of C++, you are going to have a lot less gray hair if you have a solid understanding of the fundamentals of C++, such as header files and interfaces.

After you register with the VSIP program and download the 100+ MB SDK, you can start building applications that truly extend the IDE. Some examples of tasks that can be performed in a package include: customizing editors, creating new projects types, integrating debug tools, and integrating new languages.

One caveat worth mentioning is that if you plan on commercially marketing any package you will need pay into the VSIP partner program. Plus, as most would assume, the developers license key distributed with the SDK cannot be used for distribution. To distribute a VS Package one needs to apply for a “product license key” (PLK) from Microsoft.

Managed VS Packages

While the functionality found in Add—ins and Macros is extremely useful, there is already a lot of documentation out there on these subjects. Not to say that the help files and the samples with the VS SDK are bad, because they’re not, but many of them are complicated for non C++ developers because they refer to C++ interfaces, use Hungarian notation, and use COM terms like IUnknown, Co—Creatable, etc. that many new developers will not be familiar with. Additionally, there are many significant changes to the VS SDK that need to be examined.

The VS 2005 SDK supports several new and enhanced features to make it easier for you to integrate your programs into VS 2005. The first new feature is the MPF. The MPF enables you to develop packages in managed code using C# or VB.Net. The MPF is a significant upgrade from the VSIP 2003 program because it provides default implementations for many of the more complicated COM interfaces.

You may be wondering why I keep mentioning COM. Well, if you haven’t figured it out yet, Visual Studio is developed using COM objects. What this means is you should be prepared for GUID’s and cryptic interface names. Fortunately, when using managed code you have two layers of abstraction from the underlying COM Interfaces as illustrated in Figure 1.

Figure 1:

As Figure 1 shows, when developing a managed package there is a hierarchy of assemblies. Although I wish I could say that you will only be interacting with the MPF, the fact is you will probably need to access the Visual Studio Interop Assemblies directly to retrieve handles to various interfaces. Both scenarios will be explored further in the example later in this article.

So, you are probably wondering what you can actually do with the MPF? Let’s look at some specifics.

The MPF can help you all of the following points of extensibility:

  • Tool Windows / Document Windows
    • Creation and manipulation
  • Commands
    • Adding Commands
    • Command Routing
  • Tools Options, Automation model additions and VS Settings participation
    • Made much easier and can use the same underlying code to handle all these areas
    • Package Registration
      • Accomplished through declarative code attributes
      • Extensible by 3rd parties
    • Task List / Error List integration
    • Properties window integration

    For the most part you can accomplish the majority of tasks through the MPF. Some examples of places where you will have to dive into the interop assemblies include: language services, editors, and debuggers. Additionally, there are cases where you will have to use a combination of the MPF and the Interop assemblies to accomplish your goals.

    The final point worth mentioning before we move into an example is the fact that the MPF and the Visual Studio SDK assemblies are completely separate from the automation model (different namespaces, assemblies, etc.). Although there is overlap in functionality, the VS SDK hooks directly into the core of Visual Studio while the automation model, which is stored in the EnvDTE and EnvDTE80 assemblies, works through a higher level layer of abstraction. With that said, there is no reason why you can’t use the automation model in your VS Package, in fact it is probably recommended in some cases. I just want to make sure that we are clear, and that nobody misunderstands and thinks that they worked with the SDK by using EnvDTE for an Add—in.

    Getting Started

    I have discussed at a high level the Visual Studio 2005 extensibility model and I’ve provided an overview of the Managed Package Framework plus the VS SDK. It has always been my opinion that the best way to really understand software development is to develop some software.

    To begin, download the VS SDK from http://msdn.microsoft.com/vstudio/extend/. You will need to go through a painless registration process. In order to work with the “Experimental” version of Visual Studio 2005 (what you’re downloading), you will need to have a non—express edition of Visual Studio 2005 installed on your workstation.

    You may have noticed that I just called the VS SDK an “experimental” version. Well, to clarify, it’s not that this is some Frankenstein laboratory creation, but rather Microsoft has done something smart by isolating the SDK in a separate area of the machines registry. The separation takes place in a new registry hive stored in HKEY_LOCAL_MACHINESOFTWAREMicrosoftVisualStudio8.0Exp. This new hive is an exact clone of your Visual Studio 2005 install located in HKEY_LOCAL_MACHINESOFTWAREMicrosoftVisualStudio8.0. What this does is effectively create a sandbox for you to work in so you don’t have to worry about destroying your normal Visual Studio installation.

    In addition with the new install, you will see a new start item called “Visual Studio 2005 SDK.” Below that, you will see the build (I’m using the 3/2006 build), and under that you will see a number of new items. Of note, you will see “Start Visual Studio 2005 under experimental hive” which will launch VS 2005 running in the sandbox and you will also see an item that says “Reset the Visual Studio 2005 experimental hive.” You guessed it, this is basically the reset button which restores your sandbox to its original versions after you dork it up when writing code. I assure you will use this at least once while working with the SDK!

    Adding a Menu Item

    Launch the experimental version of Visual Studio from the start menu. From the file menu select “New—>Project” and from the “Other Project Types” menu select “Extensibility” and choose “Visual Studio Integration Package” (see Figure 2). For this first example you will be adding a new button to the top—level tool bar of the Visual Studio 2005 IDE.

    Figure 2:




    Click here for larger image

    After your new package opens, the wizard window will pop up. The Visual Studio Integration Package Wizard does a lot of the heavy lifting for you by creating the plumbing code needed to build a package.

    The first step in the wizard is the language selector; for this example you will be using C# and you should let the wizard generate the cryptographic key for you (see Figure 3). Step 2 requires you to enter some information such as Company Name, Package Name, Minimum VS Version, etc., all pretty self—explanatory.

    Figure 3:

    In step 3 the wizard will ask you if you want to create a “Menu Command”, “Tool Window”, or “Custom Editor”. You can select any combination of those, but for this example select “Menu Command”. After selecting “Menu Command” in Step 3, Step 4 will display “Command Name” and “Command ID” text boxes, which will be “ErrorLog” (more on this to come) and “cmdidErrorLog” in this example. After you have typed in the information in step 4 you are done with the wizard and can click “Finish.”

    What Are All Of These Files?

    You are probably noticing a whole bunch of files in your solution. Let’s take a look at some of the files that the wizard generated. The first file is VsPkg.cs, which is the core of the project, and the VS Package. This file contains the class, “MyFirstPackage”, which inherits from the Microsoft.Visual.Studio.Shell.Package class. The base class “Package” is one of the many classes in the Managed Package Framework (MPF). The “Package” class provides the implementation for IVsPackage which is stored in Microsoft.VisualStudio.Shell.Interop. Along with the implementation code you will also see a number of attributes at the top of the “MyFirstPackage” class. Without going into too much detail, these attributes are used as registration instructions. For example:

     [DefaultRegistryRoot("SoftwareMicrosoftVisualStudio8.0")] 
    

    The “DefaultRegistryRoot” attribute tells the registration system (Regpkg.exe) where the default registry hive is located.

    Along with VsPkg.cs, the following files have also been generated and added to the solution. One thing to note is that the files stored in the CTCComponents directory are Command Table Configuration (CTC) files. For the most part these files are C++ files which contain the meta—data used by the CTC compiler to define the layout and type of the actual commands. The CTC compiler takes all of the CTC specific files and translates them into binary that is used by Visual Studio. Don’t worry if you’re not a C++ person because the work that you will be doing in these files is minimal.

    • CTCComponents Directory
      • Guids.h – CTC file containing the list of command GUID’s
      • PkgCmdID.h – CTC file containing Command ID’s
      • Resource.h – CTC resource ID’s
      • MyFirstPackage.ctc – CTC file that contains layout specific directives
    • Guids.cs – contains all of the command GUID’s, C# equivalent of Guids.h
    • Key.snk – The key file used to sign the assembly
    • PkgCmdID – contains the list of Command ID’s, C# equivalent of PkgCmdID.h
    • ResourcesId.cs — Contains a list of Resource IDs, C# equivalent of Resource.h
    • VsPkg.cs – The actual package

    Make It Go!

    As it stands right now, you have a simple package with a new menu command under the tools menu (see Figure 4). What if you want the command button positioned as a top—level menu item like: “File, Edit, or View” and what if you want to change that stupid “1” icon that the wizard automatically added? As previously stated, the CTC files control the layout of the commands in the IDE. So, to move the button, you just have to make a few edits to the MyFirstPackage.ctc file.

    Figure 4:

    Locate the NEWGROUPS_BEGINNEWGROUPS_END section in the ctc file. This section defines menu groups that are containers for menus and or buttons (i.e., commands). In the NEWGROUPS section you will see the following code:

    guidMyFirstPackageCmdSet:MyMenuGroup, guidSHLMainMenu:IDM_VS_MENU_TOOL, 0x0600;
    

    In this code, the “MyMenuGroup” is being defined under the “Tools” menu that is denoted by the IDM_VS_MENU_TOOL ID. In order to move your menu group to the top—level toolbar, simply change the IDM_VS_MENU_TOOL to IDM_VS_TOOL_MAINMENU like this:

    guidMyFirstPackageCmdSet:MyMenuGroup, guidSHLMainMenu:IDM_VS_TOOL_MAINMENU, 0x0600;
    

    The next step is to tell CTC to change the bitmap file associated with your command. Note that all of the top—level toolbar items (i.e. “File”, “Edit” etc.) are bitmaps so you must have a bitmap associated with your group. If you choose to leave the group under the Tools menu, then you can just removed the icon.

    There are two sections you should examine to change the bitmap associated with the menu group. The first section, the BITMAPS_BEGINBITMAPS_END section, contains the bitmap’s associated with the command.

    guidMyFirstPackageCmdSet:IDB_MENU_IMAGES, bmpPic1, bmpPic2, 
      bmpPicSmile, bmpPicX, bmpPicArrows;
    

    The second section BUTTONS_BEGINBUTTONS_END defines the buttonmenu groups visibility, priority, etc.

    guidMyFirstPackageCmdSet:cmdidErrorLog, guidMyFirstPackageCmdSet:MyMenuGroup,
           0x0100, guidMyFirstPackageCmdSet:bmpPic1, BUTTON, , "ErrorLog";
    

    As you may have already figured out, to change the icon, simply change guidMyFirstPackageCmdSet:bmpPic1 to guidMyFirstPackageCmdSet:bmpPicX. At this point you are probably asking: Can I customize the bitmaps? The answer is yes, however I am not going to cover it in this article. If you are really curious, check the “Creating Custom Bitmap Icons” in the help documentation. Additionally, by default you can use the bitmaps that ship with Windows, Office, or Visual Studio.

    In Figure 5 you see that the menu has moved from the tools menu to the top—level toolbar, and the icon has changed to an “X.”

    Figure 5:

    Make It Really Go!

    You have successfully created a button, but it at this point it doesn’t really do anything, so let’s walk through an example which reads the errors in the Error List window and writes them to a file. Let’s say that you working on a .Net project and you want to keep a running log of the errors you receive when you compile. Well, after we are done with the example all you will have to do after you compile is click on the “X” button you just created to write the errors to a running log file.

    The wizard has already hooked up an event handler in the Initialize method of VsPkg.cs so you already have a callback method named MenuItemCallback. The wizard has also added some sample implementation code in the callback method, which pops up a message box.

    IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
    Guid clsid = Guid.Empty;
    int result;
    uiShell.ShowMessageBox(
        0,
        ref clsid,
        "MyFirstPackage",
        string.Format(CultureInfo.CurrentCulture, 
                      "Inside {0}.MenuItemCallback()", this.ToString()),
        string.Empty,
        0,
        OLEMSGBUTTON.OLEMSGBUTTON_OK,
        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
        OLEMSGICON.OLEMSGICON_INFO,
        0,        // false
        out result);
        

    As you can see, when working with the SDK the syntax is a little different than what you are used to in the .Net framework.

    After removing the generated code from the MenuItemCallback method you can add the following code which creates a generic IList, gets the pointer to the SVsErrorList interface, enumerates the tasks, and calls the WriteLog method to dump the results to disk.

    IList tasks = new List();
    
    IVsTaskList iel = (IVsTaskList)GetService(typeof(SVsErrorList));
    
    IVsEnumTaskItems enumTaskItems;
    iel.EnumTaskItems(out enumTaskItems);
    int result;
    
    uint[] fetched = new uint[1];
    do
    {
       IVsTaskItem[] taskItems = new IVsTaskItem[1];
    
       result = enumTaskItems.Next(1, taskItems, fetched);
    
       if (fetched[0] == 1)
       {
           IVsTaskItem taskItem = taskItems[0] as IVsTaskItem;
           tasks.Add(taskItem);
       }
    
    } while (result == 0 && fetched[0] == 1);
    
    WriteLog(tasks);
    

    Although this example is fairly simple, it does provide a good starting point for integrating all sorts of cool applications into the IDE. You can even extend this example by integrating the error logging into the event system of the IDE, so your errors are automatically logged when you compile. (Note: the entire source code for the package is available for download at the end of this article).

    In conclusion, I know that I have only glazed over many of the topics and left out many others (i.e., deployment, toolwindows, editors, Domain Specific Languages, etc.). However, as an introduction to the hugely massive VS SDK I hope my articles have opened your eyes to the possibilities and the power of the VS SDK.

    Downloads

    Source code: VSExtensibility.zip – 22 kb

    About the Author

    Vijay P. Mehta works as a technical consultant in Indiana, where he uses VS.NET
    to design, develop, and architect enterprise solutions. Reach him at
    vijay@mehtasolutions.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories