Sviluppare un Add-In per Reflector
Introduzione
Reflector è IL tool che ogni sviluppatore .net deve avere installato nella propria macchina. E' stato sviluppato da Lutz Roeder e permette di decompilare eseguibili e dll .net per poter comprendere il codice scritto all'interno. Usato per scopi di studio, è uno strumento formidabile, in quanto permette di rendersi conto di come è stata implementata una caratteristica del Framework, oppure di come potrebbe essere realizzata una funzionalità particolare molto simile ad un'altra già presente in qualche assembly del Framework .net.
L'applicazione si presenta con un'interfaccia explorer-like, con l'insieme degli assembly "indagati" in una treeview sulla sinistra e con un'area adibita a ricerca e browser di codice sulla destra. Non vado oltre nella descrizione delle funzionalità di questa perla di programmazione, invitando chiunque (ma credo siano pochissimi) non conosca Reflector a scaricarlo a questo indirizzo. Non resterete delusi.
Estendibilità ed Add-Ins
Una delle features più interessanti, e forse più "segrete" di Reflector, è la sua espandibilità. Il creatore del programma ha infatti previsto la possibilità di creare nuove funzionalità che possono essere integrate all'interno dell'interfaccia di Reflector in modo abbastanza semplice. In questo articolo andremo a realizzare un Add-In minimo, ovvero una semplice finestra che visualizzi un pulsante all'interno dell'interfaccia di Reflector.
Creazione del progetto
Per prima cosa creiamo una soluzione di Visual Studio 2002 di tipo Library. Avete capito bene: Visual Studio 2002...infatti reflector è ancora compilato con il .net Framework 1.0 ed il riferimento a mscorlib.dll è alla versione 1.0.3300.0, potete controllare utilizzando Reflector per decompilare... Reflector.exe!!! Una volta creata la soluzione ed il progetto di tipo dll, è il momento di creare il riferimento all'assembly Reflector.exe, che contiene le interfacce che dovremo andare ad implementare e gli oggetti che dovremo creare per gestire il nostro add-in. Qui nasce il primo problema, solitamente, in quanto con VS2002 non è possibile creare un riferimento ad un file eseguibile. La soluzione potete trovarla in un mio precedente tip, e consiste nella modifica "a mano" del file di progetto della nostra library. Una volta aggiunto il riferimento, è possibile cominciare ad esplorare ed utilizzare le interfacce e le classi che ci mette a disposizione Reflector. Non è scopo di questo articolo elencare le classi e le interfacce, bensì vedremo come creare un semplice add-in, e per crearlo dobbiamo, come prima cosa, aggiungere una classe che chiameremo ClassDumperPackage al nostro progetto.
Questa classe dovrà implementare l'interfaccia IPackage che è fondamentale per l'integrazione all'interno di Reflector. L'interfaccia IPackage contiene 2 metodi Load(ByVal serviceProvider As System.IServiceProvider) e Unload() che vengono invocati al caricamento del plugin. L'informazione più importante sta in quell'oggetto serviceProvider che viene passato alla funzione Load. Esso espone infatti il metodo GetService() che permette di referenziare servizi base del motore di reflector all'interno del nostro plugin.
Tra i servizi più importanti possiamo ricordare:
- IWindowManager: si occupa della parte di interfaccia di reflector e permette di aggiungere una nuova finestra alla collection delle finestre visibili.
- ICommandBarManager: si occupa della gestione delle toolbar e dei menù di Reflector. Utilizzando tale servizio è possibile aggiungere un pulsante in un menu o in una toolbar
- IAssemblyBrowser: permette di conoscere l'elemento selezionato nella TreeView
- IAssemblyManager: permette tra le altre cose, di caricare, scaricare ed elencare gli assembly caricati nella TreeView principale
- ILanguageManager: gestisce i linguaggi disponibili per effettuare le decompilazioni. Solitamente sono VB.net e C#, ma nelle ultime versione di Reflector sono stati aggiunti l'IL, Delphi e C++
- ITranslatorManager: è il servizio di traduzione che permette di disassemblare classi, metodi e namespaces.
Nel nostro caso, per semplificare il template d'esempio, non andremo a complicare ulteriormente il codice e ci limiteremo ad utilizzare i servizi per lasciare ad un altro articolo l'approfondimento relativo alla decompilazione.
Prima di scrivere qualsiasi riga di codice, consideriamo anche il fatto che il nostro plugin dovrà avere un'interfaccia. Questa viene implementata attraverso l'uso di uno UserControl che andremo a battezzare ClassDumperWindow. E' da notare che tale controllo non deve obbligatoriamente implementare alcuna interfaccia, ma sarà bene creare al suo interno una serie di proprietà pubbliche atte a mantenere i riferimenti ai servizi che dovranno essere utilizzati dalle funzionalità che andremo a sviluppare.
Vediamo a questo punto il codice minimo della classe ClassDumperWindow che permette di caricare il nostro usercontrol all'interno dell'interfaccia di Reflector, dandoci la possibilità di utilizzare tutti i servizi che quest'ultimo espone.
Imports Reflector
Public Class ClassDumperPackage
Implements IPackage
Private _button As ICommandBarButton 'Pulsante per richiamare il plugin
Private _commandBarManager As ICommandBarManager 'Toolbar nella quale inserire il pulsante
Private _separator As ICommandBarSeparator 'Separatore (per chiarezza)
Private _windowManager As IWindowManager 'Servizio di gestione interfaccia di Reflector
'''Codice da eseguire al caricamento del plugin
Public Sub Load(ByVal serviceProvider As System.IServiceProvider) Implements IPackage.Load
'Creazione interfaccia
Dim _classDumperWindow As New ClassDumperWindow()
'ottengo un riferimento al servizio di interfaccia
_windowManager = DirectCast(serviceProvider.GetService(GetType(IWindowManager)), IWindowManager)
'aggiungo il mio usercontrol alla collection delle finestre di Reflector
_windowManager.Windows.Add("ClassDumperWindow", _classDumperWindow, "Class Dumper")
'ottengo un riferimento al servizio di toolbar
_commandBarManager = DirectCast(serviceProvider.GetService(GetType(ICommandBarManager)), ICommandBarManager)
'Aggiungo un separatore
_separator = _commandBarManager.CommandBars.Item("Tools").Items.AddSeparator
'Aggiungo un pulsante per attivare il plugin
_button = _commandBarManager.CommandBars.Item("Tools").Items.AddButton("&Class Dumper", New EventHandler(AddressOf ClassDumperButtonHandler))
End Sub
'''Codice da eseguire allo scaricamento del plugin
Public Sub Unload() Implements IPackage.Unload
'Rimuovo la finstra il pulsante ed il separatore che ho creato nella fase di caricamento
_windowManager.Windows.Remove("ClassDumperWindow")
_commandBarManager.CommandBars.Item("Tools").Items.Remove(_button)
_commandBarManager.CommandBars.Item("Tools").Items.Remove(_separator)
End Sub
''' Gestore del click del pulsante
Private Sub ClassDumperButtonHandler(ByVal sender As Object, ByVal e As EventArgs)
'rendo visibile l'interfaccia del mio plugin
_windowManager.Windows.Item("ClassDumperWindow").Visible = True
End Sub
End Class
Lo UserControl includerà solo un pulsante che al click farà comparire una messagebox con un saluto, quindi non riporto il codice relativo...
A questo punto, compilato l'assembly, è possibile registrarlo all'interno degli Add-In di Reflector seguendo i passi:
- Copiare l'assembly nella directory di Reflector
- Aprire Reflector
- Selezionare il menu View>Add-Ins...
- Scegliere Add... e selezionare l'assembly che abbiamo appena compilato.
Se tutto è stato fatto nel modo corretto, nel menù tools dovrebbe comparire una voce "Class Dumper" che permetterà, una volta cliccata, di visualizzare il nostro UserControl incorporato nell'interfaccia di Reflector.
Conclusioni
In questo articolo abbiamo visto come scrivere il codice "minimo" per far partire un Add-In con Interfaccia all'interno di .net Reflector. In un successivo articolo approfondiremo l'uso dei servizi esposti da Reflector. Nel frattempo, scaricate il codice sorgente e buoni Add-Ins a tutti!!
indietro