Как подписаться на решение и события проекта из VSPackage
Я разрабатываю языковой сервис для 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 ответа
В качестве альтернативы вы можете использовать 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
событие (хотя описанная проблема точно такая же для остальных событий).
Пример кода, который вы показали, пытается зарегистрировать 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 - Области применения переменных соответственно в обработчиках событий:
Распространенной ошибкой в программировании обработчиков событий является подключение обработчика событий к объекту, который был объявлен с областью, слишком ограниченной для обработки события. У объекта должна быть жизнь, охватывающая не только функцию, которая соединяет метод обратного вызова как обработчик события объекта, но также и сам метод обратного вызова, где событие фактически обрабатывается. В противном случае, если объект находится вне области и больше не определен в методе обратного вызова, метод обратного вызова не вызывается, и событие не обрабатывается должным образом.