From 68cb1c4bc8e4f6c51599e362c3f4d38a13c2853e Mon Sep 17 00:00:00 2001 From: Matteo Prosperi Date: Fri, 7 Nov 2025 16:56:26 -0800 Subject: [PATCH 1/2] Add a sample to show interactions between VSSDK and VisualStudio.Extensibility portions of an in-proc extension --- .../.vsextension/string-resources.json | 4 + .../AsyncPackageAndMEF.csproj | 18 ++ .../Samples/AsyncPackageAndMEF/README.md | 231 ++++++++++++++++++ .../VSSDK/MyMEFComponent.cs | 21 ++ .../AsyncPackageAndMEF/VSSDK/MyPackage.cs | 55 +++++ .../AsyncPackageAndMEF/VSSDK/MyService.cs | 15 ++ .../ExtensionEntrypoint.cs | 22 ++ .../IMyBrokeredService.cs | 29 +++ .../InteractWithAsyncPackageCommand.cs | 33 +++ .../InteractWithMEFComponentCommand.cs | 34 +++ .../MyBrokeredService.cs | 51 ++++ .../source.extension.vsixmanifest | 23 ++ .../Samples/CommentRemover/global.json | 9 - .../source.extension.vsixmanifest | 3 - .../source.extension.vsixmanifest | 3 - New_Extensibility_Model/Samples/Samples.sln | 11 +- 16 files changed, 545 insertions(+), 17 deletions(-) create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/.vsextension/string-resources.json create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/AsyncPackageAndMEF.csproj create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyMEFComponent.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyPackage.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyService.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/ExtensionEntrypoint.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/IMyBrokeredService.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithAsyncPackageCommand.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithMEFComponentCommand.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/MyBrokeredService.cs create mode 100644 New_Extensibility_Model/Samples/AsyncPackageAndMEF/source.extension.vsixmanifest delete mode 100644 New_Extensibility_Model/Samples/CommentRemover/global.json diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/.vsextension/string-resources.json b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/.vsextension/string-resources.json new file mode 100644 index 00000000..ee3f2f7d --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/.vsextension/string-resources.json @@ -0,0 +1,4 @@ +{ + "AsyncPackageAndMEF.InteractWithAsyncPackageCommand.DisplayName": "Interact with AsyncPackage", + "AsyncPackageAndMEF.InteractWithMEFComponentCommand.DisplayName": "Interact with MEF Component" +} \ No newline at end of file diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/AsyncPackageAndMEF.csproj b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/AsyncPackageAndMEF.csproj new file mode 100644 index 00000000..801e7e55 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/AsyncPackageAndMEF.csproj @@ -0,0 +1,18 @@ + + + net472 + enable + enable + 12 + + true + true + true + + + + + + + + diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md new file mode 100644 index 00000000..505a66a5 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md @@ -0,0 +1,231 @@ +--- +title: In-proc VisualStudio.Extensibility extension with MEF components and AsyncPackage sample +description: A reference sample for how VisualStudio.Extensibility in-proc extensions can include MEF components and AsyncPackages. +date: 2025-11-07 +--- + +# Walkthrough: In-proc VisualStudio.Extensibility extension with MEF components and AsyncPackage sample + +This is sample of how to write an in-proc VisualStudio.Extensibility extensions that also includes components +that are common in VSSDK extensions: `AsyncPackage`s and MEF components. + +When creating such an extension, there is one important considerations to keep in mind: the VisualStudio.Extensibility extension class, the MEF component and the AsyncPackage are all initialized +independently from each others. Before any interaction between them, the initiator must ensure that the other +component is initialized. + +We start with an empty in-proc VisualStudio.Extensibility extension project as described [here](https://learn.microsoft.com/en-us/visualstudio/extensibility/visualstudio.extensibility/get-started/in-proc-extensions). + +## Adding an AsyncPackage + +In a VSSDK extension, the `AsyncPackage` acts as the main entry point for the extension. This is similar to how +the `Extension` class acts as the main entry point for a VisualStudio.Extensibility extension. The `AsyncPackage` +allows the extension to provide many functionalities, including providing VS services. + +To add an `AsyncPackage` to the extension, we need to add a new class that derives from `AsyncPackage` and override +the necessary methods. We will also define a new service (`MyService`) that will be provided by the package. This +service will allow the VisualStudio.Extensibility `Extension` to interact with the package guaranteeing that the +necessary initialization is performed. + +```cs +[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] +[Guid(MyPackage.PackageGuidString)] +[ProvideService(typeof(MyService), IsAsyncQueryable = true)] +public sealed class MyPackage : AsyncPackage +{ + public const string PackageGuidString = "ac1de0e2-bc69-4a63-bb7e-15f3274448c7"; + + protected override Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + this.AddService( + typeof(MyService), + (container, cancellationToken, serviceType) => Task.FromResult(new MyService()), + promote: true); + + return Task.CompletedTask; + } +} +``` + +We also need to update the project to generate a pkgdef file for the extension: + +```xml + + true + true + +``` + +And add the corresponding asset to the `.vsixmanifest` file: + +```xml + + + +``` + +With this setup, we can add any VSSDK functionality to the `AsyncPackage` and use the `MyService` to expose +it to the VisualStudio.Extensibility part of the extension. + +For example, we can add a command like this: + +```cs +[VisualStudioContribution] +internal class InteractWithAsyncPackageCommand : Command +{ + private readonly AsyncServiceProviderInjection myServiceInjection; + + public InteractWithAsyncPackageCommand(AsyncServiceProviderInjection myServiceInjection) + { + this.myServiceInjection = myServiceInjection; + } + + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + var myService = await this.myServiceInjection.GetServiceAsync(); + // Use myService + } + + ... +``` + +By debugging the extension, we can verify that the `AsyncPackage` is only initialized when the command requests +`MyService` by calling `GetServiceAsync`. + +## Adding a MEF component + +To add a MEF component to the extension, we need to create a new class that is decorated with the `Export` +attribute. + +```cs +[Export(typeof(MyMEFComponent))] +internal class MyMEFComponent +{ +} +``` + +We can also add code to the `MyMEFComponent` to import other MEF components as needed (but we won't do it in +this example to keep it simple). + +We must also add an asset to the `.vsixmanifest` file to declare that the extension contains MEF components: +```xml + + + +``` + +Now we can retrieve the MEF component from the VisualStudio.Extensibility part of the extension: + +```cs +[VisualStudioContribution] +internal class InteractWithMEFComponentCommand : Command +{ + private readonly MefInjection myMefComponentInjection; + + public InteractWithMEFComponentCommand(MefInjection myMefComponentInjection) + { + this.myMefComponentInjection = myMefComponentInjection; + } + + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + var myMefComponent = await this.myMefComponentInjection.GetServiceAsync(); + // Use myMefComponent + } + + ... +``` + +By debugging the extension, we can verify that `MyMEFComponent` is only initialized when the command requests +is by calling `GetServiceAsync`. We can also verify that the `AsyncPackage` is not initialized when the MEF +component is requested: MEF components and AsyncPackages are independent from each other. + +## Communicating from the AsyncPackage to the VisualStudio.Extensibility extension + +We have discussed how the VisualStudio.Extensibility part of an extension can interact with the VSSDK part +(`AsyncPackage` and MEF components). However, there might be scenarios where the `AsyncPackage` or a MEF +component need to interact with the VisualStudio.Extensibility part of the extension. + +To achieve this we can expose a brokered service: + +```cs +public interface IMyBrokeredService +{ + public static class Configuration + { + public const string ServiceName = "AsyncPackageAndMEF.MyBrokeredService"; + public static readonly Version ServiceVersion = new(1, 0); + + public static readonly ServiceMoniker ServiceMoniker = new(ServiceName, ServiceVersion); + + public static ServiceRpcDescriptor ServiceDescriptor => new ServiceJsonRpcDescriptor( + ServiceMoniker, + ServiceJsonRpcDescriptor.Formatters.MessagePack, + ServiceJsonRpcDescriptor.MessageDelimiters.BigEndianInt32LengthHeader); + } + + // Add public methods as needed + ... +} + +[VisualStudioContribution] +internal class MyBrokeredService : IMyBrokeredService +{ + private readonly VisualStudioExtensibility extensibility; + + private ProgressReporter? progressReporter; + + public MyBrokeredService(VisualStudioExtensibility extensibility) + { + this.extensibility = extensibility; + } + + [VisualStudioContribution] + public static BrokeredServiceConfiguration BrokeredServiceConfiguration + => new(IMyBrokeredService.Configuration.ServiceName, IMyBrokeredService.Configuration.ServiceVersion, typeof(MyBrokeredService)) + { + ServiceAudience = BrokeredServiceAudience.Local, + }; + + ... +} +``` + +and update the `Extension` class to register the service: +```cs +protected override void InitializeServices(IServiceCollection serviceCollection) +{ + serviceCollection.ProfferBrokeredService(MyBrokeredService.BrokeredServiceConfiguration, IMyBrokeredService.Configuration.ServiceDescriptor); + base.InitializeServices(serviceCollection); +} +``` + +Now the `AsyncPackage` can retrieve the brokered service and interact with it: + +```cs +var serviceBrokerContainer = await this.GetServiceAsync(); +var serviceBroker = serviceBrokerContainer.GetFullAccessServiceBroker(); +IMyBrokeredService? myBrokeredServiceProxy = null; +try +{ + myBrokeredServiceProxy = await serviceBroker.GetProxyAsync(IMyBrokeredService.Configuration.ServiceDescriptor, this.DisposalToken); + /// Use myBrokeredServiceProxy +} +finally +{ + (myBrokeredServiceProxy as IDisposable)?.Dispose(); +} +``` + +If we wanted to do the same from some other part of the VSSDK extension where the `AsyncPackage` instance is +not readily available, we could modify the code above to retrieve the `IBrokeredServiceContainer` from the global +service provider: + +```cs +var serviceBrokerContainer = await AsyncServiceProvider.GlobalProvider.GetServiceAsync(); +``` + +By debugging the extension, we can verify that the VisualStudio.Extensibility part of the extension (the +`Extension` class) is only initialized when the brokered service proxy is retrieved by calling `GetProxyAsync`. + +This is the proper way for the VSSDK part of the extension to use the VisualStudio.Extensibility features +provided by the `VisualStudioExtensibility` object. diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyMEFComponent.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyMEFComponent.cs new file mode 100644 index 00000000..0460cd37 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyMEFComponent.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.ComponentModel.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Shell; + +[Export(typeof(MyMEFComponent))] +internal class MyMEFComponent +{ + public MyMEFComponent() + { + } + + internal Task SayHelloAsync(VisualStudioExtensibility extensibility, CancellationToken cancellationToken) + => extensibility.Shell().ShowPromptAsync("Hello from a MEF component!", PromptOptions.OK, cancellationToken); +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyPackage.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyPackage.cs new file mode 100644 index 00000000..824260a8 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyPackage.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft; +using Microsoft.ServiceHub.Framework; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.ServiceBroker; + +[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] +[Guid(MyPackage.PackageGuidString)] +[ProvideService(typeof(MyService), IsAsyncQueryable = true)] +public sealed class MyPackage : AsyncPackage +{ + public const string PackageGuidString = "ac1de0e2-bc69-4a63-bb7e-15f3274448c7"; + + protected override Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + this.AddService( + typeof(MyService), + (container, cancellationToken, serviceType) => Task.FromResult(new MyService()), + promote: true); + + this.DoSomethingAsync() + .FileAndForget("AsyncPackageAndMEF/MyPackage/DoSomething"); + + return Task.CompletedTask; + } + + private async Task DoSomethingAsync() + { + var serviceBrokerContainer = await this.GetServiceAsync(); + var serviceBroker = serviceBrokerContainer.GetFullAccessServiceBroker(); + IMyBrokeredService? myBrokeredServiceProxy = null; + try + { + myBrokeredServiceProxy = await serviceBroker.GetProxyAsync(IMyBrokeredService.Configuration.ServiceDescriptor, this.DisposalToken); + + Assumes.NotNull(myBrokeredServiceProxy); + await myBrokeredServiceProxy.StartReportingProgressAsync("Doing some work", this.DisposalToken); + + // Simulate doing some work. + await Task.Delay(10000, this.DisposalToken); + + await myBrokeredServiceProxy.StopReportingProgressAsync(this.DisposalToken); + } + finally + { + (myBrokeredServiceProxy as IDisposable)?.Dispose(); + } + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyService.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyService.cs new file mode 100644 index 00000000..b940e3aa --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VSSDK/MyService.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Shell; + +internal class MyService +{ + internal Task SayHelloAsync(VisualStudioExtensibility extensibility, CancellationToken cancellationToken) + => extensibility.Shell().ShowPromptAsync("Hello from a VS service!", PromptOptions.OK, cancellationToken); +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/ExtensionEntrypoint.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/ExtensionEntrypoint.cs new file mode 100644 index 00000000..ed3ab977 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/ExtensionEntrypoint.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.Extensibility; + +[VisualStudioContribution] +public class ExtensionEntrypoint : Extension +{ + public override ExtensionConfiguration ExtensionConfiguration => new() + { + RequiresInProcessHosting = true, + }; + + protected override void InitializeServices(IServiceCollection serviceCollection) + { + serviceCollection.ProfferBrokeredService(MyBrokeredService.BrokeredServiceConfiguration, IMyBrokeredService.Configuration.ServiceDescriptor); + base.InitializeServices(serviceCollection); + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/IMyBrokeredService.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/IMyBrokeredService.cs new file mode 100644 index 00000000..4d8881d8 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/IMyBrokeredService.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.ServiceHub.Framework; + +public interface IMyBrokeredService +{ + Task StartReportingProgressAsync(string message, CancellationToken cancellationToken); + + Task StopReportingProgressAsync(CancellationToken cancellationToken); + + public static class Configuration + { + public const string ServiceName = "AsyncPackageAndMEF.MyBrokeredService"; + public static readonly Version ServiceVersion = new(1, 0); + + public static readonly ServiceMoniker ServiceMoniker = new(ServiceName, ServiceVersion); + + public static ServiceRpcDescriptor ServiceDescriptor => new ServiceJsonRpcDescriptor( + ServiceMoniker, + ServiceJsonRpcDescriptor.Formatters.MessagePack, + ServiceJsonRpcDescriptor.MessageDelimiters.BigEndianInt32LengthHeader); + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithAsyncPackageCommand.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithAsyncPackageCommand.cs new file mode 100644 index 00000000..7bed30a6 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithAsyncPackageCommand.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; +using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility; + +[VisualStudioContribution] +internal class InteractWithAsyncPackageCommand : Command +{ + private readonly AsyncServiceProviderInjection myServiceInjection; + + public InteractWithAsyncPackageCommand(AsyncServiceProviderInjection myServiceInjection) + { + this.myServiceInjection = myServiceInjection; + } + + public override CommandConfiguration CommandConfiguration => new(displayName: "%AsyncPackageAndMEF.InteractWithAsyncPackageCommand.DisplayName%") + { + Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu], + Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText), + }; + + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + var myService = await this.myServiceInjection.GetServiceAsync(); + await myService.SayHelloAsync(this.Extensibility, cancellationToken); + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithMEFComponentCommand.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithMEFComponentCommand.cs new file mode 100644 index 00000000..1cdb5795 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/InteractWithMEFComponentCommand.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; +using Microsoft.VisualStudio.Extensibility.Shell; +using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility; + +[VisualStudioContribution] +internal class InteractWithMEFComponentCommand : Command +{ + private readonly MefInjection myMefComponentInjection; + + public InteractWithMEFComponentCommand(MefInjection myMefComponentInjection) + { + this.myMefComponentInjection = myMefComponentInjection; + } + + public override CommandConfiguration CommandConfiguration => new(displayName: "%AsyncPackageAndMEF.InteractWithMEFComponentCommand.DisplayName%") + { + Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu], + Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText), + }; + + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + var myMefComponent = await this.myMefComponentInjection.GetServiceAsync(); + await myMefComponent.SayHelloAsync(this.Extensibility, cancellationToken); + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/MyBrokeredService.cs b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/MyBrokeredService.cs new file mode 100644 index 00000000..4e809740 --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/VisualStudio.Extensibility/MyBrokeredService.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace AsyncPackageAndMEF; + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Shell; + +[VisualStudioContribution] +internal class MyBrokeredService : IMyBrokeredService +{ + private readonly VisualStudioExtensibility extensibility; + + private ProgressReporter? progressReporter; + + public MyBrokeredService(VisualStudioExtensibility extensibility) + { + this.extensibility = extensibility; + } + + [VisualStudioContribution] + public static BrokeredServiceConfiguration BrokeredServiceConfiguration + => new(IMyBrokeredService.Configuration.ServiceName, IMyBrokeredService.Configuration.ServiceVersion, typeof(MyBrokeredService)) + { + ServiceAudience = BrokeredServiceAudience.Local, + }; + + public async Task StartReportingProgressAsync(string message, CancellationToken cancellationToken) + { + if (this.progressReporter is not null) + { + throw new InvalidOperationException("Progress reporting has not been started."); + } + + this.progressReporter = await this.extensibility.Shell().StartProgressReportingAsync(message, cancellationToken); + } + + public Task StopReportingProgressAsync(CancellationToken cancellationToken) + { + if (this.progressReporter is null) + { + throw new InvalidOperationException("Progress reporting has not been started."); + } + + this.progressReporter?.Dispose(); + this.progressReporter = null; + return Task.CompletedTask; + } +} diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/source.extension.vsixmanifest b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/source.extension.vsixmanifest new file mode 100644 index 00000000..e3ab7aec --- /dev/null +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/source.extension.vsixmanifest @@ -0,0 +1,23 @@ + + + + + In-proc extension with MEF and AsyncPackage + An extension demonstrating interactions between different parts of a VisualStudio.Extensibility extension that also provides MEF components and an AsyncPackage. + + + + amd64 + + + arm64 + + + + + + + + + + \ No newline at end of file diff --git a/New_Extensibility_Model/Samples/CommentRemover/global.json b/New_Extensibility_Model/Samples/CommentRemover/global.json deleted file mode 100644 index 8a53575a..00000000 --- a/New_Extensibility_Model/Samples/CommentRemover/global.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "sdk": { - "version": "7.0.403", - "rollForward": "patch" - }, - "msbuild-sdks": { - "Microsoft.Build.Traversal": "3.1.6" - } -} diff --git a/New_Extensibility_Model/Samples/CommentRemover/source.extension.vsixmanifest b/New_Extensibility_Model/Samples/CommentRemover/source.extension.vsixmanifest index ca90eb23..84b3448e 100644 --- a/New_Extensibility_Model/Samples/CommentRemover/source.extension.vsixmanifest +++ b/New_Extensibility_Model/Samples/CommentRemover/source.extension.vsixmanifest @@ -22,7 +22,4 @@ - - - diff --git a/New_Extensibility_Model/Samples/InProcFeatureGallery/source.extension.vsixmanifest b/New_Extensibility_Model/Samples/InProcFeatureGallery/source.extension.vsixmanifest index 0bb7ee94..be69aa0a 100644 --- a/New_Extensibility_Model/Samples/InProcFeatureGallery/source.extension.vsixmanifest +++ b/New_Extensibility_Model/Samples/InProcFeatureGallery/source.extension.vsixmanifest @@ -16,7 +16,4 @@ - - - diff --git a/New_Extensibility_Model/Samples/Samples.sln b/New_Extensibility_Model/Samples/Samples.sln index c4c34243..acefbaec 100644 --- a/New_Extensibility_Model/Samples/Samples.sln +++ b/New_Extensibility_Model/Samples/Samples.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.0.10520.38 main +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36616.10 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{070F0AEA-C0A0-4B5D-9286-55574A37BE7A}" ProjectSection(SolutionItems) = preProject @@ -83,8 +83,11 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extension", "ExtensionWithTraditionalComponents\Extension\Extension.csproj", "{15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Container", "ExtensionWithTraditionalComponents\Container\Container.csproj", "{D99787A2-36E1-FE47-40E9-62CF6BE72BFB}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassificationSample", "ClassificationSample\ClassificationSample.csproj", "{781AC13C-B948-F6C0-FAE7-179CC49641F2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncPackageAndMEF", "AsyncPackageAndMEF\AsyncPackageAndMEF.csproj", "{42EABE13-6056-AFA4-D0C5-5192B72C9038}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -213,6 +216,10 @@ Global {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Debug|Any CPU.Build.0 = Debug|Any CPU {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Release|Any CPU.Build.0 = Release|Any CPU + {42EABE13-6056-AFA4-D0C5-5192B72C9038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42EABE13-6056-AFA4-D0C5-5192B72C9038}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42EABE13-6056-AFA4-D0C5-5192B72C9038}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42EABE13-6056-AFA4-D0C5-5192B72C9038}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 2122f4c92706e597107c8c2df1ca2a99d65c19b3 Mon Sep 17 00:00:00 2001 From: Matteo Prosperi Date: Tue, 11 Nov 2025 10:58:16 -0800 Subject: [PATCH 2/2] Address comments --- .../Samples/AsyncPackageAndMEF/README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md index 505a66a5..6b36f1f2 100644 --- a/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md +++ b/New_Extensibility_Model/Samples/AsyncPackageAndMEF/README.md @@ -9,11 +9,20 @@ date: 2025-11-07 This is sample of how to write an in-proc VisualStudio.Extensibility extensions that also includes components that are common in VSSDK extensions: `AsyncPackage`s and MEF components. -When creating such an extension, there is one important considerations to keep in mind: the VisualStudio.Extensibility extension class, the MEF component and the AsyncPackage are all initialized +Many functions of the `AsyncPackage`, like providing commands and tool windows, have a more modern +alternative in VisualStudio.Extensibility APIs. Similarly functionalities, like classifications and taggers, +that used to require MEF have now easier to use alternatives provided by VisualStudio.Extensibility APIs. +This sample is not meant as an invitation for extenders to use `AsyncPackage`s and MEF, but as instructions +on how to correctly interact with `AsyncPackage`s and MEF components in those cases when an extender needs to +rely on APIs that don't have a VisualStudio.Extensibility alternative. + +When creating such an extension, there is one important considerations to keep in mind: the +VisualStudio.Extensibility extension class, the MEF component and the AsyncPackage are all initialized independently from each others. Before any interaction between them, the initiator must ensure that the other component is initialized. -We start with an empty in-proc VisualStudio.Extensibility extension project as described [here](https://learn.microsoft.com/en-us/visualstudio/extensibility/visualstudio.extensibility/get-started/in-proc-extensions). +We start with an empty in-proc VisualStudio.Extensibility extension project as described +[here](https://learn.microsoft.com/en-us/visualstudio/extensibility/visualstudio.extensibility/get-started/in-proc-extensions). ## Adding an AsyncPackage @@ -199,7 +208,7 @@ protected override void InitializeServices(IServiceCollection serviceCollection) } ``` -Now the `AsyncPackage` can retrieve the brokered service and interact with it: +Now the `AsyncPackage` can retrieve a [proxy of the brokered service](https://microsoft.github.io/vs-streamjsonrpc/docs/proxies.html#proxy-traits) and interact with it: ```cs var serviceBrokerContainer = await this.GetServiceAsync();