为VSPackage加载解决方案时如何通知?

Cod*_*Fox 2 c# vspackage vsix

当解决方案已完全加载时,我想收到通知。受此答案启发,我尝试实施IVsSolutionEvents

当我加载具有两个C#项目的解决方案时,请等到加载完成并最终关闭Visual Studio 2017时,输出仅显示以下跟踪消息:

VSTestPackage1: OnAfterOpenProject
VSTestPackage1: OnQueryCloseSolution
VSTestPackage1: OnQueryCloseProject
VSTestPackage1: OnQueryCloseProject
VSTestPackage1: OnBeforeCloseSolution
VSTestPackage1: OnQueryCloseProject
VSTestPackage1: OnBeforeCloseProject
VSTestPackage1: OnQueryCloseProject
VSTestPackage1: OnBeforeCloseProject
VSTestPackage1: OnAfterCloseSolution
Run Code Online (Sandbox Code Playgroud)

这是预期的行为吗?为什么OnAfterOpenSolution不被调用?

这是程序包的实现:

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(PackageGuidString)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly",
    Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasMultipleProjects_string)]
public sealed class VSPackage1 : Package, IVsSolutionEvents
{
    public const string PackageGuidString = "2e655097-9510-4cf8-b9d4-ceeacebbaf3c";

    private DTE _dte;
    private uint _hSolutionEvents = uint.MaxValue;
    private IVsSolution _solution;

    /// <summary>
    ///     Initialization of the package; this method is called right after the package is sited, so this is the place
    ///     where you can put all the initialization code that rely on services provided by VisualStudio.
    /// </summary>
    protected override void Initialize()
    {
        base.Initialize();

        _dte = (DTE) GetService(typeof(DTE));

        AdviseSolutionEvents();
    }

    protected override void Dispose(bool disposing)
    {
        UnadviseSolutionEvents();

        base.Dispose(disposing);
    }

    private void AdviseSolutionEvents()
    {
        UnadviseSolutionEvents();

        _solution = GetService(typeof(SVsSolution)) as IVsSolution;

        _solution?.AdviseSolutionEvents(this, out _hSolutionEvents);
    }

    private void UnadviseSolutionEvents()
    {
        if (_solution == null) return;
        if (_hSolutionEvents != uint.MaxValue)
        {
            _solution.UnadviseSolutionEvents(_hSolutionEvents);
            _hSolutionEvents = uint.MaxValue;
        }

        _solution = null;
    }

    #region Implementation of IVsSolutionEvents

    int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
    {
        Trace.WriteLine("OnAfterOpenProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)
    {
        Trace.WriteLine("OnQueryCloseProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)
    {
        Trace.WriteLine("OnBeforeCloseProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
    {
        Trace.WriteLine("OnAfterLoadProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
    {
        Trace.WriteLine("OnQueryUnloadProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)
    {
        Trace.WriteLine("OnBeforeUnloadProject", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
    {
        Trace.WriteLine("OnAfterOpenSolution", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)
    {
        Trace.WriteLine("OnQueryCloseSolution", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
    {
        Trace.WriteLine("OnBeforeCloseSolution", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
    {
        Trace.WriteLine("OnAfterCloseSolution", "VSTestPackage1");
        return VSConstants.S_OK;
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

小智 5

是的,这是设计使然。观察到此行为的原因是,相关事件在加载包之前触发。关闭解决方案,然后重新打开它(在装入程序包之后),可以观察事件确实触发来进行测试。在第二遍,您将看到事件触发。

您的示例使用的是SolutionHasMultipleProjects上下文guid,它确保仅当解决方案具有多个项目时才加载您的包。IDE确定的唯一方法是,首先加载解决方案负载,然后设置UI上下文。因此,基本上,您设置事件处理程序的时间为时已晚。

如果要确保收到该特定通知,可以注册软件包以使用NoSolution_string和SolutionExists_string加载。但这有点邪恶,因为这会迫使您的程序包始终加载(即使不需要时也是如此),这是不太理想的解决方案。

使用SolutionExistsAndFullyLoadedContext可能是更好的方法。最初加载程序包时,您会知道已经满足条件,并且可以在从程序包的Initialize覆盖返回之前运行您的处理程序代码。您的原始IVsSolutionEvents处理程序将在后续解决方案加载中调用。

您可能还需要考虑注册/使用基于规则的UI上下文,如下所述:

如何:对Visual Studio扩展使用基于规则的UI上下文

诚挚的,Ed Dore