Logo
HOWTO: Create two Visual Studio packages in a single assembly

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2010
Date: January 2015   Microsoft Visual Studio 2012
      Microsoft Visual Studio 2013
      Microsoft Visual Studio 2015
Introduction

If you need to create two packages, the recommended approach (because it is more clean and isolated) would be to create two package projects in a single solution, each package project with its own .vsct command table file, and each package project would generate its own output assembly and .vsix file. However, it is possible to create two packages in a single assembly, each one with its own .vsct file. This article explains the steps to do it correctly.

More Information

To create a package project with two packages inside, follow these steps:

  • In the New Project dialog of Visual Studio, select the "Visual C#" > "Extensibility" > "Visual Studio Package" template. In the Name field, enter the VSTwoPackages value.
  • Follow the pages of the package wizard and:
    • In the page 1 of 7 (Select a Programming Language) select Visual C#.
    • In the page 2 of 7 (Basic VSPackage Information) enter the values "MyCompany" in the "Company name" field and "VSTwoPackages" in the "VSPackage name" field.
    • In the page 3 of 7 (VSPackage Options) mark the "Menu command" checkbox.
    • In the page 4 of 7 (Command Options) enter the values "My Command name 1" in the "Command name" field and "cmdidMyCommand1" in the "Command ID" field.
  • In the Solution Explorer:
    • Rename the "VSTwoPackages.vsct" file to "VSPackage1.vsct".
    • Right-click on the "VSPackage1.vsct" file and click the "Copy" menu entry.
    • Select the "VSTwoPackages" project node and right click on it.
    • Select the "Paste" menu entry. A new "VSPackage1 - Copy.vsct" file is generated. Rename it to "VSPackage2.vsct".
    • Select the "VSTwoPackages" project node, right-click on it and click the "Unload Project" menu entry.
    • Select the "VSTwoPackages (unavailable)" project node, right-click on it and click the "Edit VSTwoPackages.csproj" menu entry.
  • In the editor window of the VSTwoPackages.csproj file, change the <ItemGroup> entries that reference the .vsct files as follows, to provide a different resource name for the for each one:
Language: XML   Copy Code Copy Code (IE only)
<ItemGroup>
   <VSCTCompile Include="VSPackage1.vsct">
      <ResourceName>Menus.ctmenu1</ResourceName>
   </VSCTCompile>
</ItemGroup>
<ItemGroup>
   <VSCTCompile Include="VSPackage2.vsct">
      <ResourceName>Menus.ctmenu2</ResourceName>
   </VSCTCompile>
</ItemGroup>
<ItemGroup>
   <None Include="Resources\Images.png" />
</ItemGroup>
<ItemGroup>
   <Content Include="Resources\Package.ico" />
</ItemGroup>
  • Save the changes and close the editor window.
  • In the Solution Explorer, select the "VSTwoPackages (unavailable)" project node, right-click on it and click the "Reload Project" menu entry.
  • Edit the "PkgCmdID.cs" file to add second command id (they can have the same value since they will belong to different package command sets):
Language: C#   Copy Code Copy Code (IE only)
namespace MyCompany.VSTwoPackages
{
   static class PkgCmdIDList
   {
      // Command IDs for package 1
      public const uint cmdidMyCommand1 = 0x100;

      // Command IDs for package 2
      public const uint cmdidMyCommand2 = 0x100;

   }
}
  • Edit the "Guids.cs" file and define the Guids for each package as follows (ensure to use different guids for your product than those used in this sample):
Language: C#   Copy Code Copy Code (IE only)
using System;

namespace MyCompany.VSTwoPackages
{
   static class GuidList
   {
      public const string guidVSPackage1PkgString = "4375b007-5579-4c16-9a35-b45cdf1365e1";
      public const string guidVSPackage2PkgString = "b1116b7e-8ffe-4761-90cf-7254591ab81e";

      public const string guidVSPackage1CmdSetString = "722552ad-79dd-46c0-820d-a39edbd274fe";
      public const string guidVSPackage2CmdSetString = "a1ae01a0-d4a9-4fd8-b299-0ce183a31df0";

      public static readonly Guid guidVSPackage1CmdSet = new Guid(guidVSPackage1CmdSetString);
      public static readonly Guid guidVSPackage2CmdSet = new Guid(guidVSPackage2CmdSetString);
   }
}
  • Edit the Package1.vsct file with this content (bitmaps have been omitted for clarity). Ensure that the guid symbols and id symbols match the ones defined in the Guids.cs file:
Language: XML   Copy Code Copy Code (IE only)
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">

   <Extern href="stdidcmd.h"/>
   <Extern href="vsshlids.h"/>

   <Commands package="guidVSPackage1Pkg">

      <Groups>
         <Group guid="guidVSPackage1CmdSet" id="MyMenuGroup" priority="0x0600">
            <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
         </Group>
      </Groups>
      <Buttons>
         <Button guid="guidVSPackage1CmdSet" id="cmdidMyCommand1" priority="0x0100" type="Button">
            <Parent guid="guidVSPackage1CmdSet" id="MyMenuGroup" />
            <Strings>
               <ButtonText>My Command name 1</ButtonText>
            </Strings>
         </Button>
      </Buttons>

   </Commands>

   <Symbols>
      <GuidSymbol name="guidVSPackage1Pkg" value="{4375b007-5579-4c16-9a35-b45cdf1365e1}" />

      <GuidSymbol name="guidVSPackage1CmdSet" value="{722552ad-79dd-46c0-820d-a39edbd274fe}">
         <IDSymbol name="MyMenuGroup" value="0x1020" />
         <IDSymbol name="cmdidMyCommand1" value="0x0100" />
      </GuidSymbol>
   </Symbols>

</CommandTable>
  • Edit the Package2.vsct file with this content (bitmaps have been omitted for clarity). Ensure that the guid symbols and id symbols match the ones defined in the Guids.cs file:
Language: XML   Copy Code Copy Code (IE only)
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">

   <Extern href="stdidcmd.h"/>
   <Extern href="vsshlids.h"/>

   <Commands package="guidVSPackage2Pkg">
      
      <Groups>
         <Group guid="guidVSPackage2CmdSet" id="MyMenuGroup" priority="0x0600">
            <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
         </Group>
      </Groups>
      <Buttons>
         <Button guid="guidVSPackage2CmdSet" id="cmdidMyCommand2" priority="0x0100" type="Button">
            <Parent guid="guidVSPackage2CmdSet" id="MyMenuGroup" />
            <Strings>
               <ButtonText>My Command name 2</ButtonText>
            </Strings>
         </Button>
      </Buttons>

   </Commands>

   <Symbols>
      <GuidSymbol name="guidVSPackage2Pkg" value="{b1116b7e-8ffe-4761-90cf-7254591ab81e}" />

      <GuidSymbol name="guidVSPackage2CmdSet" value="{a1ae01a0-d4a9-4fd8-b299-0ce183a31df0}">
         <IDSymbol name="MyMenuGroup" value="0x1020" />
         <IDSymbol name="cmdidMyCommand2" value="0x0100" />
      </GuidSymbol>
   </Symbols>

</CommandTable>
  • Edit the "VSPackage.resx" file to enter these values:
Language: Text   Copy Code Copy Code (IE only)
110: My Package 1
112: Information about My Package 1
114: My Package 
116: Information about My Package 2 
  • Edit the "VSTwoPackagesPackage.cs" file to include a second package and adjust the attributes values of both packages:
Language: C#   Copy Code Copy Code (IE only)
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;

namespace MyCompany.VSTwoPackages
{
   [PackageRegistration(UseManagedResourcesOnly = true)]
   [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
   [ProvideMenuResource("Menus.ctmenu1", 1)]
   [Guid(GuidList.guidVSPackage1PkgString)]
   public sealed class VSPackage1Package : Package
   {
      public VSPackage1Package()
      {
      }

      protected override void Initialize()
      {
         base.Initialize();

         OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
         if (null != mcs)
         {
            CommandID menuCommandID = new CommandID(GuidList.guidVSPackage1CmdSet, (int)PkgCmdIDList.cmdidMyCommand1);
            MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
            mcs.AddCommand(menuItem);
         }
      }

      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;
         Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
                    0,
                    ref clsid,
                    "VSTwoPackages",
                    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));
      }
   }

   [PackageRegistration(UseManagedResourcesOnly = true)]
   [InstalledProductRegistration("#114", "#116", "1.0", IconResourceID = 400)]
   [ProvideMenuResource("Menus.ctmenu2", 1)]
   [Guid(GuidList.guidVSPackage2PkgString)]
   public sealed class VSPackage2Package : Package
   {
      public VSPackage2Package()
      {
      }

      protected override void Initialize()
      {
         base.Initialize();

         OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
         if (null != mcs)
         {
            CommandID menuCommandID = new CommandID(GuidList.guidVSPackage2CmdSet, (int)PkgCmdIDList.cmdidMyCommand2);
            MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
            mcs.AddCommand(menuItem);
         }
      }

      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;
         Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
            0,
            ref clsid,
            "VSTwoPackages",
            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));
      }
   }
}
  • Run the project. The experimental instance of Visual Studio is launched.
  • Go to the "Help" > "About Microsoft Visual Studio" window and check that both packages appear in the "Installed Products" list.
  • Go to the "Tools" menu and check that both menu entries appear, one for each package, than when clicked show the information about the package they belong to.


Go to the 'Visual Studio Extensibility (VSX)' web site for more articles like this (Articles section)