2011-10-19 18 views
13

Sto sviluppando un servizio linguistico per Visual Studio tramite VSPackage. Devo aggiornare i miei dati di analisi ogni volta che i file vengono aggiunti/rimossi dai progetti della soluzione.Come iscriversi alla soluzione e agli eventi del progetto da un VSPackage

Desidero iscrivermi agli eventi di soluzione e progetto.

Ho provato come segue, ma nessuno di questi eventi viene generato quando aggiungo/rimuovi progetti alla soluzione o aggiungo/rimuovo gli elementi nei progetti.

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; 

Qual è il modo migliore per iscriversi a questi eventi da un VSPackage? Qualsiasi aiuto apprezzato!

risposta

8

DTE Gli eventi sono un po 'strani, è necessario memorizzare nella cache l'oggetto di origine evento (SolutionEvents e ProjectItemEvents nel tuo caso), in modo che COM Interop sappia mantenerli in vita.

public class MyClass 
{ 
    SolutionEvents solutionEvents; 

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

Maggiori informazioni su questo @http://msdn.microsoft.com/en-us/library/ms165650(v=vs.80).aspx

12

In alternativa è possibile utilizzare IVsSolutionEvents3, che ha manifestazioni molto meglio

[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

Qual è '_hSolutionEvents' in' Initialize() '? –

+0

Hai ragione, hai aggiornato il codice! Ho pensato un altro a '_hSolutionEvents', che è necessario per' UnadviseSolutionEvents' e aggiungo anche il nuovo codice. E 'un peccato che non ci siano molte informazioni su come creare plugin per il miglior DevTool ... – FooBarTheLittle

5

Lets focus sul ProjectAdded evento (anche se problema descritto è esattamente lo stesso per il resto degli eventi).

L'esempio di codice che hai mostrato tenta di registrare il gestore SolutionEvents_ProjectAdded per l'evento ProjectAdded. Tuttavia, l'oggetto SolutionEvents che espone l'evento ha una durata limitata alla chiusura del suo metodo di avvolgimento (non hai mostrato la sua firma, chiamiamolo Connect). Dopo che il flusso di controllo aveva lasciato tale ambito, oggetto locale è già stato garbage collection, per cui il suo caso non viene mai chiamato:

codice rotto:

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

Per rimediare, è necessario assegnare il SolutionEvents oggetto di alcune variabili, la cui durata si estende su un gestore SolutionEvents_ProjectAdded - ad es. sull'intera classe di wrapping. Nel seguente esempio, la portata si estende sopra il tipo intera (che chiameremo Connector), e assicura che il gestore è accessibile durante la durata di questo tipo:

codice fisso:

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

Per essere più precisi, controllare questo riferimento MSDN - Scoping Variables Appropriately in Event Handlers:

un errore comune nei gestori di eventi di programmazione si connette il gestore di eventi a un oggetto che è stato dichiarato w con uno scope troppo limitato allo scopo di gestire l'evento. L'oggetto deve avere una durata che si estende non solo sulla funzione che collega il metodo callback come gestore di eventi dell'oggetto, ma anche sul metodo callback in cui l'evento viene effettivamente gestito.Altrimenti, se l'oggetto è fuori ambito e non è più definito nel metodo callback , il metodo di callback non viene chiamato e l'evento non viene gestito come .

Problemi correlati