Tutorial 2: Creating Your First VSPackage

 

A VSPackage is the unit of extensibility you build when extending to Visual Studio. At the core, a VSPackage is a co-creatable COM object that implements the IVsPackage interface. However, by using the Managed Package Framework (MPF) introduced in Visual Studio 2005, you no longer need to implement the IVsPackage interface yourself because it provides a base class implementation that you can inherit from. The interface is completely abstracted from you and you will not even see it. We will use the MPF throughout this and other tutorials.

The package wizard creates a project in Visual Studio that consists of everything you need to create your extension. The package project includes several source and configuration files, including an important file called by default VsPkg.cs. This file includes a class that you name when you set up the project; this class is derived from a class called Package (supplied by MPF) and includes attributes that define how your package should operate with Visual Studio. Additionally, this class contains initialization code and other code such as menu item handlers. This class is the foundation of your package.

In this tutorial, you will create a new package and customize the VsPkg.cs file in various ways.

You will learn how to:

  • Create a VSPackage with the project wizard.
  • Implement the menu item handler.
  • Add a default keyboard shortcut.
  • Add custom information to Splash screen and Help/About dialog box.
  • Obtain and use a Package Load Key.

Steps

Create a VSPackage with the project wizard

  1. On the File menu, point to New, and then click Project. In the New Project dialog box, expand Other Project Types in the left tree view and click Extensibility. Under Visual Studio installed templates on the right, click Visual Studio Integration Package. Type in FirstPackage in the Name field, and enter a good location such as C:\ExtendVS. Optionally, you can create a new solution, such as FirstPackage. Click OK.

  2. On the wizard welcome page, click Next.

  3. Click Visual C#, click Generate a new key file to sign the assembly, and then click Next.

  4. Enter a company name, type FirstPackage in the VSPackage name field, and enter any number you want for VSPackage version. Next to Minimum Visual Studio edition, click Standard. You can optionally fill in additional information in the Detailed information field. Then click Next.

  5. Select Menu Command and click Next.

  6. In the Command name field, type First Command (this is the text that will appear on the menu). Next to Command ID, type cmdidFirstCommand (this is the identifier that will be used throughout your code to identify the command). Then click Finish.

    After a few moments, the wizard will create a new Visual Studio project for you.

    Initially, you'll have a basic project with minimal functionality. You can try it out by clicking Debug and then clicking Start without debugging.

    The first time the project builds, it could take a few minutes, and you could get a warning message that appears over the Windows tray saying that Visual Studio is busy. After a few more moments, the experimental build of Visual Studio will open and your extension will be loaded.

  7. In the experimental build, open the Tools menu, where you'll see a First Command menu item, which you typed in the wizard.

  8. Click First Command. The following message box will appear:

Implement the menu item handler

The message box you've just seen comes from code in the handler for the menu item. This handler is in the VsPkg.cs file.

  • From the Solution Explorer, open VsPkg.cs.

    Here's the code for the menu handler, including the class header. You can see that the class is derived from Package. Also, the menu handler function is a standard event handler method like any other event handler in a .NET Windows Forms program.

    public sealed class FirstPackage : Package
    {
    
    . . .
    
        private void MenuItemCallback(object sender, EventArgs e)
        {
            // Show a Message Box to prove we were here
            IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
            Guid clsid = Guid.Empty;
            int result;
            uiShell.ShowMessageBox(
                0,
                ref clsid,
                "FirstPackage",
                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);
        }
    }
    

Add a default keyboard shortcut

By default, the menu item that was created does not have a keyboard shortcut. Let's add one.

  • In the Solution Explorer, expand the CtcComponents folder. Under this folder, open FirstPackage.ctc.

This file describes the GUI elements to be used by Visual Studio. It has several #include lines (using C++ syntax) that include other files containing various identifiers.

At the end of this file is a section called KEYBINDINGS_SECTION, where you add the keyboard shortcuts.

  • Add the following line shown in bold to the key bindings section. This line goes just prior to the KEYBINDINGS_END line.

    KEYBINDINGS_SECTION
      // ...a whole bunch of comments...
      // Command                        when available          emulation               keystate
    guidFirstPackageCmdSet:cmdidFirstCommand, guidVSStd97, guidVSStd97, 'M':CS;
    KEYBINDINGS_END
    

This line contains comma-delimited fields. The first field is the command that will receive the key binding. This field is in the form GUID:ID, where the first part is the GUID for the package, which is defined in Guids.h (also in the CtcComponents folder). The second half is the command identifier you typed in when you ran the wizard; this identifer is defined in CommandIds.h (which is also in the CtcComopnents folder).

The second field is a GUID representing where this keyboard shortcut will be available. Different parts of Visual Studio can have different keyboard handlers. For example, when you are inside a text editor, the keyboard shortcut CTRL+I runs the Incremental Search command. But when the Solution Explorer is active, CTRL+I has no key binding. Thus, CTRL+I is only available to the text editor.

We want our keyboard shortcut to be available everywhere in Visual Studio; thus, it will be global. For global keyboard shortcuts, we use the GUID defined by guidVSStd97.

The third field is not presently used; for now, you use the same identifier from the second field, guidVSStd97.

The fourth and final field is the keyboard shortcut. This takes the format 'character':otherkeys, where the character for the shortcut goes in single-quotes. The otherkeys portion represents SHIFT, CTRL, and ALT with the letters S, C, and A, respectively. In this sample we're using CS, which means CTRL and SHIFT. The letter used in this example is M; thus, this keyboard shortcut is CTRL+SHIFT+M.

  1. Save FirstPackage.ctc.

  2. Run the build with Debug/Start without debugging.

    This time, when the experimental build starts and you open the Tools menu, you will see a keyboard shortcut of CTRL+SHIFT+M on the right side of the menu.

  3. Press CTRL+SHIFT+M, and you'll see the same message box that appeared earlier.

Add custom information to the Splash screen and Help/About dialog box

In order to add a touch of professionalism to your extension, you can include an icon and information in the Help/About dialog box.

Your main package class (in this example, FirstPackage, found in VsPkg.cs) includes an attribute that specifies where to find information for the About dialog box. The initial code states that the information comes from the attribute's parameters. To customize this, change the first parameter to True, in order to get the information from the package class itself.

  1. Open the VsPgk.cs file. Find the installedProductRegistration attribute, and change its parameters as shown here in bold.

    [InstalledProductRegistration(true, null, null, null)]
    

    In order for Visual Studio to obtain the information from your class, your class must be modified to implement the IVsInstalledProduct interface.

  2. Still in VsPkg.cs, find the class header line, and add the following comma and interface name, shown in bold.

    public sealed class FirstPackage : Package, IVsInstalledProduct
    
  3. To automatically add the interface functions, hover the mouse over the word IvsInstalledProduct. When the SmartTag menu appears, click the first menu item.

    This will automatically add the functions. Here's the code that got added; these are the functions we will fill in shortly:

    #region IVsInstalledProduct Members
    
    public int IdBmpSplash(out uint pIdBmp)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    
    public int IdIcoLogoForAboutbox(out uint pIdIco)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    
    public int OfficialName(out string pbstrName)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    
    public int ProductDetails(out string pbstrProductDetails)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    
    public int ProductID(out string pbstrPID)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    
    #endregion
    
  4. In the directory C:\Program Files\Visual Studio 2005 SDK\<YYYY>.<MM>\VisualStudioIntegration\Samples\IDE\CSharp\Reference.Package, you'll find two files, GenericPackage.bmp and GenericPackage.ico, which you can use for this project. Copy these files to your extension's resource directory, C:\ExtendVS\FirstPackage\FirstPackage\Resources.

  5. In the Solution Explorer, right-click the Resources directory, and in the pop-up menu click Add and then click Existing Item. Set your file type filter to "All Files (*.*)" and add the GenericPackage.bmp file. Then repeat the procedure and add the GenericPackage.ico file.

  6. Right-click the file C:\ExtendVS\FirstPackage\FirstPackage\VSPackage.resx, and then click Open With. Select the XML Editor in the dialog box to open the file in the XML Editor.

  7. Add the following bold lines immediately before the final </root> line:

    <data name="500" type="System.Resources.ResXFileRef, System.Windows.Forms">
        <value>Resources\GenericPackage.bmp;System.Drawing.Bitmap, System.Drawing,
            Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
    </data>
    <data name="600" type="System.Resources.ResXFileRef, System.Windows.Forms">
        <value>Resources\GenericPackage.ico;System.Drawing.Icon, System.Drawing,
            Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
    </data>
    </root>
    

    This code declares the GenericPackage.bmp with resource ID 500, and GenericPackage.iso with resource ID 600.

  8. Replace the body of each method with the following respective code shown in bold.

    #region IVsInstalledProduct Members
    
    public int IdBmpSplash(out uint pIdBmp)
    {
        pIdBmp = 500;    return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
    public int IdIcoLogoForAboutbox(out uint pIdIco)
    {
        pIdIco = 600;    return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
    public int OfficialName(out string pbstrName)
    {
        pbstrName = "My Package";    return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
    public int ProductDetails(out string pbstrProductDetails)
    {
        pbstrProductDetails = "This is my package";    return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
    public int ProductID(out string pbstrPID)
    {
        pbstrPID = "My Package ID";    return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
    #endregion
    

    The first two functions return the IDs to use for the bitmap and icon, respectively. The remaining three functions return the name, product details, and product ID, respectively, to show in the About dialog box.

  9. Save VsPkg.cs.

  10. Run the project without debugging.

As the experimental build opens, notice the splash screen; you will see "My Package" listed along with the other installed products.

  1. Click Help, and then click About Microsoft Visual Studio. The About dialog box now shows the icon and text for your package.

Obtain and use a Package Load Key

When you ship your package to other developers, they need a special key file in order for Visual Studio to load your package. Without this key file, Visual Studio will refuse to load the package.

Note   The reason you don't need this file when you run the experimental build is because the Visual Studio SDK includes a special key, called the Developer Load Key (DLK), which allows all packages to load. By the agreement with Microsoft when you installed the SDK, you are not allowed to ship this special key that lets all packages load. You must obtain a key from Microsoft. To do so, visit this site: https://www.vsipmembers.com. (You will need your Microsoft Passport ID to log into the site.) Then follow the instructions to obtain a key.

After you obtain the key, follow these steps to add the key to your project.

  1. In the Solution Explorer, double-click VSPackage.resx to open it.

    When the resource file opens, you'll see a grid. Add a line containing a unique number in the first column and the key data you obtained from the site for the second column. (This image simply says KEYDATA where you'll add your own key data.)

  1. Save the file and close it.

    In order to use this data, however, you need to modify your package class by modifying an attribute.

  2. Open VsPkg.cs. Locate the following line, and replace its final attribute with the resource number you added, shown in bold:

    [ProvideLoadKey("Standard", "1.0", "FirstPackage", "Company", 104)]
    
  3. Save the file.

Now you need to update a setting in the project.

  1. In the Solution Explorer, right-click the FirstPackage project name, and then click Properties.

  2. In the Properties window, click the Debug tab on the left.

  3. Add the following argument, shown in bold, to the Command line arguments setting. The /noVSIP switch tells Visual Studio not to use the DLK and to load packages as if they were deployed normally

    /rootsuffix Exp /noVSIP
    
  4. Close the project settings page. Click File, and then click Save All.

Your project is now set up with its own key. If you are experiencing trouble with getting your package to load properly, Dr. Ex has a great blog post on how you can debug it here.

What's Next

In the next tutorial, we will create our own tool window inside the Visual Studio IDE. Tool windows are the basic windows throughout Visual Studio, such as the Solution Explorer and the Task List. Our tool window will have all the same docking behavior as the other tool windows, and will allow us to play music files right inside of Visual Studio!