如何从vpackage订阅解决方案和项目事件

本文关键字:解决方案 项目 事件 vpackage | 更新日期: 2023-09-27 18:11:39

我正在通过vpackage为Visual Studio开发语言服务。当文件从解决方案的项目中添加/删除时,我需要更新我的解析数据。

我想订阅解决方案和项目事件。

我尝试了如下操作,但是当我向解决方案添加/删除项目或向项目添加/删除项时,这些事件都不会被触发。

DTE dte = (DTE)languageService.GetService(typeof(DTE));
if (dte == null)
    return;
((Events2)dte.Events).SolutionEvents.ProjectAdded += SolutionEvents_ProjectAdded;
((Events2)dte.Events).SolutionEvents.ProjectRemoved += SolutionEvents_ProjectRemoved;
((Events2)dte.Events).ProjectItemsEvents.ItemAdded += ProjectItemsEvents_ItemAdded;
((Events2)dte.Events).ProjectItemsEvents.ItemRemoved += ProjectItemsEvents_ItemRemoved;

从vpackage中订阅这些事件的最佳方式是什么?

如何从vpackage订阅解决方案和项目事件

或者您可以使用IVsSolutionEvents3,它具有更好的事件

[PackageRegistration( UseManagedResourcesOnly = true )]
[InstalledProductRegistration( "#110", "#112", "1.0", IconResourceID = 400 )]
// add these 2 Annotations to execute Initialize() immediately when a project is loaded
[ProvideAutoLoad( VSConstants.UICONTEXT.SolutionHasSingleProject_string )]
[ProvideAutoLoad( VSConstants.UICONTEXT.SolutionHasMultipleProjects_string )]
[Guid( GuidList.XYZ )]
public sealed class UnityProjectUpdateHandlerPackage : Package, IVsSolutionEvents3
{
    private DTE _dte;
    private IVsSolution solution = null;
    private uint _hSolutionEvents = uint.MaxValue;
    protected override void Initialize()
    {
        Trace.WriteLine( string.Format( CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString() ) );
        base.Initialize();
        this._dte = (DTE) this.GetService( typeof( DTE ) );
        AdviseSolutionEvents();
    }
    protected override void Dispose( bool disposing )
    {
        UnadviseSolutionEvents();
        base.Dispose( disposing );
    }
    private void AdviseSolutionEvents()
    {
        UnadviseSolutionEvents();
        solution = this.GetService( typeof( SVsSolution ) ) as IVsSolution;
        if ( solution != null )
        {
            solution.AdviseSolutionEvents( this, out _hSolutionEvents );
        }
    }
    private void UnadviseSolutionEvents()
    {
        if ( solution != null )
        {
            if ( _hSolutionEvents != uint.MaxValue )
            {
                solution.UnadviseSolutionEvents( _hSolutionEvents );
                _hSolutionEvents = uint.MaxValue;
            }
            solution = null;
        }
    }
    private Project[] GetProjects()
    {
        return _dte.Solution.Projects
            .Cast<Project>()
            .Select( x => ( (VSProject) x.Object ).Project )
            .ToArray();
    }
    public int OnAfterLoadProject( IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy )
    {
        // Do something
        return VSConstants.S_OK;
    }
    public int OnAfterOpenSolution( object pUnkReserved, int fNewSolution )
    {
        foreach ( var project in GetProjects() )
            ; // Do something
        return VSConstants.S_OK;
    }
    public int OnBeforeUnloadProject( IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy )
    {
        // Do something
        return VSConstants.S_OK;
    }
    public int OnAfterCloseSolution( object pUnkReserved )
    { return VSConstants.S_OK; }
    public int OnAfterClosingChildren( IVsHierarchy pHierarchy )
    { return VSConstants.S_OK; }
    public int OnAfterMergeSolution( object pUnkReserved )
    { return VSConstants.S_OK; }
    public int OnAfterOpenProject( IVsHierarchy pHierarchy, int fAdded )
    { return VSConstants.S_OK; }
    public int OnAfterOpeningChildren( IVsHierarchy pHierarchy )
    { return VSConstants.S_OK; }
    public int OnBeforeCloseProject( IVsHierarchy pHierarchy, int fRemoved )
    { return VSConstants.S_OK; }
    public int OnBeforeClosingChildren( IVsHierarchy pHierarchy )
    { return VSConstants.S_OK; }
    public int OnBeforeOpeningChildren( IVsHierarchy pHierarchy )
    { return VSConstants.S_OK; }
    public int OnBeforeCloseSolution( object pUnkReserved )
    { return VSConstants.S_OK; }
    public int OnQueryCloseProject( IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel )
    { return VSConstants.S_OK; }
    public int OnQueryCloseSolution( object pUnkReserved, ref int pfCancel )
    { return VSConstants.S_OK; }
    public int OnQueryUnloadProject( IVsHierarchy pRealHierarchy, ref int pfCancel )
    { return VSConstants.S_OK; }
}

DTE事件有点奇怪,您需要缓存事件源对象(在您的例子中是SolutionEvents和ProjectItemEvents),以便COM Interop知道保持它们活动。

public class MyClass
{
    SolutionEvents solutionEvents; 
    public void ConnectToEvents()
    {
        solutionEvents = ((Events2)dte.Events).SolutionEvents; 
        solutionEvents.ProjectAdded += OnProjectAdded; 
        // Etc 
    }
}

更多信息@ http://msdn.microsoft.com/en-us/library/ms165650(v=vs.80).aspx

让我们关注ProjectAdded事件(尽管所描述的问题与其他事件完全相同)。

您所展示的代码示例试图为ProjectAdded事件注册SolutionEvents_ProjectAdded处理程序。但是,暴露事件的SolutionEvents对象的生命周期范围仅限于其包装方法的闭包(您还没有显示其签名—让我们称其为Connect)。控制流离开该作用域后,本地对象已经被垃圾收集,因此它的事件永远不会被调用:

破代码:

public class Connector
{
    public void Connect()
    {
        ((Events2)dte.Events).SolutionEvents.ProjectAdded += SolutionEvents_ProjectAdded;
    }
    void SolutionEvents_ProjectAdded() 
    { 
        // callback is dead
    }
}

要解决这个问题,您需要将SolutionEvents对象分配给某个变量,该变量的生命周期跨越SolutionEvents_ProjectAdded处理程序-例如整个包装类。在下面的示例中,作用域扩展到整个类型(我们称之为Connector),并确保在该类型的生命周期内处理程序是可访问的:

固定代码:

public class Connector
{
    SolutionEvents _solutionEvents;
    public void Connect()
    {
        _solutionEvents = ((Events2)dte.Events).SolutionEvents; 
        _solutionEvents.ProjectAdded += SolutionEvents_ProjectAdded;
    }
    void SolutionEvents_ProjectAdded() 
    { 
        // callback works
    }
}

要更精确,请检查MSDN引用-在事件处理程序中适当地限定变量的范围:

在编程事件处理程序时的一个常见错误是连接事件使用作用域太有限的对象声明的处理程序用于处理事件。对象必须有生命这不仅跨越了连接回调的函数方法作为对象的事件处理程序,还可以通过回调方法本身,在其中实际处理事件。否则,如果对象超出了作用域,并且不再在回调中定义方法,则不调用回调方法并且不处理事件。