VSPackage: sapere se siete in “debug mode” o “design mode”
funziona a singhiozzo… , 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:this.dte.Events.DTEEvents.ModeChanged += .... this.dte.Events.DebuggerEvents.OnEnterDesignMode += ... this.dte.Events.DebuggerEvents.OnEnterRunMode += ...
Funziona, ma è comunque la strada sbagliata. La documentazione dice chiaro e tondo che tutti quegli eventi sono ad uso interno (punto).var events = this.dte.Events; if( events != null ) { this.dteEvents = events.DTEEvents; if( this.dteEvents != null ) { this.dteEvents.ModeChanged += new _dispDTEEvents_ModeChangedEventHandler( OnDTEModeChanged ); } }
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:
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:var monitorSelectionService = this.GetService( typeof( IVsMonitorSelection ) ) as IVsMonitorSelection;
L’interfaccia prevede che vengano implementati 3 metodi:public class MyToolWindowViewModel : ..., IVsSelectionEvents { }
A noi interessa solo il primo, quindi gli altri due li lasciamo con la semplice “return OK”.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; }
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:
Brutto… ma 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(); }
- per ogni variazione di stato l’IDE ci chiama passandoci:
- un “cookie” che possiamo considerare come un “token” che identifica la cosa che è cambiata;
- il nuovo stato: attivo (1), disattivo (0);
- Recuperiamo un riferimento al GUID che rappresenta lo “UI Context” che interessa a noi, nel nostro caso Debugging;
- Passiamo il GUID in questione a GetCmdUIContextCookie e otteniamo in cambio il cookie/token che rappresenta quel contesto;
- Se il cookie coincide con quello che ci viene passato significa che il contesto che è cambiato è quello che interessa a noi;
Semplice no?void OnDTEModeChanged() { switch( this.dte.Mode ) { case vsIDEMode.vsIDEModeDebug: { //Siamo in debug mode } break; case vsIDEMode.vsIDEModeDesign: { //Siamo in design mode } break; } }
.m