我正在通过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;
从VSPackage订阅这些事件的最佳方法是什么?任何帮助表示赞赏!
答案 0 :(得分:13)
或者你可以使用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; }
}
答案 1 :(得分:10)
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
答案 2 :(得分:5)
让我们关注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参考 - Scoping Variables Appropriately in Event Handlers:
编程事件处理程序时常见的错误是连接事件 已声明范围太有限的对象的处理程序 为了处理事件。对象必须有生命 它不仅涵盖连接回调的函数 方法作为对象的事件处理程序,也可以通过回调 实际处理事件的方法本身。否则,如果 对象超出范围,不再在回调中定义 方法,不调用回调方法,不处理事件 根据需要。