2011-10-19 10 views
13

Estoy desarrollando un servicio de idioma para Visual Studio a través de VSPackage. Necesito actualizar mis datos de análisis cuando los archivos se agregan/eliminan de los proyectos de la solución.¿Cómo me suscribo a los eventos de solución y proyecto de un paquete VSPackage?

Deseo suscribirme a eventos de soluciones y proyectos.

Intenté lo siguiente, pero ninguno de estos eventos se dispara cuando agrego/quito proyectos a la solución o agrego/quito elementos a los proyectos.

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; 

¿Cuál es la mejor manera de suscribirse a estos eventos de un VSPackage? Cualquier ayuda apreciada!

Respuesta

8

DTE Los eventos son un poco raros, debe almacenar en caché el objeto de origen del evento (SolutionEvents y ProjectItemEvents en su caso), para que COM Interop sepa que los mantiene vivos.

public class MyClass 
{ 
    SolutionEvents solutionEvents; 

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

Más información sobre este @http://msdn.microsoft.com/en-us/library/ms165650(v=vs.80).aspx

12

otra posibilidad es utilizar IVsSolutionEvents3, que tiene mucho mejores eventos

[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; } 
} 
+0

¿Qué es '_hSolutionEvents' en' Initialize() '? –

+0

¡Tiene razón, actualizó el código! Pensé en '_HSolutionEvents' que se necesita para' UnadviseSolutionEvents' y también agregué el nuevo código. Es una pena que no haya mucha información sobre cómo hacer complementos para la mejor DevTool ... – FooBarTheLittle

5

permite centrarse en ProjectAdded caso (aunque problema descrito es exactamente el mismo para el resto de los eventos).

El ejemplo de código que ha mostrado intenta registrar el controlador SolutionEvents_ProjectAdded para el evento ProjectAdded. Sin embargo, el objeto SolutionEvents que expone el evento, tiene un alcance vitalicio limitado al cierre de su método de envoltura (no ha mostrado su firma, llamémoslo Connect). Después de que el flujo de control había dejado que alcance, objeto local ya ha sido basura recogida, por lo que su evento nunca se llama:

código roto:

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

Para corregir esto, es necesario asignar el SolutionEvents objetar a alguna variable, cuya duración abarca el controlador SolutionEvents_ProjectAdded, por ejemplo sobre toda la clase de envoltura. En el siguiente ejemplo, el alcance se extiende sobre toda el tipo (llamémoslo Connector), y asegura que el controlador es accesible durante la vida útil de ese tipo:

código fijo:

public class Connector 
{ 
    SolutionEvents _solutionEvents; 
    public void Connect() 
    { 
     _solutionEvents = ((Events2)dte.Events).SolutionEvents; 
     _solutionEvents.ProjectAdded += SolutionEvents_ProjectAdded; 
    } 
    void SolutionEvents_ProjectAdded() 
    { 
     // callback works 
    } 
} 

Para ser más precisos, comprobar esta referencia MSDN - Scoping Variables Appropriately in Event Handlers:

un error común en los controladores de eventos de programación está conectando el controlador de eventos a un objeto que ha sido declarado w con un alcance demasiado limitado con el fin de manejar el evento. El objeto debe tener una vida que abarque no solo la función que conecta el método de devolución de llamada como un controlador de eventos del objeto, sino también a través del método de devolución de llamada donde se maneja realmente el evento.De lo contrario, si el objeto está fuera del alcance y ya no está definido en el método de devolución de llamada , no se llama al método de devolución de llamada y el evento no se maneja como según se desee.

Cuestiones relacionadas