### Complete Visual Studio Extension Package Example Source: https://vsix.guide/packages A comprehensive example demonstrating package registration, GUID, menu resource, auto-load, options page, tool window, and service registration with asynchronous initialization. ```csharp [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(PackageGuids.MyPackageString)] [ProvideMenuResource("Menus.ctmenu", 1)] [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)] [ProvideOptionPage(typeof(GeneralOptionsPage), "My Extension", "General", 0, 0, true)] [ProvideToolWindow(typeof(MyToolWindow))] [ProvideService(typeof(SMyService), IsAsyncQueryable = true)] public sealed class MyPackage : ToolkitPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // Add custom service AddService(typeof(SMyService), CreateServiceAsync, true); // Register commands await this.RegisterCommandsAsync(); } private async Task CreateServiceAsync( IAsyncServiceContainer container, CancellationToken ct, Type serviceType) { return new MyService(); } } ``` -------------------------------- ### Complete Command Example with Dynamic Visibility and Formatting Source: https://vsix.guide/commands An example of a command that dynamically controls its visibility based on the active document type and performs text formatting. It demonstrates BeforeQueryStatus for visibility and ExecuteAsync for action. ```csharp [Command(PackageIds.FormatDocumentCommand)] internal sealed class FormatDocumentCommand : BaseCommand { protected override void BeforeQueryStatus(EventArgs e) { // Only show for text files var doc = VS.Documents.GetActiveDocumentViewAsync().Result; Command.Visible = doc?.FilePath?.EndsWith(".txt") == true; Command.Enabled = doc?.TextView != null; } protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) { var docView = await VS.Documents.GetActiveDocumentViewAsync(); if (docView?.TextView == null) return; // Get the text buffer var textBuffer = docView.TextView.TextBuffer; using (var edit = textBuffer.CreateEdit()) { var text = edit.Snapshot.GetText(); var formatted = FormatText(text); edit.Replace(0, text.Length, formatted); edit.Apply(); } await VS.StatusBar.ShowMessageAsync("Document formatted!"); } private string FormatText(string text) { // Your formatting logic return text.Trim(); } } ``` -------------------------------- ### Install SDK NuGet Package Source: https://vsix.guide/sdk-tools Example of how to install the Microsoft.VSSDK.BuildTools NuGet package in a Visual Studio project. This package provides build tools for VSIX extensions. ```xml all runtime; build; native ``` -------------------------------- ### Install VSIX Extension Source: https://vsix.guide/sdk-tools Use VsixInstaller.exe to install, uninstall, or manage VSIX extensions from the command line. Specify /quiet for silent installation. ```bash VsixInstaller.exe /quiet MyExtension.vsix ``` ```bash VsixInstaller.exe /uninstall:MyExtension.UniqueGuid ``` ```bash VsixInstaller.exe /rootSuffix:Exp MyExtension.vsix ``` -------------------------------- ### Complete Image Manifest Example Source: https://vsix.guide/file-icons An example of a complete .imagemanifest file defining symbols and image sources for custom file types, including theme variants. ```xml ``` -------------------------------- ### Complete InfoBar Example with Actions Source: https://vsix.guide/infobars An example of an InfoBar that suggests enabling a feature, including hyperlinks for enabling, learning more, and dismissing. It handles user interactions with action items and provides a complete implementation for managing feature suggestions. ```csharp public class FeatureSuggestionService { private IVsInfoBarUIElement _currentInfoBar; private const string SettingKey = "FeatureXEnabled"; public async Task SuggestFeatureAsync() { // Don't show if already enabled or dismissed if (await IsFeatureEnabledAsync() || await WasDismissedAsync()) return; var model = new InfoBarModel( new IVsInfoBarTextSpan[] { new InfoBarTextSpan("Feature X can improve your productivity. "), new InfoBarHyperlink("Enable", "enable"), new InfoBarTextSpan(" | "), new InfoBarHyperlink("Learn More", "learn"), new InfoBarTextSpan(" | "), new InfoBarHyperlink("Don't show again", "dismiss") }, KnownMonikers.StatusInformation, isCloseButtonVisible: true); var infoBar = await VS.InfoBar.CreateAsync(model); infoBar.ActionItemClicked += async (sender, args) => { var action = args.ActionItem.ActionContext as string; switch (action) { case "enable": await EnableFeatureAsync(); await VS.StatusBar.ShowMessageAsync("Feature X enabled!"); args.InfoBarUIElement.Close(); break; case "learn": await VS.Commands.ExecuteAsync( "View.WebBrowser", "https://example.com/feature-x"); break; case "dismiss": await SaveDismissedAsync(); args.InfoBarUIElement.Close(); break; } }; if (await infoBar.TryShowInfoBarUIAsync()) { // Track so we can close programmatically if needed _currentInfoBar = null; // Get from event if needed } } private Task IsFeatureEnabledAsync() => Task.FromResult(false); private Task WasDismissedAsync() => Task.FromResult(false); private Task EnableFeatureAsync() => Task.CompletedTask; private Task SaveDismissedAsync() => Task.CompletedTask; } ``` -------------------------------- ### Complete Tool Window Example Source: https://vsix.guide/tool-windows A comprehensive example of a Visual Studio tool window, including its C# code and associated control. ```csharp // MyToolWindow.cs [Guid("12345678-1234-1234-1234-123456789012")] public class MyToolWindow : BaseToolWindow { public override string GetTitle(int toolWindowId) => "Solution Explorer+"; public override Type PaneType => typeof(Pane); public override async Task CreateAsync(int toolWindowId, CancellationToken ct) { var solution = await VS.Solutions.GetCurrentSolutionAsync(); return new SolutionExplorerControl(solution); } internal class Pane : ToolWindowPane { public Pane() { BitmapImageMoniker = KnownMonikers.Solution; ToolBar = new CommandID(PackageGuids.guidMyPackageCmdSet, PackageIds.SolutionToolbar); } } } // SolutionExplorerControl.xaml.cs public partial class SolutionExplorerControl : UserControl { public SolutionExplorerControl(Solution solution) { InitializeComponent(); DataContext = new SolutionViewModel(solution); } } ``` -------------------------------- ### Complete Reusable Logging Service Example Source: https://vsix.guide/output-window A comprehensive C# example demonstrating a thread-safe logging service for Visual Studio extensions, including methods for logging messages, warnings, errors, and clearing the pane. It also shows how to initialize and use the logger within a ToolkitPackage. ```csharp using System; using System.Threading.Tasks; using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio.Shell; public interface IExtensionLogger { Task LogAsync(string message); Task LogWarningAsync(string message); Task LogErrorAsync(string message); Task ClearAsync(); } public class ExtensionLogger : IExtensionLogger { private readonly string _paneName; private OutputWindowPane _pane; public ExtensionLogger(string paneName) { _paneName = paneName; } private async Task EnsurePaneAsync() { _pane ??= await VS.Windows.CreateOutputWindowPaneAsync(_paneName); } public async Task LogAsync(string message) { await EnsurePaneAsync(); await _pane.WriteLineAsync($"[{DateTime.Now:HH:mm:ss}] {message}"); } public async Task LogWarningAsync(string message) { await EnsurePaneAsync(); await _pane.WriteLineAsync($"[{DateTime.Now:HH:mm:ss}] ⚠ WARNING: {message}"); } public async Task LogErrorAsync(string message) { await EnsurePaneAsync(); await _pane.WriteLineAsync($"[{DateTime.Now:HH:mm:ss}] ❌ ERROR: {message}"); await _pane.ActivateAsync(); } public async Task ClearAsync() { await EnsurePaneAsync(); await _pane.ClearAsync(); } } // Usage in your package public sealed class MyPackage : ToolkitPackage { public static IExtensionLogger Logger { get; private set; } protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { await base.InitializeAsync(cancellationToken, progress); Logger = new ExtensionLogger("My Extension"); await Logger.LogAsync("Extension loaded successfully"); } } // Usage anywhere in your extension await MyPackage.Logger.LogAsync("Processing file..."); await MyPackage.Logger.LogErrorAsync("Failed to process file"); ``` -------------------------------- ### Installation Element with Target Products Source: https://vsix.guide/vsix-manifest Specifies installation details, including whether it's installed by MSI and for all users. It defines target Visual Studio editions and their version ranges. ```xml amd64 ``` -------------------------------- ### Set Up LSP Client in Visual Studio Source: https://vsix.guide/language-services Implement the ILanguageClient interface to connect Visual Studio to a Language Server Protocol (LSP) server. Ensure the 'Microsoft.VisualStudio.LanguageServer.Client' NuGet package is installed. ```csharp [Export(typeof(ILanguageClient))] [ContentType("myLanguage")] public class MyLanguageClient : ILanguageClient { public string Name => "My Language Server"; public IEnumerable ConfigurationSections => null; public object InitializationOptions => null; public IEnumerable FilesToWatch => null; public event AsyncEventHandler StartAsync; public event AsyncEventHandler StopAsync; public async Task ActivateAsync(CancellationToken token) { var serverPath = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "server", "myLanguageServer.exe"); var info = new ProcessStartInfo { FileName = serverPath, RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; var process = Process.Start(info); return new Connection( process.StandardOutput.BaseStream, process.StandardInput.BaseStream); } public async Task OnLoadedAsync() { await StartAsync.InvokeAsync(this, EventArgs.Empty); } public Task OnServerInitializedAsync() { return Task.CompletedTask; } public Task OnServerInitializeFailedAsync( ILanguageClientInitializationInfo initializationState) { return Task.FromResult(new InitializationFailureContext { FailureMessage = "Failed to initialize language server" }); } } ``` -------------------------------- ### Perform a Search Operation with Status Bar Updates Source: https://vsix.guide/status-bar This example demonstrates how to use the status bar to provide feedback during a search operation, including starting an animation, showing messages, and clearing the status bar afterwards. It handles cancellation and ensures animations are stopped. ```csharp public async Task> SearchAsync(string query, CancellationToken ct) { var results = new List(); try { await VS.StatusBar.StartAnimationAsync(StatusAnimation.Find); await VS.StatusBar.ShowMessageAsync($"Searching for '{query}'..."); results = await PerformSearchAsync(query, ct); await VS.StatusBar.ShowMessageAsync($"Found {results.Count} results"); } catch (OperationCanceledException) { await VS.StatusBar.ShowMessageAsync("Search canceled"); } finally { await VS.StatusBar.EndAnimationAsync(StatusAnimation.Find); await Task.Delay(3000); await VS.StatusBar.ClearAsync(); } return results; } ``` -------------------------------- ### Install VSIX Viewer Source: https://vsix.guide/prerequisites Install the VSIX Viewer tool to inspect the contents of .vsix packages. This can be done via the Visual Studio Marketplace or directly through the Extensions menu in Visual Studio. ```shell # Install from Visual Studio Marketplace or via Extensions menu ``` -------------------------------- ### Project Template Structure Source: https://vsix.guide/sdk-tools Example structure for a Visual Studio project template, including the .vstemplate file, project file, source files, and icon. ```plaintext MyTemplate/ ├── MyTemplate.vstemplate ├── MyProject.csproj ├── Program.cs └── __TemplateIcon.ico ``` -------------------------------- ### Complete TextMate Grammar Example Source: https://vsix.guide/textmate-grammars A comprehensive example of a TextMate grammar definition in JSON format, including repository definitions for comments, keywords, strings, numbers, and functions. ```json { "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "MyLanguage", "scopeName": "source.mylang", "fileTypes": ["mylang"], "patterns": [ { "include": "#comments" }, { "include": "#keywords" }, { "include": "#strings" }, { "include": "#numbers" }, { "include": "#functions" } ], "repository": { "comments": { "patterns": [ { "name": "comment.line.mylang", "match": "#.*$" } ] }, "keywords": { "patterns": [ { "name": "keyword.control.mylang", "match": "\\b(if|else|elif|for|while|return|break|continue)\\b" }, { "name": "storage.type.mylang", "match": "\\b(func|var|const|class|struct)\\b" }, { "name": "constant.language.mylang", "match": "\\b(true|false|null)\\b" } ] }, "strings": { "patterns": [ { "name": "string.quoted.double.mylang", "begin": "\"", "end": "\"", "patterns": [ { "name": "constant.character.escape.mylang", "match": "\\\\[\\\\\"nrt]" } ] }, { "name": "string.quoted.single.mylang", "begin": "'", "end": "'" } ] }, "numbers": { "patterns": [ { "name": "constant.numeric.hex.mylang", "match": "\\b0x[0-9a-fA-F]+\\b" }, { "name": "constant.numeric.mylang", "match": "\\b[0-9]+(\\.[0-9]+)?\\b" } ] }, "functions": { "patterns": [ { "match": "\\b(func)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", "captures": { "1": { "name": "storage.type.function.mylang" }, "2": { "name": "entity.name.function.mylang" } } } ] } } } ``` -------------------------------- ### Find and Activate Any Tool Window by GUID Source: https://vsix.guide/tool-window-guids This method finds and shows any tool window using its GUID. It ensures the operation is performed on the main thread and forces creation if the window is not already open. ```csharp public static async Task ShowToolWindowAsync(Guid toolWindowGuid) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var shell = await VS.Services.GetUIShellAsync(); shell.FindToolWindow( (uint)__VSFINDTOOLWIN.FTW_fForceCreate, ref toolWindowGuid, out var frame); frame?.Show(); } ``` -------------------------------- ### Command Line VSIX Installation and Uninstallation Source: https://vsix.guide/packaging Automate the installation or uninstallation of your VSIX package using VSIXInstaller.exe. Ensure the correct path to VSIXInstaller.exe and the extension's unique identifier are used. ```batch # Install "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VSIXInstaller.exe" /quiet YourExtension.vsix # Uninstall "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VSIXInstaller.exe" /uninstall:YourExtension.UniqueGuid ``` -------------------------------- ### Display InfoBar in a Tool Window Source: https://vsix.guide/infobars This example demonstrates how to display an InfoBar within a specific tool window. You need to pass an instance of your tool window to the CreateAsync method. ```csharp // In your tool window class var model = new InfoBarModel("Update available for this extension."); var infoBar = await VS.InfoBar.CreateAsync(MyToolWindow, model); await infoBar.TryShowInfoBarUIAsync(); ``` -------------------------------- ### Diagnostic Logging with TraceSource Source: https://vsix.guide/advanced-overview Implement diagnostic logging for troubleshooting advanced extensions using `TraceSource`. This example shows how to log information and error events. ```csharp private static readonly TraceSource _trace = new TraceSource("MyExtension"); public async Task DoWorkAsync() { _trace.TraceEvent(TraceEventType.Information, 0, "Starting work..."); try { await PerformWorkAsync(); _trace.TraceEvent(TraceEventType.Information, 0, "Work completed."); } catch (Exception ex) { _trace.TraceEvent(TraceEventType.Error, 0, $"Error: {ex}"); throw; } } ``` -------------------------------- ### Item Template Structure Source: https://vsix.guide/sdk-tools Example structure for a Visual Studio item template, including the .vstemplate file, source file, and icon. ```plaintext MyItemTemplate/ ├── MyItemTemplate.vstemplate ├── MyClass.cs └── __TemplateIcon.ico ``` -------------------------------- ### Implement Language Client with LSP Source: https://vsix.guide/advanced-overview Export a language client for cross-editor compatibility using the Language Service Protocol. This example shows how to activate the language server and establish a connection. ```csharp [Export(typeof(ILanguageClient))] [ContentType("myLanguage")] public class MyLanguageClient : ILanguageClient { public string Name => "My Language Server"; public async Task ActivateAsync(CancellationToken token) { var process = StartLanguageServer(); return new Connection(process.StandardOutput.BaseStream, process.StandardInput.BaseStream); } } ``` -------------------------------- ### Prerequisites Element with Component Requirements Source: https://vsix.guide/vsix-manifest Lists prerequisites that must be installed with Visual Studio, such as the core editor or specific compilers. Version ranges are specified for compatibility. ```xml ``` -------------------------------- ### Write to Built-in General Pane (Community Toolkit) Source: https://vsix.guide/output-window A concise example using the Community Toolkit to write a message directly to the built-in General output pane. ```csharp // Community Toolkit await VS.Windows.WriteToOutputWindowAsync("Message to General pane"); ``` -------------------------------- ### Text View Creation Listener for C# Editor Source: https://vsix.guide/mef-components An example of implementing `IWpfTextViewCreationListener` to react when a C# editor is created. It demonstrates importing services like `IClassificationFormatMapService` and `IEditorFormatMapService`. ```csharp [Export(typeof(IWpfTextViewCreationListener))] [ContentType("CSharp")] [TextViewRole(PredefinedTextViewRoles.Document)] internal sealed class CSharpEditorListener : IWpfTextViewCreationListener { [Import] internal IClassificationFormatMapService FormatMapService { get; set; } [Import] internal IEditorFormatMapService EditorFormatMapService { get; set; } public void TextViewCreated(IWpfTextView textView) { // Called when a C# document editor is created textView.Properties.GetOrCreateSingletonProperty( () => new CSharpEditorEnhancement(textView, FormatMapService)); } } ``` -------------------------------- ### Minimal Package Implementation Source: https://vsix.guide/packages A basic C# class implementing the AsyncPackage base class for Visual Studio extensions. Requires a unique GUID and can be configured for managed resources and background loading. ```csharp [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid("your-unique-guid-here")] public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // Initialization code here } } ``` -------------------------------- ### Dynamic Visibility with OleMenuCommand Source: https://vsix.guide/context-menus Implement dynamic visibility for traditional OleMenuCommands by subscribing to the BeforeQueryStatus event. This example shows how to show the command only for single selections in Solution Explorer. ```csharp private void InitializeCommand() { var commandId = new CommandID(PackageGuids.guidMyPackageCmdSet, PackageIds.MyCommand); var command = new OleMenuCommand(Execute, commandId); command.BeforeQueryStatus += OnBeforeQueryStatus; var commandService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); commandService.AddCommand(command); } private void OnBeforeQueryStatus(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); var command = (OleMenuCommand)sender; // Get selected items in Solution Explorer var monitorSelection = (IVsMonitorSelection)GetService(typeof(SVsShellMonitorSelection)); monitorSelection.GetCurrentSelection( out IntPtr hierarchyPtr, out uint itemId, out IVsMultiItemSelect multiSelect, out IntPtr containerPtr); // Show only for single selection command.Visible = multiSelect == null && hierarchyPtr != IntPtr.Zero; } ``` -------------------------------- ### Project Setup for Grammar File Inclusion Source: https://vsix.guide/textmate-grammars MSBuild XML snippet for configuring a grammar file to be included in a VSIX project, setting build actions and output directory properties. ```xml true PreserveNewest ``` -------------------------------- ### HelloWorldExtensionPackage.cs Entry Point Source: https://vsix.guide/first-extension This is the extension's entry point, responsible for package registration and initialization. It links to the command table and registers commands. ```csharp using System; using System.Runtime.InteropServices; using System.Threading; using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio.Shell; using Task = System.Threading.Tasks.Task; namespace HelloWorldExtension { [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(PackageGuids.HelloWorldExtensionString)] [ProvideMenuResource("Menus.ctmenu", 1)] public sealed class HelloWorldExtensionPackage : ToolkitPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { await this.RegisterCommandsAsync(); } } } ``` -------------------------------- ### Retrieve Icons Programmatically with IVsImageService2 Source: https://vsix.guide/file-icons Use IVsImageService2 to get icon images programmatically. This example shows how to retrieve the 'Document' moniker and cast its data to a BitmapSource. ```csharp var imageService = await VS.GetServiceAsync(); // Get image attributes var attributes = new ImageAttributes { StructSize = Marshal.SizeOf(typeof(ImageAttributes)), ImageType = (uint)_UIImageType.IT_Bitmap, Format = (uint)_UIDataFormat.DF_WPF, LogicalWidth = 16, LogicalHeight = 16, Flags = (uint)_ImageAttributesFlags.IAF_RequiredFlags }; // Get the image IVsUIObject uiObject = imageService.GetImage(KnownMonikers.Document, attributes); uiObject.get_Data(out object data); var bitmapSource = data as BitmapSource; ``` -------------------------------- ### Complete VSCT File for Context Menu Command Source: https://vsix.guide/context-menus A full example of a VSCT file defining a command group and a button, parented to the Solution Explorer's solution node context menu. Includes necessary GUID and ID symbols. ```xml ``` -------------------------------- ### Implement Quick Info Source Provider Source: https://vsix.guide/mef-components Provides hover-over tooltips for specific keywords or code elements. Customize the logic in `GetQuickInfoItemAsync` to define what information is displayed. ```csharp [Export(typeof(IAsyncQuickInfoSourceProvider))] [Name("myLanguageQuickInfo")] [ContentType("myLanguage")] internal sealed class MyQuickInfoSourceProvider : IAsyncQuickInfoSourceProvider { public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) { return new MyQuickInfoSource(textBuffer); } } internal sealed class MyQuickInfoSource : IAsyncQuickInfoSource { private readonly ITextBuffer _buffer; public MyQuickInfoSource(ITextBuffer buffer) { _buffer = buffer; } public async Task GetQuickInfoItemAsync( IAsyncQuickInfoSession session, CancellationToken cancellationToken) { var triggerPoint = session.GetTriggerPoint(_buffer.CurrentSnapshot); if (triggerPoint == null) return null; var line = triggerPoint.Value.GetContainingLine(); var lineText = line.GetText(); // Example: show info for specific words var word = GetWordAtPoint(triggerPoint.Value); if (word == "myKeyword") { var span = new TrackingSpan(...); return new QuickInfoItem(span, new ContainerElement(ContainerElementStyle.Stacked, new ClassifiedTextElement( new ClassifiedTextRun(PredefinedClassificationTypeNames.Keyword, "myKeyword"), new ClassifiedTextRun(PredefinedClassificationTypeNames.Text, " - A special keyword")))); } return null; } public void Dispose() { } } ``` -------------------------------- ### Guid Attribute for Package Identification Source: https://vsix.guide/packages Assigns a unique GUID to the package, essential for Visual Studio to identify and manage it. Generate new GUIDs using Tools > Create GUID in Visual Studio. ```csharp [Guid("12345678-1234-1234-1234-123456789012")] ``` -------------------------------- ### Asynchronous Package Loading (Good) Source: https://vsix.guide/async-services Implement async loading by inheriting from `AsyncPackage`. This allows extensions to initialize in the background, improving Visual Studio startup performance. ```csharp // Good: Doesn't block VS startup public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // This runs in the background await DoExpensiveWorkAsync(); // Switch to main thread only when needed await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); RegisterCommands(); } } ``` -------------------------------- ### Async Package Loading with Background Loading Source: https://vsix.guide/performance Use AsyncPackage with AllowsBackgroundLoading = true for background initialization. Switch to the main thread only when necessary for UI operations. ```csharp // Good: Async package with background loading [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // Do background work first var config = await LoadConfigAsync(); // Only switch to main thread when necessary await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); InitializeUI(config); } } ``` -------------------------------- ### Complete Async Package Example Source: https://vsix.guide/async-services This C# code demonstrates a complete asynchronous Visual Studio package. It includes background loading, service registration, and phased initialization. Ensure `VSConstants` and `PackageGuids` are correctly defined in your project. ```csharp [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(PackageGuids.MyPackageString)] [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)] [ProvideService(typeof(SMyService), IsAsyncQueryable = true)] public sealed class MyPackage : AsyncPackage { private IMyService _service; private CancellationTokenSource _cts; protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // Phase 1: Background initialization progress.Report(new ServiceProgressData("Initializing service...", 1, 3)); _service = new MyService(); await _service.InitializeAsync(_cts.Token); AddService(typeof(SMyService), (_, _, _) => Task.FromResult(_service), true); // Phase 2: Wait for VS to be ready progress.Report(new ServiceProgressData("Waiting for solution...", 2, 3)); await KnownUIContexts.SolutionExistsAndFullyLoadedContext.WaitForActivationAsync(); // Phase 3: Main thread initialization progress.Report(new ServiceProgressData("Registering commands...", 3, 3)); await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await this.RegisterCommandsAsync(); // Start background processing _ = ProcessSolutionInBackgroundAsync(_cts.Token); } private async Task ProcessSolutionInBackgroundAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await _service.ProcessAsync(cancellationToken); await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken); } } catch (OperationCanceledException) { // Expected during shutdown } } protected override void Dispose(bool disposing) { if (disposing) { _cts?.Cancel(); _cts?.Dispose(); (_service as IDisposable)?.Dispose(); } base.Dispose(disposing); } } ``` -------------------------------- ### Service Registration Source: https://vsix.guide/registry Register a Visual Studio service with its GUID and display name. The package GUID is also specified. ```ini [$RootKey$\\Services\{ServiceGUID}] @="{PackageGUID}" "Name"="Service Display Name" ``` -------------------------------- ### Synchronous Package Loading (Bad) Source: https://vsix.guide/async-services Avoid this pattern as it blocks Visual Studio startup. The `Initialize` method runs on the main thread, preventing UI interaction during initialization. ```csharp // Bad: Blocks VS startup public sealed class MyPackage : Package { protected override void Initialize() { // This runs on the main thread during VS startup base.Initialize(); DoExpensiveWork(); // Blocks the UI! } } ``` -------------------------------- ### Language GUIDs for Visual Studio Source: https://vsix.guide/interfaces These GUIDs identify different programming languages within the Visual Studio environment. They are used for language-specific services and features. ```csharp guidCLang // C language guidCPPLang // C++ language guidVBLang // VB.NET language guidCSharpLang // C# language (implicit) guidFSharpLang // F# language guidSQLLang // SQL language guidJScriptLang // JScript language guidVBScriptLang // VBScript language ``` -------------------------------- ### ProvideService and Service Registration Source: https://vsix.guide/packages Demonstrates how a package can provide a service to other extensions using the ProvideService attribute and registering a factory method for creating the service instance. IsAsyncQueryable should be set to true for asynchronous service creation. ```csharp [ProvideService(typeof(SMyService), IsAsyncQueryable = true)] public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync(...) { // Add service to the container AddService(typeof(SMyService), CreateMyServiceAsync, true); } private async Task CreateMyServiceAsync( IAsyncServiceContainer container, CancellationToken ct, Type serviceType) { await JoinableTaskFactory.SwitchToMainThreadAsync(ct); return new MyService(); } } ``` -------------------------------- ### Show and Activate Output Window Panes Source: https://vsix.guide/output-window Demonstrates how to programmatically show the Output window and activate a specific pane using both the Community Toolkit and traditional Visual Studio interfaces. ```csharp // Show the Output window (bring to front) await VS.Windows.ShowOutputWindowAsync(); // Activate your specific pane await pane.ActivateAsync(); ``` ```csharp // Traditional approach var outputWindow = (IVsOutputWindow)GetService(typeof(SVsOutputWindow)); outputWindow.GetPane(ref paneGuid, out var pane); pane.Activate(); ``` ```csharp // Show Output window via UI shell var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); var outputWindowGuid = new Guid("{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}"); uiShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fForceCreate, ref outputWindowGuid, out var frame); frame.Show(); ``` -------------------------------- ### Show InfoBar Using IVsInfoBarUIFactory Source: https://vsix.guide/infobars Demonstrates the traditional approach to showing an InfoBar by directly using the `IVsInfoBarUIFactory`. This method requires obtaining the factory and host services and manually creating and adding the InfoBar UI element. ```csharp public class InfoBarService { private readonly IServiceProvider _serviceProvider; public InfoBarService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task ShowInfoBarAsync(IVsWindowFrame frame, string message) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var factory = (IVsInfoBarUIFactory)_serviceProvider.GetService(typeof(SVsInfoBarUIFactory)); var host = frame as IVsInfoBarHost; if (factory == null || host == null) return; var model = new InfoBarModel( new[] { new InfoBarTextSpan(message) }, KnownMonikers.StatusInformation, isCloseButtonVisible: true); var uiElement = factory.CreateInfoBar(model); uiElement.Advise(new InfoBarEvents(), out _); host.AddInfoBar(uiElement); } } public class InfoBarEvents : IVsInfoBarUIEvents { public void OnClosed(IVsInfoBarUIElement infoBarUIElement) { // InfoBar was closed } public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem) { ThreadHelper.ThrowIfNotOnUIThread(); var context = actionItem.ActionContext as string; // Handle action... } } ``` -------------------------------- ### Property Filter GUIDs for Debugging Source: https://vsix.guide/interfaces These GUIDs are used with the IDebugProperty2 interface to filter the properties displayed during debugging. They allow you to specify categories like locals, arguments, or registers. ```csharp guidFilterLocals // Local variables guidFilterAllLocals // All locals guidFilterArgs // Function arguments guidFilterLocalsPlusArgs // Locals and arguments guidFilterRegisters // CPU registers guidFilterThis // 'this' pointer ``` -------------------------------- ### Manage Experimental Instances Source: https://vsix.guide/sdk-tools CreateExpInstance.exe is used to manage experimental instances of Visual Studio. Use /Reset to revert to default settings or /Create to set up a new instance. ```bash CreateExpInstance.exe /Reset /VSInstance=17.0 /RootSuffix=Exp ``` ```bash CreateExpInstance.exe /Create /VSInstance=17.0 /RootSuffix=MyTest ``` -------------------------------- ### Debug Engine GUIDs Source: https://vsix.guide/interfaces These GUIDs identify different types of debug engines available in Visual Studio. They are used to specify which engine to use for debugging various types of code. ```csharp guidNativeOnlyEng // Native-only engine guidCOMPlusOnlyEng // Managed-only engine guidCOMPlusNativeEng // Mixed-mode engine guidScriptEng // Script engine guidSQLEng // SQL engine guidCoreSystemClrEng // Core CLR engine ``` -------------------------------- ### Visual Studio SDK COM Interop Example Source: https://vsix.guide/advanced-overview Demonstrates direct COM interop with the Visual Studio SDK, specifically accessing the Running Document Table to enumerate open documents. ```csharp // Get the running object table IVsRunningDocumentTable rdt = await VS.Services.GetRunningDocumentTableAsync(); // Enumerate open documents rdt.GetRunningDocumentsEnum(out IEnumRunningDocuments enumDocs); uint[] cookies = new uint[1]; while (enumDocs.Next(1, cookies, out uint fetched) == VSConstants.S_OK && fetched == 1) { rdt.GetDocumentInfo(cookies[0], out uint flags, out uint readLocks, out uint editLocks, out string moniker, out IVsHierarchy hierarchy, out uint itemId, out IntPtr docData); } ``` -------------------------------- ### UI Context Activation with WaitForActivationAsync Source: https://vsix.guide/async-services Defer package initialization until specific UI contexts are active, such as when a solution is loaded. This ensures necessary services are available. ```csharp [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)] public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // Wait for solution to be fully loaded await KnownUIContexts.SolutionExistsAndFullyLoadedContext.WaitForActivationAsync(); // Now safe to access solution services var solution = await VS.Solutions.GetCurrentSolutionAsync(); } } ``` -------------------------------- ### Configuring Debug Launch Settings in .csproj Source: https://vsix.guide/debugging Configure your project's `.csproj` file to specify the debug executable and arguments for launching the Experimental Instance. ```xml Program $(DevEnvDir)devenv.exe /rootsuffix Exp ``` -------------------------------- ### Menu Registration Source: https://vsix.guide/registry Register menus and commands for Visual Studio extensions. Specifies the package GUID and command set/ID. ```ini [$RootKey$\\Menus] "{PackageGUID}"=", 1000, 1" [$RootKey$\\Commands\{CommandSetGUID}\\{CommandID}] "Package"="{PackageGUID}" "CommandFlags"=dword:00000000 ``` -------------------------------- ### Write to Built-in Debug Pane (Traditional) Source: https://vsix.guide/output-window Illustrates writing a message to the built-in Debug output pane using its GUID and `OutputStringThreadSafe`. ```csharp var debugPaneGuid = VSConstants.GUID_OutWindowDebugPane; outputWindow.GetPane(ref debugPaneGuid, out var debugPane); debugPane.OutputStringThreadSafe("Debug message\n"); ``` -------------------------------- ### Create a Command (Traditional Approach) Source: https://vsix.guide/commands This is the traditional method for creating commands without the Community Toolkit. It involves manual registration with the command service. ```csharp public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync(...) { await JoinableTaskFactory.SwitchToMainThreadAsync(); var commandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; var cmdId = new CommandID(PackageGuids.guidMyPackageCmdSet, PackageIds.MyCommand); var menuItem = new MenuCommand(Execute, cmdId); commandService.AddCommand(menuItem); } private void Execute(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); // Command logic } } ``` -------------------------------- ### Package using Community Toolkit Source: https://vsix.guide/packages An example of a package using the Community Toolkit's ToolkitPackage base class, which simplifies command registration. Ensure the Menu resource is correctly linked. ```csharp [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(PackageGuids.MyPackageString)] [ProvideMenuResource("Menus.ctmenu", 1)] public sealed class MyPackage : ToolkitPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { // Register all commands in the assembly await this.RegisterCommandsAsync(); } } ``` -------------------------------- ### Product Architecture Specification Source: https://vsix.guide/vsix-manifest Defines the target processor architecture for the VSIX installation. Valid values include x86, amd64, and arm64. ```xml amd64 ``` -------------------------------- ### Track Document Changes Source: https://vsix.guide/services Example of how to advise for document change events using the SVsTrackProjectDocuments service. Requires an event sink implementation. ```csharp // Example: Track document changes var tracker = (IVsTrackProjectDocuments2)GetService(typeof(SVsTrackProjectDocuments)); tracker.AdviseTrackProjectDocumentsEvents(myEventSink, out uint cookie); ``` -------------------------------- ### Providing Async Services Source: https://vsix.guide/async-services Register an asynchronous service using `[ProvideService(..., IsAsyncQueryable = true)]` and implement `AddService` in `InitializeAsync`. The service creation can be done on a background thread. ```csharp [ProvideService(typeof(SMyService), IsAsyncQueryable = true)] public sealed class MyPackage : AsyncPackage { protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) { AddService(typeof(SMyService), CreateServiceAsync, promote: true); } private async Task CreateServiceAsync( IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType) { // Create service on background thread if possible var service = new MyService(); await service.InitializeAsync(); return service; } } ``` -------------------------------- ### Implement Custom Scanner (Tokenizer) Source: https://vsix.guide/language-services Implement the IScanner interface to define how your language is tokenized. This example handles whitespace, keywords, and identifiers. ```csharp public class MyScanner : IScanner { private string _source; private int _offset; private IVsTextLines _buffer; public MyScanner(IVsTextLines buffer) { _buffer = buffer; } public void SetSource(string source, int offset) { _source = source; _offset = offset; } public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state) { if (_offset >= _source.Length) return false; tokenInfo.StartIndex = _offset; char c = _source[_offset]; if (char.IsWhiteSpace(c)) { while (_offset < _source.Length && char.IsWhiteSpace(_source[_offset])) _offset++; tokenInfo.EndIndex = _offset - 1; tokenInfo.Type = TokenType.WhiteSpace; return true; } if (char.IsLetter(c)) { while (_offset < _source.Length && char.IsLetterOrDigit(_source[_offset])) _offset++; tokenInfo.EndIndex = _offset - 1; var word = _source.Substring(tokenInfo.StartIndex, _offset - tokenInfo.StartIndex); if (IsKeyword(word)) { tokenInfo.Type = TokenType.Keyword; tokenInfo.Color = TokenColor.Keyword; } else { tokenInfo.Type = TokenType.Identifier; tokenInfo.Color = TokenColor.Identifier; } return true; } // Handle other token types... _offset++; tokenInfo.EndIndex = _offset - 1; tokenInfo.Type = TokenType.Unknown; return true; } private bool IsKeyword(string word) { var keywords = new[] { "if", "else", "for", "while", "return", "function" }; return keywords.Contains(word.ToLower()); } } ``` -------------------------------- ### Accessing Services Asynchronously and Synchronously Source: https://vsix.guide/services Demonstrates the recommended asynchronous method and the legacy synchronous method for retrieving services from the service provider. ```csharp // Async (recommended) var service = await ServiceProvider.GetServiceAsync(typeof(SVsUIShell)) as IVsUIShell; // Sync (legacy) var service = (IVsTextManager)GetService(typeof(SVsTextManager)); ```