【问题标题】:How do I subscribe to solution and project events from a VSPackage如何从 VSPackage 订阅解决方案和项目事件
【发布时间】:2011-12-11 03:33:22
【问题描述】:

我正在通过 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 订阅这些事件的最佳方式是什么?

【问题讨论】:

    标签: c# visual-studio-extensions vspackage mpf


    【解决方案1】:

    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】:

      您也可以使用 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; }
      }
      

      【讨论】:

      • Initialize() 中的_hSolutionEvents 是什么?
      • 你是对的,更新了代码!对_hSolutionEvents 有另一个想法,UnadviseSolutionEvents 需要它并添加新代码。遗憾的是,没有太多关于为最好的 DevTool 制作插件的信息......
      【解决方案3】:

      让我们关注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 参考 - Scoping Variables Appropriately in Event Handlers

      编程事件处理程序的一个常见错误是连接事件 处理程序到已声明的范围太有限的对象 用于处理事件。对象必须有生命 不仅跨越连接回调的函数 方法作为对象的事件处理程序,也可以通过回调 实际处理事件的方法本身。否则,如果 对象超出范围,不再在回调中定义 方法,不调用回调方法,不处理事件 根据需要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多