Project Structure

A VSIX project contains several files that work together to define your extension. Let’s examine each one.

Project Files

.csproj File

The project file configures your extension build:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
    <GeneratePkgDefFile>true</GeneratePkgDefFile>
    <UseCodebase>true</UseCodebase>
    <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
    <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
    <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
    <CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
    <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Community.VisualStudio.Toolkit.17" Version="17.0.*" />
    <PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.0.*">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native</IncludeAssets>
    </PackageReference>
  </ItemGroup>
</Project>

Key properties:

  • TargetFramework - VS extensions use .NET Framework 4.8
  • GeneratePkgDefFile - Creates registry entries for your package
  • UseCodebase - Loads assemblies from the VSIX location

source.extension.vsixmanifest

The VSIX manifest describes your extension:

<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0"
                 xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
  <Metadata>
    <Identity Id="HelloWorldExtension.guid"
              Version="1.0"
              Language="en-US"
              Publisher="Your Name" />
    <DisplayName>Hello World Extension</DisplayName>
    <Description>A simple Hello World extension for Visual Studio.</Description>
    <Tags>hello, world, sample</Tags>
  </Metadata>
  <Installation>
    <InstallationTarget Id="Microsoft.VisualStudio.Community"
                        Version="[17.0, 18.0)">
      <ProductArchitecture>amd64</ProductArchitecture>
    </InstallationTarget>
  </Installation>
  <Dependencies>
    <Dependency Id="Microsoft.Framework.NDP"
                DisplayName="Microsoft .NET Framework"
                Version="[4.8,)" />
  </Dependencies>
  <Prerequisites>
    <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor"
                  Version="[17.0,18.0)"
                  DisplayName="Visual Studio core editor" />
  </Prerequisites>
  <Assets>
    <Asset Type="Microsoft.VisualStudio.VsPackage"
           Path="|%CurrentProject%;PkgdefProjectOutputGroup|" />
    <Asset Type="Microsoft.VisualStudio.MefComponent"
           Path="|%CurrentProject%|" />
  </Assets>
</PackageManifest>

Important sections:

  • Metadata - Extension name, description, and tags for Marketplace
  • Installation - Which VS versions and editions are supported
  • Prerequisites - Required VS components
  • Assets - Files included in the VSIX
Note

The version range [17.0, 18.0) means VS 2022 (any version from 17.0 up to but not including 18.0).

Command Files

VSCommandTable.vsct

The Visual Studio Command Table defines menus, groups, and commands:

<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable">
  <Extern href="stdidcmd.h"/>
  <Extern href="vsshlids.h"/>
  <Include href="KnownImageIds.vsct"/>

  <Commands package="guidMyPackage">
    <Groups>
      <!-- Command group definitions -->
    </Groups>
    <Buttons>
      <!-- Command button definitions -->
    </Buttons>
    <Bitmaps>
      <!-- Image definitions -->
    </Bitmaps>
  </Commands>

  <Symbols>
    <!-- GUID and ID definitions -->
  </Symbols>
</CommandTable>

This file is compiled into a .ctmenu file during build.

VSCommandTable.cs

Auto-generated constants from your .vsct file:

namespace HelloWorldExtension
{
    internal sealed partial class PackageGuids
    {
        public const string guidHelloWorldExtensionPackageString = "...";
        public static Guid guidHelloWorldExtensionPackage = new Guid(guidHelloWorldExtensionPackageString);
    }

    internal sealed partial class PackageIds
    {
        public const int MyCommand = 0x0100;
        public const int MyMenuGroup = 0x1020;
    }
}
Tip

Don’t edit this file manually - it’s regenerated on every build from the .vsct file.

Package File

YourPackage.cs

The package class is your extension’s entry point:

[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(PackageGuids.HelloWorldExtensionString)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class HelloWorldExtensionPackage : ToolkitPackage
{
    protected override async Task InitializeAsync(
        CancellationToken cancellationToken,
        IProgress<ServiceProgressData> progress)
    {
        await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
        await this.RegisterCommandsAsync();
    }
}

Common attributes:

  • PackageRegistration - Registers the package
  • ProvideMenuResource - Links to command table
  • ProvideAutoLoad - Auto-loads the package in specific contexts

Output Files

When you build, these files are generated:

FileDescription
bin/Debug/YourExtension.vsixThe deployable extension package
bin/Debug/YourExtension.dllYour compiled code
bin/Debug/YourExtension.pkgdefRegistry configuration
bin/Debug/extension.vsixmanifestProcessed manifest

Next Steps

Learn how to Debug Your Extension using the Experimental Instance.