VS扩展无法随Visual Studio启动问题排查与解决
问题:VS扩展无法在未加载解决方案时启动,工具栏快捷方式无法使用
我正在开发一款包含工具栏、工具窗口和设置页的VS扩展,原本该扩展仅在加载解决方案后才能正常工作,但因为工具栏包含外部应用快捷方式,希望能在未加载解决方案时就启动这些应用。
我的主Package代码如下:
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] [ProvideMenuResource("Menus.ctmenu", 1)] [ProvideToolWindow(typeof(MyBarToolWindow))] [ProvideOptionPage(typeof(UserSettingsPage), "My Toolbar", "Settings", 0, 0, true)] [ProvideProfileAttribute(typeof(UserSettingsPage), "MyBar (new)", "General", 1002, 1003, true)] [Guid(GroupsGuids.guidMyBarUIPkgString)] [ProvideAutoLoad(UIContextGuids80.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] public sealed class MyBar_UIPackage : AsyncPackage, IDteServiceProvider { private IMyBarSettings settings; // ..... /// <summary> /// Called when the VSPackage is loaded by Visual Studio. /// </summary> protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress) { // Switches to the UI thread in order to consume some services used in command initialization await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await base.InitializeAsync(cancellationToken, progress); this.GetDte().Events.DTEEvents.OnStartupComplete += this.OnStartupCompleted; this.GetDte().Events.SolutionEvents.Opened += this.OnInitialize; this.GetDte().Events.WindowEvents.WindowActivated += this.OnWindowEventsWindowActivated; // Query service asynchronously from the UI thread var dte = GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; } }
当前遇到的问题:
Opened、WindowActivated和OnStartupComplete事件仅在加载解决方案时触发,而非Visual Studio启动时。- 若关闭VS时工具窗口处于打开状态,下次启动VS时工具窗口会重新打开,扩展也会随之启动;但如果VS启动时工具窗口未打开,就无法打开工具窗口,必须加载解决方案才能启动扩展。
原因分析
- 尽管添加了
[ProvideAutoLoad(UIContextGuids80.NoSolution)],但VS的包加载机制可能因菜单或工具窗口的注册依赖,导致包并未真正在NoSolution上下文加载。 - 菜单命令的可见性/可用性设置可能绑定了“解决方案已加载”的上下文,只有加载解决方案后,菜单触发才会触发包加载。
- 事件绑定逻辑写在包初始化方法中,若包未在VS启动时加载,这些事件自然不会被注册和触发。
解决方案
1. 强制包在VS启动时加载
修改ProvideAutoLoad属性,同时添加UIContextGuids80.EmptySolution(对应VS启动后无解决方案的状态),并调整加载标记:
[ProvideAutoLoad(UIContextGuids80.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] [ProvideAutoLoad(UIContextGuids80.EmptySolution, PackageAutoLoadFlags.BackgroundLoad)]
如果仍不生效,可以暂时移除BackgroundLoad标记测试:
[ProvideAutoLoad(UIContextGuids80.NoSolution)] [ProvideAutoLoad(UIContextGuids80.EmptySolution)]
2. 调整菜单命令的可见性
打开Menus.ctmenu文件,确保工具栏命令的VisibilityConstraints未绑定到“解决方案已存在”的上下文,设置为无解决方案和有解决方案时都可见:
<VisibilityConstraints> <VisibilityItem guid="guidMyBarCmdSet" id="MyExternalAppCmdId" context="UICONTEXT_NoSolution"/> <VisibilityItem guid="guidMyBarCmdSet" id="MyExternalAppCmdId" context="UICONTEXT_SolutionExists"/> </VisibilityConstraints>
同时确保命令的CommandFlag包含合适标记(如DynamicVisibility),避免默认隐藏。
3. 直接在包初始化时完成核心逻辑
不要依赖SolutionEvents.Opened这类仅在解决方案加载时触发的事件,将启动外部应用的逻辑直接放在InitializeAsync中,同时绑定VS启动完成事件:
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress) { await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await base.InitializeAsync(cancellationToken, progress); // 直接初始化外部应用命令,无需等待解决方案加载 await MyExternalAppCommand.InitializeAsync(this); // 绑定相关事件 var dte = GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; if (dte != null) { dte.Events.DTEEvents.OnStartupComplete += OnStartupCompleted; dte.Events.SolutionEvents.Opened += OnInitialize; dte.Events.WindowEvents.WindowActivated += OnWindowEventsWindowActivated; } }
4. 调整工具窗口的加载逻辑
若需要在未加载解决方案时打开工具窗口,确保ProvideToolWindow无解决方案相关绑定,并在命令中手动创建工具窗口实例:
private async Task ShowToolWindowAsync() { var window = await this.FindToolWindowAsync(typeof(MyBarToolWindow), 0, true, CancellationToken.None); if ((null == window) || (null == window.Frame)) { throw new NotSupportedException("无法创建工具窗口"); } var windowFrame = (IVsWindowFrame)window.Frame; ErrorHandler.ThrowOnFailure(windowFrame.Show()); }
内容的提问来源于stack exchange,提问作者Robin Holenweger
相关产品推荐
相关产品推荐

