VsixCommunity / Community.VisualStudio.Toolkit.DependencyInjection

Adds Dependency Injection tooling for the Community.VisualStudio.Toolkit
Other
14 stars 7 forks source link

Exception "ctor must be called on the UI thread" during startup #10

Closed AlonAm closed 11 months ago

AlonAm commented 1 year ago

Version

17.0.13

Description

Error ".ctor must be called on the UI thread" occurs when the package loads in the background.

Analysis

The base package class DIToolkitPackage creates instances of [CommandWrapper<>]() during InitializeAsync. The class CommandWrapper calls IMenuCommandService.AddCommand in the constructor, which requires the main UI thread.

ActivityLog.xml

SetSite failed for package [DatadogPackage]Source: 'Microsoft.VisualStudio.Shell.Framework' Description: .ctor must be called on the UI thread. System.Runtime.InteropServices.COMException (0x8001010E): .ctor must be called on the UI thread. at Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(String callerMemberName) at Community.VisualStudio.Toolkit.DependencyInjection.Core.CommandWrapper1..ctor(IServiceProvider serviceProvider, AsyncPackage package)&#x000D;&#x000A;--- End of stack trace from previous location where exception was thrown ---&#x000D;&#x000A; at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument)&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType)&#x000D;&#x000A; at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)&#x000D;&#x000A; at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)&#x000D;&#x000A; at Community.VisualStudio.Toolkit.DependencyInjection.DIToolkitPackage1.<InitializeAsync>d0.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 Datadog.VisualStudio.Extension.VS2022.DatadogPackage.<InitializeAsync>d24.MoveNext() in D:\a\1\s\src\Datadog.VisualStudio.Extension.VS2022\DatadogPackage.cs:line 41 --- 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.Shell.AsyncPackage.<>c__DisplayClass21_0.<<Microsoft-VisualStudio-Shell-Interop-IAsyncLoadablePackageInitialize-Initialize>b__1>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.VisualStudio.Services.VsTask.RethrowException(AggregateException e) at Microsoft.VisualStudio.Services.VsTask.InternalGetResult(Boolean ignoreUIThreadCheck)

AlonAm commented 1 year ago

This looks related: https://github.com/VsixCommunity/Community.VisualStudio.Toolkit.DependencyInjection/blob/ee9b1ad6f0329b3b74eafae6c11aa8b1ca1e0c51/src/Core/Shared/CommandWrapper.cs#L37C13-L37C49

AlonAm commented 11 months ago

@StevenRasmussen @madskristensen hi, please advice, I'm not sure how to resolve the issue above. Either adding 'await JoinableTaskFactory.SwitchToMainThreadAsync();' somewhere or changing the toolkit design so the commands are always added on the main UI thread. Kindly help

madskristensen commented 11 months ago

Adding commands have to happen on the UI thread as the exception points out. You can switch to the UI thread as you stated, and that should work. For your specific case, I'll let @StevenRasmussen comment if there's anything of interest for the DI system here

AlonAm commented 11 months ago

As a temporary solution to avoid the error message, is it better to add await JoinableTaskFactory.SwitchToMainThreadAsync(); before the await base.InitializeAsync(cancellationToken, progress);?

        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await JoinableTaskFactory.SwitchToMainThreadAsync();;

            await base.InitializeAsync(cancellationToken, progress);

            await this.RegisterCommandsAsync();

            this.RegisterToolWindows();

            // other initializations
        }
StevenRasmussen commented 11 months ago

Sorry for the delay in responding. Just approved and merged the PR. Thanks!

AlonAm commented 11 months ago

Thank you for your help! 🙂