Visual Studio 2005 Extensibility – VS Packages Part II

In the first part of this series we discussed at a high level the Visual Studio 2005 extensibility model and 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.

Getting Started

To begin, download the VS SDK from 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 Frankstein laboratory creation, but rather Microsoft has done something smart byisolating the SDK in a separate area of the machines registry. The separation takes place in a new registry hive stored in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0Exp. This new hive is an exact clone of your Visual Studio 2005 install located in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.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 we will be adding a new button to the top level tool bar of the Visual Studio 2005 IDE.

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 we will be using C# and we will let the wizard generate the cryptographic key for us (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. 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 we will be selecting “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.cswhich 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("Software\\Microsoft\\VisualStudio\\8.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 which 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 we want the command button positioned as a top-level menu item like: “File, Edit, or View” and what if we 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, we just have to make a few edits to the MyFirstPackage.ctc file.

Locate the NEWGROUPS_BEGIN\NEWGROUPS_END section in the ctc file. This section defines menu groups which 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 which is denoted by the IDM_VS_MENU_TOOL ID. In order to move our menu group to the top level toolbar we simply have to 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 our command. Please note that all of the top level toolbar items (i.e. “File”, “Edit” etc.) are bitmaps so we must have a bitmap associated with our group. If we had chosen to leave the group under the Tools menu, then we could have just removed the icon.

There are two sections we should examine to change the bitmap associated with the menu group. The first section, the BITMAPS_BEGIN\BITMAPS_END section, contains the bitmap’s associated with the command.

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

The second section BUTTONS_BEGIN\BUTTONS_END defines the button\menu groups visibility, priority, etc.

guidMyFirstPackageCmdSet:cmdidErrorLog, guidMyFirstPackageCmdSet:MyMenuGroup, 0x0100, guidMyFirstPackageCmdSet:bmpPic1, BUTTON, , “ErrorLog”;

As you may have already figured out, to change the icon we simply need to change guidMyFirstPackageCmdSet:bmpPic1 to guidMyFirstPackageCmdSet:bmpPicX. At this point you are probably asking: Can I cusomtize the bitmaps? The answer is yes, however I am not going to cover it in this article. If you are really curious, checkthe “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 we see that the our menu has moved from the tools menu to the top-level toolbar, and the icon has changed to an “X.”

Make It Really Go!

We 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 we 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.

IListIVsTaskItem> tasks = newListIVsTaskItem>();

IVsTaskList iel = (IVsTaskList)GetService(typeof(SVsErrorList));

IVsEnumTaskItems enumTaskItems;

iel.EnumTaskItems(out enumTaskItems);

int result;

uint[] fetched = newuint[1];

do

{

IVsTaskItem[] taskItems = newIVsTaskItem[1];

result = enumTaskItems.Next(1, taskItems, fetched);

if (fetched[0] == 1)

{

IVsTaskItem taskItem = taskItems[0] asIVsTaskItem;

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 that this series has opened some eyes on the possibilities and the power of the VS SDK.