State scrivendo un’estensione per Visual Studio e avete bisogno di intervenire nel momento in cui si passa da design a debug e viceversa. Spulciando la scarnissima documentazione trovata la soluzione, o presunta tale, abbastanza rapidamente, anzi trovate più di una di strada per arrivare al risultato desiderato:
this.dte.Events.DTEEvents.ModeChanged += ....
this.dte.Events.DebuggerEvents.OnEnterDesignMode += ...
this.dte.Events.DebuggerEvents.OnEnterRunMode += ...
funziona a singhiozzo… Smile, l’inghippo principale è che VS è un gigantesco oggettone COM con un po’ di .Net intorno quindi scrivendo così succede che il vostro event handler viene collectato dal Garbage Collector al primo giro perché nessuno si rende conto che avete una reference, ad esempio, a DTEEvents… aggirare il problema è comunque abbastanza facile:
var events = this.dte.Events;
if( events != null )
{
     this.dteEvents = events.DTEEvents;
     if( this.dteEvents != null )
     {
         this.dteEvents.ModeChanged += new _dispDTEEvents_ModeChangedEventHandler( OnDTEModeChanged );
     }
}
Funziona, ma è comunque la strada sbagliata. La documentazione dice chiaro e tondo che tutti quegli eventi sono ad uso interno (punto).
Quindi?
Non è proprio così immediato seguire la strada ufficiale ma si può fare, in primis recuperiamo una reference a un servizio che si chiama IVsMonitorSelection:
var monitorSelectionService = this.GetService( typeof( IVsMonitorSelection ) ) as IVsMonitorSelection;
Fatto questo possiamo chiedere al servizio di essere notificati quando succede qualcosa all’interno dell’IDE, nello specifico ci interessa sapere quando cambia lo UI Context, purtroppo non è così semplice come agganciare un event handler; la prima cosa che dobbiamo fare è implementare da qualche parte (tipicamente dove abbiamo bisogno di ricevere la notifica) l'interfaccia IVsSelectionEvents:
public class MyToolWindowViewModel : ..., IVsSelectionEvents
{
}
L’interfaccia prevede che vengano implementati 3 metodi:
int IVsSelectionEvents.OnCmdUIContextChanged( uint dwCmdUICookie, int fActive )
{
     return VSConstants.S_OK;
}
 
int IVsSelectionEvents.OnElementValueChanged( uint elementid, object varValueOld, object varValueNew )
{
     return VSConstants.S_OK;
}
 
int IVsSelectionEvents.OnSelectionChanged( IVsHierarchy pHierOld, uint itemidOld, IVsMultiItemSelect pMISOld, ISelectionContainer pSCOld, IVsHierarchy pHierNew, uint itemidNew, IVsMultiItemSelect pMISNew, ISelectionContainer pSCNew )
{
     return VSConstants.S_OK;
}
A noi interessa solo il primo, quindi gli altri due li lasciamo con la semplice “return OK”.
OnCmdUIContextChanged viene chiamato una quantità spaventosa di volte praticamente per ogni minima variazione di contesto all’interno dell’IDE, è potentissimo perché vi permette di sapere pressoché tutto quello che succede, è altrettanto scomodissimo sapere che cosa succede:
uint debuggerCookie;
var debugging_guid = VSConstants.UICONTEXT.Debugging_guid;
ErrorHandler.ThrowOnFailure( this.monitorSelectionService.GetCmdUIContextCookie( ref debugging_guid, out debuggerCookie ) );
if( debuggerCookie == dwCmdUICookie )
{
     this.OnDTEModeChanged();
}
Brutto… Smile with tongue out ma che cosa succede:
  1. per ogni variazione di stato l’IDE ci chiama passandoci:
  1. un “cookie” che possiamo considerare come un “token” che identifica la cosa che è cambiata;
  2. il nuovo stato: attivo (1), disattivo (0);
A questo punto abbiamo bisogno di sapere se il cookie è quello che fa per noi:
  1. Recuperiamo un riferimento al GUID che rappresenta lo “UI Context” che interessa a noi, nel nostro caso Debugging;
  2. Passiamo il GUID in questione a GetCmdUIContextCookie e otteniamo in cambio il cookie/token che rappresenta quel contesto;
  3. Se il cookie coincide con quello che ci viene passato significa che il contesto che è cambiato è quello che interessa a noi;
Possiamo quindi intervenire di conseguenza: OnDTEModeChanged() è un metodo della mia estensione che si limita a prendere una decisione sulla base del nuovo stato:
void OnDTEModeChanged()
{
     switch( this.dte.Mode )
     {
         case vsIDEMode.vsIDEModeDebug:
             {
                 //Siamo in debug mode
             }
             break;

         case vsIDEMode.vsIDEModeDesign:
             {
                 //Siamo in design mode
             }
             break;
     }
}
Semplice no? Smile
.m