Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
After creating your first extension, let's understand the key parts of the project.
Project structure
A typical VisualStudio.Extensibility extension project looks like this:
MyExtension/
├── MyExtension.csproj
├── ExtensionEntrypoint.cs ← Extension class (entry point)
├── Commands/
│ └── Command1.cs ← Command handlers
├── ToolWindows/ ← Tool window definitions
├── .vsextension/
│ └── string-resources.json ← Localized strings
└── bin/Debug/.vsextension/
└── extension.json ← Generated build output (metadata)
The Extension class
Every extension has one class that inherits from Extension. This is your entry point:
[VisualStudioContribution]
internal class ExtensionEntrypoint : Extension
{
public override ExtensionConfiguration ExtensionConfiguration => new()
{
Metadata = new(
id: "MyExtension.abc123",
version: this.ExtensionAssemblyVersion,
publisherName: "My Company",
displayName: "My Extension"),
};
}
The [VisualStudioContribution] attribute tells Visual Studio this class exists and should be loaded.
Commands
Commands are the most common contribution. Each command is a class:
[VisualStudioContribution]
internal class MyCommand : Command
{
public override CommandConfiguration CommandConfiguration => new("My Command")
{
Placements = [CommandPlacement.KnownPlacements.ToolsMenu],
};
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken ct)
{
// Your logic here
}
}
Key concepts
| Concept | What it does |
|---|---|
[VisualStudioContribution] |
Registers a class as a VS contribution (command, tool window, etc.) |
CommandConfiguration |
Declares where a command appears, its icon, and visibility rules |
IClientContext |
Provides access to the current IDE state when a command executes |
Extensibility property |
Access to VS services (shell, editor, documents) |
Extension class |
The entry point that defines metadata for your extension |
How it connects
Visual Studio Your Extension (separate process)
┌─────────────────────┐ ┌─────────────────────────────┐
│ │ ← JSON → │ ExtensionEntrypoint │
│ Loads │ │ │
│ extension.json │ ← Commands →│ MyCommand.ExecuteCommandAsync()│
│ │ │ │
│ Menu click ─────────│────────────>│ │
│ │ │ │
│ Tool window <───────│─────────────│ RemoteUserControl │
│ │ ← UI → │ (XAML + ViewModel) │
└─────────────────────┘ └─────────────────────────────┘
Your extension runs in its own process. Visual Studio communicates with it via JSON-RPC. This isolates extension process crashes, though extension bugs can still affect the IDE.
Next steps
- Add a command — the most common extension feature
- Tool windows — add custom UI
- Activation constraints — control when your extension is active
- Dependency injection — how services are provided