Creating Visual Studio 2019 Extension

In this article, we are going through the details and benefits of using Visual Studio extensions and also going through creating a Visual Studio 2019 extension step by step.

In this article, we are going through the details and benefits of using Visual Studio extensions and also going through creating a Visual Studio 2019 extension to extend the Menu step by step. 
 
Pre-Requisites

What are Visual Studio 2019 Extensions?

 
Visual Studio Extensions are extended customized functionalities that may be added to Visual Studio by any developer.
 
With a Visual Studio Extension, you may customize everything like,
  1. Visual Studio editor with new languages, IntelliSense, syntax color, etc..
  2. Visual Studio menu with new items and actions;
  3. Visual Studio projects with new templates;
  4. Visual Studio commands with customized settings;
  5. And so on... your creativity is your limit.
You can find a list of existent Visual Studio Extension on Visual Studio MarketPlace
 
Read more about Visual Studio Extension here
 

Types of Visual Studio Extensions

 
The most used types of Visual Studio extensions are the ones as follows, which you may be adding new items or overriding the behavior of the existing ones,
  • Menu, as explained in this article;
  • Command;
  • Tools;
  • Editor;
  • Language;
  • Projects;
  • User settings;
  • Isolated shell;
Read more about Visual Studio Extension types here.
 

Creating your first Visual Studio 2019 Extension Step by Step

 
Creating the project
 
Add a new project of type VSIX Project,
 
 
 
Set your project name as SampleVisualStudioExtension,
 
 
This is the project structure created from the template,
 
 

Understanding the project structure

 
Command class
  • Has the code for the desired behavior of the functionalities;
  • Is initialized by package class;
  • Has the property CommandId, which binds the command with the menu item, set by the vsct file inside the Symbols section;
  • Has the property CommandSet, which binds the command with the commands menu group, set by the vsct file inside the Symbols section;
  • Read more here.
 
 
 
VSCT file
  • It is the command table configuration file, which works as a Web.Config for Web Applications projects;
  • XML based;
  • Has the menu definitions;
  • Has the buttons definitions;
  • Has the commands definitions;
  • Has the images definitions;
  • Read more here.
 
 
 
Package class
  • Represents the package itself;
  • Can handle asynchronous services;
  • Initializes the command classes;
  • Has the property PackageGuidString, which is the package GUID, set by the vsct file inside the Symbols section;
  • Read more here
 
 
 
Package manifest file
  • Has the installation desired behavior for the Visual Studio extension;
  • Has the Visual Studio extension description. Such as Extension Name, Author, Description, License, etc..
  • Read more here
Resources folder 
  • Storage for the resources used in the extension. Such as images, icons, keys, etc..
  • The items here are mapped with the vsct file inside the Commands section;
  • Read more here.
Implementing a new functionality
 
Add a new item into your project,
 
Select the Custom Command item, and set its name as SampleCommand,
 
output
 
Objects created
 
Class created,
  1. using System;  
  2. using System.ComponentModel.Design;  
  3. using System.Globalization;  
  4. using System.Threading;  
  5. using System.Threading.Tasks;  
  6. using Microsoft.VisualStudio.Shell;  
  7. using Microsoft.VisualStudio.Shell.Interop;  
  8. using Task = System.Threading.Tasks.Task;  
  9.   
  10. namespace SampleVisualStudioExtension  
  11. {  
  12.     /// <summary>  
  13.     /// Command handler  
  14.     /// </summary>  
  15.     internal sealed class SampleCommand  
  16.     {  
  17.         /// <summary>  
  18.         /// Command ID.  
  19.         /// </summary>  
  20.         public const int CommandId = 0x0100;  
  21.   
  22.         /// <summary>  
  23.         /// Command menu group (command set GUID).  
  24.         /// </summary>  
  25.         public static readonly Guid CommandSet = new Guid("f12cb590-7cad-4292-99cb-49d97f71e500");  
  26.   
  27.         /// <summary>  
  28.         /// VS Package that provides this command, not null.  
  29.         /// </summary>  
  30.         private readonly AsyncPackage package;  
  31.   
  32.         /// <summary>  
  33.         /// Initializes a new instance of the <see cref="SampleCommand"/> class.  
  34.         /// Adds our command handlers for menu (commands must exist in the command table file)  
  35.         /// </summary>  
  36.         /// <param name="package">Owner package, not null.</param>  
  37.         /// <param name="commandService">Command service to add command to, not null.</param>  
  38.         private SampleCommand(AsyncPackage package, OleMenuCommandService commandService)  
  39.         {  
  40.             this.package = package ?? throw new ArgumentNullException(nameof(package));  
  41.             commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));  
  42.   
  43.             var menuCommandID = new CommandID(CommandSet, CommandId);  
  44.             var menuItem = new MenuCommand(this.Execute, menuCommandID);  
  45.             commandService.AddCommand(menuItem);  
  46.         }  
  47.   
  48.         /// <summary>  
  49.         /// Gets the instance of the command.  
  50.         /// </summary>  
  51.         public static SampleCommand Instance  
  52.         {  
  53.             get;  
  54.             private set;  
  55.         }  
  56.   
  57.         /// <summary>  
  58.         /// Gets the service provider from the owner package.  
  59.         /// </summary>  
  60.         private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider  
  61.         {  
  62.             get  
  63.             {  
  64.                 return this.package;  
  65.             }  
  66.         }  
  67.   
  68.         /// <summary>  
  69.         /// Initializes the singleton instance of the command.  
  70.         /// </summary>  
  71.         /// <param name="package">Owner package, not null.</param>  
  72.         public static async Task InitializeAsync(AsyncPackage package)  
  73.         {  
  74.             // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires  
  75.             // the UI thread.  
  76.             await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);  
  77.   
  78.             OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;  
  79.             Instance = new SampleCommand(package, commandService);  
  80.         }  
  81.   
  82.         /// <summary>  
  83.         /// This function is the callback used to execute the command when the menu item is clicked.  
  84.         /// See the constructor to see how the menu item is associated with this function using  
  85.         /// OleMenuCommandService service and MenuCommand class.  
  86.         /// </summary>  
  87.         /// <param name="sender">Event sender.</param>  
  88.         /// <param name="e">Event args.</param>  
  89.         private void Execute(object sender, EventArgs e)  
  90.         {  
  91.             ThreadHelper.ThrowIfNotOnUIThread();  
  92.             string message = string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()"this.GetType().FullName);  
  93.             string title = "SampleCommand";  
  94.   
  95.             // Show a message box to prove we were here  
  96.             VsShellUtilities.ShowMessageBox(  
  97.                 this.package,  
  98.                 message,  
  99.                 title,  
  100.                 OLEMSGICON.OLEMSGICON_INFO,  
  101.                 OLEMSGBUTTON.OLEMSGBUTTON_OK,  
  102.                 OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);  
  103.         }  
  104.     }  
  105. }  
Vsct file created,
  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">      
  3.       
  4.   <!--    
  5.     This is the file that defines the actual layout and type of the commands.      
  6.         It is divided in different sections (e.g. command definition, command      
  7.         placement, ...), with each defining a specific set of properties.      
  8.         See the comment before each section for more details about how to      
  9.         use it.  
  10.   -->      
  11.       
  12.   <!--    
  13.     The VSCT compiler (the tool that translates this file into the binary      
  14.         format that VisualStudio will consume) has the ability to run a preprocessor      
  15.         on the vsct file; this preprocessor is (usually) the C++ preprocessor, so      
  16.         it is possible to define includes and macros with the same syntax used      
  17.         in C++ files. Using this ability of the compiler here, we include some files      
  18.         defining some of the constants that we will use inside the file.   
  19.   -->      
  20.       
  21.   <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->      
  22.   <Extern href="stdidcmd.h"/>      
  23.       
  24.   <!--This header contains the command ids for the menus provided by the shell. -->      
  25.   <Extern href="vsshlids.h"/>      
  26.       
  27.   <!--  
  28.     The Commands section is where commands, menus, and menu groups are defined.      
  29.       This section uses a Guid to identify the package that provides the command defined inside it.   
  30.   -->      
  31.   <Commands package="guidSampleVisualStudioExtensionPackage">      
  32.     <!--   
  33.     Inside this section we have different sub-sections: one for the menus, another      
  34.     for the menu groups, one for the buttons (the actual commands), one for the combos      
  35.     and the last one for the bitmaps used. Each element is identified by a command id that      
  36.     is a unique pair of guid and numeric identifier; the guid part of the identifier is usually      
  37.     called "command set" and is used to group different command inside a logically related      
  38.     group; your package should define its own command set in order to avoid collisions      
  39.     with command ids defined by other packages.   
  40.     -->      
  41.       
  42.     <!--   
  43.     In this section you can define new menu groups. A menu group is a container for      
  44.          other menus or buttons (commands); from a visual point of view you can see the      
  45.          group as the part of a menu contained between two lines. The parent of a group      
  46.          must be a menu.   
  47.     -->      
  48.     <Groups>      
  49.       <Group guid="guidSampleVisualStudioExtensionPackageCmdSet" id="MyMenuGroup" priority="0x0600">      
  50.         <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>      
  51.       </Group>      
  52.     </Groups>      
  53.       
  54.     <!--Buttons section. -->      
  55.     <!--  
  56.     This section defines the elements the user can interact with, like a menu command or a button      
  57.         or combo box in a toolbar.   
  58.     -->      
  59.     <Buttons>      
  60.       <!--  
  61.     To define a menu group you have to specify its ID, the parent menu and its display priority.      
  62.           The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use      
  63.           the CommandFlag node.      
  64.           You can add more than one CommandFlag node e.g.:      
  65.               <CommandFlag>DefaultInvisible</CommandFlag>      
  66.               <CommandFlag>DynamicVisibility</CommandFlag>      
  67.           If you do not want an image next to your command, remove the Icon node />   
  68.       -->      
  69.       <Button guid="guidSampleVisualStudioExtensionPackageCmdSet" id="SampleCommandId" priority="0x0100" type="Button">      
  70.         <Parent guid="guidSampleVisualStudioExtensionPackageCmdSet" id="MyMenuGroup" />      
  71.         <Icon guid="guidImages" id="bmpPic1" />      
  72.         <Strings>      
  73.           <ButtonText>Invoke SampleCommand</ButtonText>      
  74.         </Strings>      
  75.       </Button>      
  76.     </Buttons>      
  77.       
  78.     <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->      
  79.     <Bitmaps>      
  80.       <!--    
  81.         The bitmap id is defined in a way that is a little bit different from the others:      
  82.             the declaration starts with a guid for the bitmap strip, then there is the resource id of the      
  83.             bitmap strip containing the bitmaps and then there are the numeric ids of the elements used      
  84.             inside a button definition. An important aspect of this declaration is that the element id      
  85.             must be the actual index (1-based) of the bitmap inside the bitmap strip.   
  86.     -->      
  87.       <Bitmap guid="guidImages" href="Resources\SampleCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough"/>      
  88.     </Bitmaps>      
  89.   </Commands>      
  90.       
  91.   <Symbols>      
  92.     <!-- This is the package guid. -->      
  93.     <GuidSymbol name="guidSampleVisualStudioExtensionPackage" value="{c86c0c68-5ff5-43af-8a60-54540cbea3e2}" />      
  94.       
  95.     <!-- This is the guid used to group the menu commands together -->      
  96.     <GuidSymbol name="guidSampleVisualStudioExtensionPackageCmdSet" value="{f12cb590-7cad-4292-99cb-49d97f71e500}">      
  97.       <IDSymbol name="MyMenuGroup" value="0x1020" />      
  98.       <IDSymbol name="SampleCommandId" value="0x0100" />      
  99.     </GuidSymbol>      
  100.       
  101.     <GuidSymbol name="guidImages" value="{9dddc041-b350-494a-9e4b-8b89d67437aa}" >      
  102.       <IDSymbol name="bmpPic1" value="1" />      
  103.       <IDSymbol name="bmpPic2" value="2" />      
  104.       <IDSymbol name="bmpPicSearch" value="3" />      
  105.       <IDSymbol name="bmpPicX" value="4" />      
  106.       <IDSymbol name="bmpPicArrows" value="5" />      
  107.       <IDSymbol name="bmpPicStrikethrough" value="6" />      
  108.     </GuidSymbol>      
  109.   </Symbols>      
  110. </CommandTable>    
Creating your objects
 
Creating the menu item,
 
Vsct file
  1. <Menus>  
  2.     <Menu guid="guidSampleVisualStudioExtensionPackageCmdSet" id="SampleMenu" priority="0x700" type="Menu">  
  3.       <Parent guid="guidSHLMainMenu"  
  4.               id="IDG_VS_MM_TOOLSADDINS" />  
  5.       <Strings>  
  6.         <ButtonText>Sample Menu</ButtonText>  
  7.         <CommandName>Sample Menu</CommandName>  
  8.       </Strings>  
  9.     </Menu>  
  10. </Menus>  
  1. <Groups>  
  2.   <Group guid="guidSampleVisualStudioExtensionPackageCmdSet" id="MyMenuGroup" priority="0x0600">  
  3.     <Parent guid="guidSampleVisualStudioExtensionPackageCmdSet" id="SampleMenu"/>  
  4.   </Group>  
  5. </Groups>  
  1. <IDSymbol name="SampleMenu" value="0x1021"/>  
Customizing menu button text,
 
Vsct file
  1. <Button guid="guidSampleVisualStudioExtensionPackageCmdSet" id="SampleCommandId" priority="0x0100" type="Button">  
  2.    <Parent guid="guidSampleVisualStudioExtensionPackageCmdSet" id="MyMenuGroup" />  
  3.    <Icon guid="guidImages" id="bmpPic1" />  
  4.    <Strings>  
  5.      <ButtonText>Sample button text.</ButtonText>  
  6.    </Strings>  
  7. </Button>  
Customizing the output message,
 
Command Class
  1. private void Execute(object sender, EventArgs e)  
  2. {  
  3.     ThreadHelper.ThrowIfNotOnUIThread();  
  4.     string message = string.Format(CultureInfo.CurrentCulture, "Hello World! UTC Time now: {0}", DateTime.UtcNow);  
  5.     string title = "Sample title Command";  
  6.   
  7.     // Show a message box to prove we were here  
  8.     VsShellUtilities.ShowMessageBox(  
  9.         this.package,  
  10.         message,  
  11.         title,  
  12.         OLEMSGICON.OLEMSGICON_INFO,  
  13.         OLEMSGBUTTON.OLEMSGBUTTON_OK,  
  14.         OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);  
  15. }  

Debugging

 
Debugging Visual Studio extensions works like debugging any kind of project, as our final product is a Visual Studio extension so when we push F5 it is going to open a new Visual Studio window simulating to have the extension installed as follows:
 
Push F5
 
New Visual Studio Window result,
 
 
 
Extension Menu and items
 
 
 
Hitting break points
 

Publishing

 
it is free and anyone could publish your own extension at Visual Studio Marketplace.
 
Microsoft has a very nice tutorial explaining step-by-step on how to publish your Visual Studio extension into Visual Studio Marketplace here
 
Congratulations, you have successfully created your first Visual Studio 2019 extension. 
External References