microsoft / VSExtensibility

A repo for upcoming changes to extensibility in Visual Studio, the new extensibility model, and language server protocol.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sharing the same Service between VS SDK and VisualStudio.Extensibility components within an in-proc extension

awschristou opened this issue · comments

The extension I work on is too big to port from the VS SDK to VisualStudio.Extensibility in one go. I would like to lean into the in-proc/hybrid approach. This would allow me to start migrating portions of the extension to VisualStudio.Extensibility while keeping the extension fully functional.

I was planning to have both my existing VS SDK based AsyncPackage and a new class deriving from the VisualStudio.Extensibility Extension class in the same VSIX. I'd like to start moving some of the menus and commands away from the VS SDK/VSCT/OleMenuCommand entry points over to classes derived from the VisualStudio.Extensibility Command class, however these new Command classes would need to share the same extension code, and often the same instance/state. The business logic and lower-level support objects would continue to use a mix of VS SDK based "VsService" services. Is it possible to accomplish this?

I've tried to define a Service (SMyService/IMyService), and expose it from my AsyncPackage (VsSdkPackage) using ProvideService. My hope was that I could request this service from VisualStudio.Extensibility, and that CreateMyServiceAsync would be invoked.

    [Guid("10E300AC-DDAD-4EE0-9795-D3D525E3089A")]
    public interface SMyService{}

    public interface IMyService
    {
        int DoThing();
        void DoAnotherThing();
    }

    // This implementation would do VS SDK things until VisualStudio.Extensibility can replace it
    internal class MyService : IMyService { ... }

    [ProvideService((typeof(SMyService)), IsAsyncQueryable = true)]
    ...
    public class VsSdkPackage : AsyncPackage
    {
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await JoinableTaskFactory.SwitchToMainThreadAsync();

            AddService(typeof(SMyService), CreateMyServiceAsync);
        }

        private async Task<object> CreateMyServiceAsync(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType)
        {
            var service = new MyService(this);
            await service.InitializeAsync(cancellationToken);
            return service;
        }
    }

Next I tried a couple of approaches:

  • I tried calling Package.GetGlobalService(typeof(SMyService)) from my VisualStudio.Extensibility command
    • this returns null
    • I also tried calling this from my VisualStudio.Extensibility Extension class, and it returns null there too
  • I tried calling Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SMyService)) from my VisualStudio.Extensibility command
    • this returns null
    • I also tried calling this from my VisualStudio.Extensibility Extension class, and it returns null there too
  • I injected AsyncServiceProviderInjection<SMyService, IMyService> into my VisualStudio.Extensibility command
    • this raises an exception, and writes a lot of text to my debugger's output pane (see below)
Debugger Output contents
The thread 26532 has exited with code 0 (0x0).
Exception thrown: 'Microsoft.VisualStudio.Shell.ServiceUnavailableException' in mscorlib.dll
JsonRpc Error: 10 : Exception thrown from request "3" for method ExecuteCommandAsync: System.AggregateException: One or more errors occurred. ---> Microsoft.VisualStudio.Shell.ServiceUnavailableException: The SMyService service is unavailable.
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
 at ProtoHybridExt.MyCommand.<ExecuteCommandAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommandHandler.<ExecuteCommandAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.CommandSetProvider.<ExecuteCommandAsync>d__16.MoveNext()
 --- End of inner exception stack trace ---
---> (Inner Exception #0) Microsoft.VisualStudio.Shell.ServiceUnavailableException: The SMyService service is unavailable.
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
 at ProtoHybridExt.MyCommand.<ExecuteCommandAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommandHandler.<ExecuteCommandAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.CommandSetProvider.<ExecuteCommandAsync>d__16.MoveNext()<---

JsonRpc Error: 10 : System.AggregateException: One or more errors occurred. ---> Microsoft.VisualStudio.Shell.ServiceUnavailableException: The SMyService service is unavailable.
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
 at ProtoHybridExt.MyCommand.<ExecuteCommandAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommandHandler.<ExecuteCommandAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.CommandSetProvider.<ExecuteCommandAsync>d__16.MoveNext()
 --- End of inner exception stack trace ---
---> (Inner Exception #0) Microsoft.VisualStudio.Shell.ServiceUnavailableException: The SMyService service is unavailable.
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
 at ProtoHybridExt.MyCommand.<ExecuteCommandAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommandHandler.<ExecuteCommandAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Extensibility.Commands.CommandSetProvider.<ExecuteCommandAsync>d__16.MoveNext()<---
, ExecuteCommandAsync, 3, 
JsonRpc Warning: 20 : An exception of type Microsoft.VisualStudio.Shell.ServiceUnavailableException, Microsoft.VisualStudio.Utilities, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a was thrown but is not serializable.
RemoteCommandsLog Error: 0 : Error executing command ProtoHybridExt.ExtensionEntrypoint:ProtoHybridExt.MyCommand (AAA - Hybrid Command)

RemoteInvocationException: The SMyService service is unavailable.

StreamJsonRpc.JsonRpc.<InvokeCoreAsync>d__151`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Commands.Client.Models.CommandSetModel.<>c__DisplayClass56_0.<<ExecuteCommandAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Threading.ReentrantSemaphore.NotAllowedSemaphore.<>c__DisplayClass2_0.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Threading.ReentrantSemaphore.NotAllowedSemaphore.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Commands.Client.Models.CommandSetModel.RemoteCommandModel.<ExecAsync>d__26.MoveNext()

HResult: 0x80131500
  Microsoft.VisualStudio.Shell.ServiceUnavailableException: The SMyService service is unavailable.

Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Extensibility.VSSdkCompatibility.AsyncServiceProviderInjection`2.<GetServiceAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
ProtoHybridExt.MyCommand.<ExecuteCommandAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommandHandler.<ExecuteCommandAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Extensibility.Commands.CommandSetProvider.<ExecuteCommandAsync>d__16.MoveNext()

Is this use case supported? If so, would you be able to share guidance and possibly a sample?

Hi @awschristou, this should be working. You can have VSSDK packages and VisualStudio.Extensibility extension in the same assembly. From your description, it sounds like your service isn't registered/proffered properly.

I believe the issue is AddService override you are using only registers the service locally for that package. You can use the override with promote: true to make the registration visible to global service provider which would be required for this use case.

If that doesn't work, can you verify a pkgdef file containing the service registration is generated when you build your project?

Thanks,

Thank you @BertanAygun , I overlooked the promote overload while setting up my proof of concept project.

This is working as expected when I set promote: true, and I do see the service registration in the pkgdef file.