Как подписаться на решение и события проекта из VSPackage - PullRequest
15 голосов
/ 19 октября 2011

Я разрабатываю языковой сервис для Visual Studio через VSPackage. Мне нужно обновлять данные анализа, когда файлы добавляются / удаляются из проектов решения.

Я хочу подписаться на решение и события проекта.

Я попытался сделать следующее, но ни одно из этих событий не запускается, когда я добавляю / удаляю проекты в решении или добавляю / удаляю элементы в проектах.

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;

Какой лучший способ подписаться на эти события из VSPackage? Любая помощь приветствуется!

Ответы [ 3 ]

13 голосов
/ 12 июня 2012

В качестве альтернативы вы можете использовать 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; }
}
10 голосов
/ 30 декабря 2011

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

6 голосов
/ 12 апреля 2014

Позволяет сосредоточиться на событии 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
    }
}

Чтобы это исправить, вам нужно присвоить объект 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 - Соответствующие переменные области видимости в обработчиках событий :

Распространенной ошибкой в ​​программировании обработчиков событий является подключение события обработчик объекта, который был объявлен с слишком ограниченной областью действия с целью обработки события. У объекта должна быть жизнь который охватывает не только функцию, которая соединяет обратный вызов Метод как обработчик события объекта, но также и через обратный вызов сам метод, где событие фактически обрабатывается. В противном случае, если объект находится вне области видимости и больше не определяется в обратном вызове метод, метод обратного вызова не вызывается и событие не обрабатывается по желанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...