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.