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

Ant*_*ien 16 mpf vspackage visual-studio-extensions

我正在通过VSPackage为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;
Run Code Online (Sandbox Code Playgroud)

从VSPackage订阅这些事件的最佳方法是什么?任何帮助赞赏!

Foo*_*tle 14

或者你可以使用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; }
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*ice 11

DTE事件有点奇怪,您需要缓存事件源对象(在您的情况下为SolutionEvents和ProjectItemEvents),以便COM Interop知道让它们保持活动状态.

public class MyClass
{
    SolutionEvents solutionEvents; 

    public void ConnectToEvents()
    {
        solutionEvents = ((Events2)dte.Events).SolutionEvents; 
        solutionEvents.ProjectAdded += OnProjectAdded; 
        // Etc 
    }
}
Run Code Online (Sandbox Code Playgroud)

更多关于这个@ http://msdn.microsoft.com/en-us/library/ms165650(v=vs.80).aspx


jwa*_*zko 7

让我们关注ProjectAdded事件(虽然描述的问题与其他事件完全相同).

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

破碎的代码:

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

要解决这个问题,您需要将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
    }
}
Run Code Online (Sandbox Code Playgroud)

更准确地说,请检查此MSDN参考 - 在事件处理程序中适当地确定范围变量:

编程事件处理程序时常见的错误是将事件处理程序连接到一个对象,该对象已被声明的范围太有限,无法处理事件.该对象的生命不仅要跨越将回调方法作为对象的事件处理程序连接的函数,还要跨越实际处理事件的回调方法本身.否则,如果对象超出范围并且不再在回调方法中定义,则不会调用回调方法,并且不会根据需要处理事件.