modello di eventi

Transcript

modello di eventi
EDITORIALE
online.infomedia.it
n. 66 - novembre/dicembre 2005
bimestrale - anno undicesimo
Direttore Responsabile
Marialetizia Mari ([email protected])
Direttore Esecutivo
Il valore
della conoscenza
Francesco Balena ([email protected])
Managing Editor
Renzo Boni ([email protected])
Collaboratori
Filippo Bonanni
Stefano Corti
Raffaele Di Natale
Maurizio Mammuccini
Carlo Pagliei
Fabio Perrone
Alberto Rosotti
Lorenzo Vandoni
Direzione
Natale Fino (nfi[email protected])
Marketing & Advertising
I
n tanti articoli su questo giornale abbiamo parlato delle tante
bellissime novità di Visual Studio, di .NET Framework 2.0 e delle nuove versioni di VB e C. In questo editoriale proverò invece
a fare qualche riflessione più “metodologica” che “tecnologica”.
Partiamo con i linguaggi VB e C#, che sono stati potenziati tantissimo con generics e nuove keyword. Una delle mie preferite è
l’istruzione Using, che è sempre stata presente in C# e ora è stata
finalmente aggiunta anche a VB. Questa istruzione è importante
perchè aiuta a scrivere correttamente applicazioni che usano
risorse di sistema e le rilasciano correttamente, permettendo di evitare resource leak.
Peccato che solo una minoranza di sviluppatori C# la usi correttamente, come ho potuto
notare durante le code review e grazie a qualche domanda mirata nei miei seminari e
conferenze.
Segreteria: 0587/736460
[email protected]
Amministrazione
Sara Mattei
([email protected])
Grafica
Manola Greco ([email protected])
Technical Book
Lisa Vanni ([email protected])
Segreteria
Enrica Nassi
([email protected])
Stampa
TIPOLITOGRAFIA PETRUZZI
Citta’ di Castello (PG)
Ufficio Abbonamenti
Tel. 0587/736460 - Fax 0587/732232
e-mail: [email protected]
www.infomedia.it
Gruppo Editoriale Infomedia srl
Via Valdera P., 116 - 56038 Ponsacco (PI) Italia
Tel. 0587/736460 - Fax 0587/732232
[email protected]
Sito Web www.infomedia.it
Passiamo a ClickOnce, che viene presentato come una rivoluzione nel mondo delle
applicazioni Windows. Tutto vero, se non fosse che questa tecnologia è già disponibile
dalla versione 1.0 di .NET, in forma meno sofisticata ma sufficientemente potente da
poter essere usata in applicazioni reali. Mentre tutti si buttavano sul Web, alcune piccole
aziende hanno investito su questa tecnologia e hanno sviluppato – in meno tempo e con
meno spese – applicazioni più potenti e usabili della maggior parte delle applicazioni ASP.
NET. Questo è un esempio di come il deficit di conoscenza possa portare a dilatare tempi
e spese di sviluppo, e in definitiva a perdere occasioni commerciali.
Il terzo esempio che mi viene in mente è il data binding nelle applicazioni Windows Forms,
che è stato potenziato in modo considerevole grazie alla introduzione del BindingSource,
un oggetto mediator tra la sorgente dati (es. un DataSet) e i controlli che mostrano i dati
stessi. Oggetto presentato come una grande innovazione, se non fosse che un oggetto
mediator molto simile è disponibile da sempre, sotto il nome di DataView. Per colpa del
deficit di informazione, molti sviluppatori hanno rinunciato a una tecnologia molto utile
(oppure hanno dovuto complicare il proprio codice all’inverosimile), ancora una volta
allungando inutilmente i tempi di sviluppo.
Dove portano queste mie considerazioni? Ovviamente al fatto che la potenza degli strumenti
– i linguaggi, il framework, l’ambiente di sviluppo – vale ben poco se non è accompagnata
dalla conoscenza delle possibilità (e dei limiti) delle tecnologie. A questi deficit si può
supplire solo con la volontà di rimanere sempre aggiornati, leggendo non solo i manuali
“ufficiali” (spesso molto carenti) ma libri, riviste come Visual Basic & .NET Journal, siti
Internet, blog, e via elencando.
Francesco Balena
[email protected]
Manoscritti e foto originali anche se non pubblicati,
non si restituiscono. È vietata la riproduzione
anche parziale di testi e immagini.
Si prega di inviare i comunicati stampa e gli inviti stampa per
la redazione all’indirizzo: [email protected]
Visual Basic Journal è una rivista di
Gruppo Editoriale Infomedia S.r.l. Via Valdera P, 116 Ponsacco - Pisa.
Registrazione presso il Tribunale di Pisa n. 20/1999
N. 66 - Novembre/Dicembre 2005 VBJ
5
NOVEMBRE/DICEMBRE
N.66
SOMMARIO
SPECIALE
.NET: Delegate e modello di eventi
8
I delegate, apparentemente strani e complicati, sono in realtà un facile meccanismo per una corretta strutturazione
OOP della programmazione ad eventi nel .NET Framework. È importante comprenderne il funzionamento per sviluppare corrette applicazioni event-driven.
di Maurizio Mammuccini
TECNICHE
18
Application e User Settings in Visual Basic 2003
di Francesco Balena
APPLICAZIONI
28
Controllo Remoto in Visual Basic .NET (terza puntata)
di Stefano Corti
.NET
36
Interprocess Communication con MSMQ e .NET Framework
Quasi tutte le applicazioni oggigiorno devono comunicare con l’esterno; vediamo un approccio alla comunicazione fra processi utilizzando la libreria MSMQ, che permette di scrivere applicazioni sicure e aperte ai futuri sviluppi delle tecnologie
di Carlo Pagliei
42
La GAC in salsa XML
Convertiamo la GAC in un file XML per recuperare velocemente le informazioni su assembly, versioni e namespace
di Filippo Bonanni
SOFTWARE ENGINEERING
48
Model Driven Architecture
Model Driven Architecture (MDA) è il termine con cui OMG identifica il suo tentativo di creare una architettura per
lo sviluppo di applicazioni indipendenti dalla piattaforma
di Lorenzo Vandoni
REPORTAGE
52
Il nuovo umanesimo tecnologico
di Alberto Rosotti
Codice allegato
All’indirizzo ftp.infomedia.it/pub/VBJ sono liberamente scaricabili tutti
i listati relativi agli articoli pubblicati.
La presenza di questa immagine indica l’ulteriore disponibilità, allo
stesso indirizzo, di un progetto software relativo all’articolo in cui
l’immagine è inserita. Il nome identifica la cartella sul sito ftp.
RUBRICHE
Editoriale
5
.NET Tools
61
N. 66 - Novembre/Dicembre 2005 VBJ
7
PROGRAMMAZIONE AD EVENTI
.NET: Delegate e
modello di eventi
I delegate, apparentemente strani e complicati, sono in realtà un
facile meccanismo per una corretta strutturazione OOP della programmazione ad eventi nel .NET Framework. È importante comprenderne il funzionamento per sviluppare corrette applicazioni
event-driven.
Eventi
di Maurizio Mammuccini
L
a finalità del presente articolo è quella di
trattare sinteticamente gli eventi, i delegate
ed il modello di eventi del Microsoft .NET
Framework, cercando di metterne in evidenza la
programmabilità OOP e chiarendone i relativi costrutti formali previsti dalle specifiche.
Il modello di eventi di .NET (e i delegate ad esso
strettamente legati) infatti, è stato implementato dalla Microsoft aderendo totalmente ed in modo estremamente rigoroso al paradigma OOP e ciò principalmente per conseguire due grandi obiettivi di base:
•
•
rendere più semplice l’utilizzo degli eventi nella fase di progettazione e sviluppo;
renderne efficiente e sicura l’esecuzione del relativo codice (managed code e safe code) a runtime.
Vorrei in questo articolo non soffermarmi sulla
sola visualizzazione di uno o due esempi di come
sia possibile far qualcosa con gli argomenti trattati nelle classi .NET prodotte, bensì fornire addizio-
Maurizio Mammuccini ha esperienza decennale nella progettazione e sviluppo di software. Dedica particolare attenzione
alle tecnologie di calcolo distribuito. Utilizza sin dalle primissime
versioni il Visual Studio di Microsoft.Attualmente è formatore
e consulente per aziende del settore informatico. È MCSD for
VS6 - MCDBA for Sql Server 2000 - MCAD for .NET - MCSD
for .NET ed MCT. Vive e risiede in Umbria.
8
VBJ
N. 66 - Novembre/Dicembre 2005
nalmente una visione organica
e generale degli argomenti in
modo che poi ogni lettore possa proseguire da solo, utilizzandoli in modo corretto nelle proprie applicazioni.
Perché gli eventi?
Penso che in ogni esposizione
che tratti di eventi nelle classi, sia
d’obbligo chiarire cosa un’evento
rappresenti per una classe e perché se ne renda necessaria l’introduzione nella programmazione.
Una classe non dovrebbe essere solo un pezzo di codice da
utilizzare quando occorre, bensì qualcosa di più organico; una
classe dovrebbe poter comunicare con l’esterno emettendo notifiche recepibili e comprensibili
da un qualche suo utilizzatore.
Chiaramente resta da determinare cosa, di preciso, la classe
dovrebbe notificare all’esterno. A
mio avviso il contenuto della notifica, il suo messaggio, dovrebbe
essere legato a quello che i pro-
PROGRAMMAZIONE AD EVENTI
gettisti object oriented definiscono come lo stato di un oggetto (scrivo usando il condizionale
perché poi nella pratica non sempre è così).
Tramite un evento la classe dovrebbe notificare all’esterno l’avvenuta modifica del suo
stato.
Possiamo riguardare lo stato di un oggetto
come l’insieme dei valori in un certo istante – nel lifetime dell’oggetto – di tutti i campi
membro della classe che definisce il tipo dell’oggetto.
È chiaro che la sola modifica di uno di questi valori a runtime, porta inevitabilmente al
cambiamento dello stato dell’oggetto che questi deve (non è comunque un obbligo) essere
notificato all’esterno tramite un evento.
Spesso questa percezione della realtà di un
evento ci sfugge perché siamo abituati a pensare agli eventi come legati esclusivamente al
mondo visuale di un’applicazione, ad esempio
la notifica del clic su un bottone in un form
od in una pagina web; gli eventi in una classe
invece devono avere una giustificazione ben
più ampia e concettualmente profonda.
Stato di un oggetto
Cerchiamo di approfondire il concetto di
stato di un oggetto. Lo facciamo in un caso
molto semplice, nel quale la classe che definisce il tipo dell’oggetto incapsuli un unico
campo booleano.
C#
// campo booleano su cui valutare la variazione
di stato
bool valueState;
VB.NET
‘campo booleano su cui valutare la variazione
di stato
Dim valueState As Boolean
L’unico campo che va a determinare lo stato
dell’oggetto è la variabile di tipo booleano valueState e l’evento dovrebbe venir sollevato ogniqualvolta il valore della variabile passi da Falso
a Vero e viceversa (cambiamento di stato).
Si potrebbe sviluppare un discorso di più
ampio respiro prevedendo una classe che incapsuli un numero di campi maggiore di uno,
ad esempio
C#
// campi di stato
int valueStateInt;
string valueStateString
VB.NET
‘campi di stato
Dim valueStateInt
As Integer
Dim valueStateString As String
Dal punto di vista della variazione di stato
dell’oggetto non è importante quale valore i
campi assumano, ma piuttosto che i valori dei
campi (od anche solo uno di questi) varino.
Questo è motivo sufficiente per sollevare una
notifica al client dell’oggetto, appunto sollevare un evento.
Si potrebbe obiettare che molti eventi sollevati da oggetti non sempre sorgono corrispondentemente ad una variazione di stato; smontare comunque questa obiezione è semplice.
Chiediamoci primariamente a cosa servano i
metodi pubblici di un oggetto, ossia quei metodi che da client dell’oggetto noi vediamo ed
invochiamo. Questi non sono che mezzi per
manipolare correttamente i dati dall’esterno
(valori dei campi) incapsulati nell’oggetto all’istante di tempo in cui li utilizziamo; in altre parole, mezzi per intervenire correttamente sui valori nei campi incapsulati, ossia sullo
stato dell’oggetto facendo compiere all’oggetto qualcosa di utile sui dati.
In questo ragionamento rientrano anche le
proprietà esposte dall’oggetto.
La classe che definisce il tipo di un oggetto
non dovrebbe fornire funzionalità pubbliche
a sé stanti, ossia logicamente scisse dai dati
incapsulati al suo interno. Se ciò avvenisse
dovrebbe essere in casi del tutto particolari
e sporadici. L’intento è sempre quello di progettare tenendo a mente il principio di incapsulamento del codice, basilare nella programmazione orientata agli oggetti (OOP).
N. 66 - Novembre/Dicembre 2005
VBJ
9
PROGRAMMAZIONE AD EVENTI
La variazione dello stato di un oggetto deve
sempre essere accompagnata da una notifica
al client dell’oggetto dell’avvenuta modifica
dei valori nei campi al suo interno.
Delegate
Vediamo dunque come .NET ci permetta di
costruire queste notifiche di un oggetto e di
spedirle al o ai client interessati, addizionandole di utili informazioni.
Il fondamento di tutto il modello di eventi di Microsoft .NET, risiede nell’importante
concetto di delegate.
I delegate sono un’eccezionale feature del
.NET Framework, e rassomigliano vagamente ai puntatori a funzione del C++. Ho scritto
“vagamente” poiché i delegate sono migliori dei classici puntatori a funzione del C++,
essendo type-safe e protetti.
Scrivendo type-safe intendiamo che nel C++
(non gestito) l’indirizzo di una funzione è soltanto il suo indirizzo in memoria; questo, diversamente dai delegate, è privo di tutto un insieme
di informazioni addizionali, quali il numero ed
il tipo dei parametri che la funzione prevede, il
tipo del valore restituito dalla funzione, la convenzione di chiamata della funzione.
Dichiarare nel codice un tipo Delegate significa soprattutto predisporre un tipo derivato dalla fondamentale classe del .NET Framework System.Delegate e contenente il meccanismo di chiamata a quella che è detta essere un’ entità chiamabile.
Cos’è un’entità chiamabile?
Nel caso di metodi d’istanza possiamo definire un’entità chiamabile come la coppia data
dall’istanza di una classe e da un metodo di
questa; nel caso invece di un metodo statico
(static) l’entità chiamabile è data dal nome
completo (ossia comprensivo del nome della
classe d’appartenenza) del metodo.
Quando viene chiamata un’istanza di delegate contenente la chiamata ad una funzione
che dichiara parametri, sarà poi possibile passare al delegate gli argomenti attesi in modo
che la funzione venga richiamata con gli insiemi di argomenti definiti.
10
VBJ
N. 66 - Novembre/Dicembre 2005
Dunque ognivolta che nel codice istanziamo
un delegate, non facciamo altro che incapsulare in modo sicuro una invocazione ad una
opportuna funzione, ossia ad una funzione di
sintassi compatibile a quella dichiarata nell’istruzione delegate.
C#
//istruzione delegate
public delegate int GetIntFromTwoInt(int i, int j);
VB.NET
‘istruzione delegate
Public Delegate Function GetIntFromTwoInt(ByVal i
As Integer, ByVal j As Integer) As Integer
Le dichiarazioni di delegate precedenti incapsulano chiamate a funzioni, invocazioni
ad entità chiamabili, di sintassi compatibile,
ossia che accettano due interi e restituiscono
un intero. Ogni altra tipologia di invocazione
non è permessa.
Da ciò comunque emerge che uno stesso delegate può essere utilizzato per invocare differenti funzioni purchè di identica sintassi.
Il delegate GetIntFromTwoInt sopra potrebbe essere indistintamente utilizzato per invocare sia una funzione Addizione che Moltiplicazione di interi, metodi pubblici di una classe OperazioniAritmetiche:
C#
//Addizione
public int Addizione(int i, int j);
//Moltiplicazione
public int Moltiplicazione(int i, int j);
VB.NET
‘Addizione
Public Function Addizione(ByVal i As Integer,
ByVal j As Integer) As Integer
‘Moltiplicazione
Public Function Moltiplicazione(ByVal i As
Integer, ByVal j As Integer) As Integer
Il delegato GetIntFromTwoInt sopra non potrebbe però venir utilizzato per invocare una
PROGRAMMAZIONE AD EVENTI
funzione Opposto di un intero nella classe
OperazioniAritmetiche:
C#
//Opposto
public int Opposto(int i);
VB.NET
‘Opposto
vengono passati gli argomenti 3 e 4. Questa
è un’impressione errata. Il compilatore, presa visione che dlg è un delegate, produce il
codice necessario all’invocazione del metodo
Invoke di dlg.
Dunque il compilatore, quando compila
l’istruzione di chiamata, la tratta come se contenesse esplicitamente l’invocazione al metodo Invoke, ossia come se fosse scritta così:
Public Function Opposto(ByVal i As Integer)
As Integer
C#
//Invocazione
Per invocare il metodo tramite delegate dev’essere prodotta prima l’istanza del delegate
con indicazione di quale funzione si intenda
incapsulare l’invocazione.
int result = dlg.Invoke(3,4);
VB.NET
‘Invocazione
Dim result As Integer = dlg.Invoke(3,4)
C#
Operazion//Istanziazione della classe del metodo
iAritmetiche oa = new OperazioniAritmetiche();
//Incapsulamento nel delegate dell’invocazione
al metodo
GetIntFromTwoInt dlg = new GetIntFromTwoInt
(oa.Addizione);
In C# non è però possibile (a differenza di
MC++ ) modificare il codice sorgente in modo
da invocare esplicitamente la chiamata al metodo Invoke trasmettendo gli argomenti per il
metodo incapsulato nel delegate; in tal caso
otterremmo il seguente errore:
VB.NET
‘Istanziazione della classe del metodo
[Compiler Error CS1533]
Dim oa As OperazioniAritmetiche =
Il metodo Invoke non può essere chiamato
New OperazioniAritmetiche()
direttamente su un delegato.
‘Incapsulamento nel delegate dell’invocazione
al metodo
Dim dlg As GetIntFromTwoInt =
New GetIntFromTwoInt(oa.Addizione)
L’esecuzione della chiamata alla funzione incapsulata avviene semplicemente chiamando
il delegate e passandogli gli argomenti
C#
//Invocazione
int result = dlg(3,4);
VB.NET
‘Invocazione
Dim result As Integer = dlg(3,4)
Se osserviamo la sintassi di invocazione al
metodo, sembra che dlg sia una funzione a cui
Utilizzando l’utility Ildasm.exe fornita dalla
Microsoft col .NET Framework ed analizzando il codice compilato è possibile entrare un
po’ negli internals del meccanismo di chiamata al metodo e quindi poter comprendere
meglio cosa succeda dietro le quinte.
Se disassembliamo con Ildasm il compilato dell’eseguibile dove i delegate sono stati
compilati, troviamo che GetIntFromTwoInt
e GetIntFromOneInt sono stati definiti dal
compilatore come classi denominate rispettivamente GetIntFromTwoInt e GetIntFromOneInt e derivate dalla fondamentale classe System.MulticastDelegate del .NET Framework.
Ci può aiutare in questo discorso dare uno
sguardo alla Figura 1 che mostra la struttura
della classe scritta nell’assembly.
N. 66 - Novembre/Dicembre 2005
VBJ
11
PROGRAMMAZIONE AD EVENTI
Figura 1
Delegati nel compilato IL
Per il compilatore tutti i delegate (allo stato
attuale del .NET Framework) derivano dalla
classe System.MulticastDelegate ed in definitiva dalla classe System.Delegate.
Soltanto i compilatori sono istruiti su come
derivare dalla Delegate o dalla MulticastDelegate, mentre per i programmatori questo
non è consentito. Dunque da codice non ci è
possible derivare queste due classi.
Come si vede dalla Figura 1 tutti i delegate
presentano un costruttore con due parametri,
un riferimento ad un oggetto ed un tipo intero
che fa riferimento al metodo incapsulato.
Questo costruttore riflette un po’ tutta la gerarchia che a partire dalla classe Delegate si
sviluppa sino alla classe di delegato passando per la MulticastDelegate.
Risalendo a ritroso la gerarchia, i valori passati ad argomento al costruttore del nostro delegate servono ad impostare due campi privati
della classe Delegate; questi campi sono _target
di tipo System.Object (un riferimento all’oggetto su cui si deve eseguire l’invocazione al
metodo incapsulato) e _methodPtr di tipo System.Int32 (un intero con cui il CLR identifica
univocamente il metodo da chiamare).
Tramite le due proprietà Target e Method
(ereditate dalla System.Delegate) la classe
MulticastDelegate mette a disposizione dei
programmatori la possibilità di leggere i valori dei campi _target e _methodPtr.
Sempre dalla Figura 1 si nota come il compi-
12
VBJ
N. 66 - Novembre/Dicembre 2005
latore abbia inoltre prodotto i tre metodi BeginInvoke, Invoke, EndInvoke nelle classi di delegate.
La chiamata al metodo Invoke utilizza i campi privati _target e _methodPtr per
richiamare il metodo incapsulato nel delegate sul particolare oggetto indicato. Pertanto la sintassi del metodo
Invoke deve coincidere esattamente con quella indicata
per il delegate.
Gli altri due metodi BeginInvoke ed EndInvoke hanno invece un’utilizzo nella gestione callback
del metodo incapsulato.
Spesso ed erroneamente molti testi od articoli connotano subito il metodo incapsulato nel
delegato come metodo callback (metodo di richiamata); ciò è vero se il metodo è utilizzato
in tal senso tramite l’utilizzo di BeginInvoke
ed EndInvoke. Ogni altro utilizzo del delegate produce soltanto un’invocazione (anonima)
delegata a metodo di un altro oggetto.
L’utilizzo callback del metodo incapsulato
nel delegate vede l’invocazione a BeginInvoke che avvia la chiamata asincrona e ritorna
immediatamente al chiamante senza attendere l’ultimazione della chiamata.
L’esecuzione di EndInvoke viene eseguita per recuperare i risultati della chiamata
asincrona e può avvenire in ogni momento
dopo l’invocazione a BeginInvoke.
Nel presente articolo non mi soffermo su questi aspetti di callback, a tale argomento sarebbe
necessario dedicare un articolo ulteriore.
Qui voglio solo evidenziare che tramite delega
si può utilizzare callback, un metodo di classe,
il cui tempo d’esecuzione potrebbe essere anche lungo e bloccare il thread che lo invoca.
Chiamate con associazione tardiva
Può inoltre essere utile, con alcune importanti osservazioni, far vedere come chiamare
con associazione tardiva (late-bound) la fun-
PROGRAMMAZIONE AD EVENTI
zione incapsulata nel delegate, passandogli gli
argomenti come un array di tipo object
ulteriori approfondimenti il lettore può riferirsi all’ottimo manuale di Jeffrey Richter [1]
indicato in bibliografia.
C#
.NET Events model
//Invocazione con associazione tardiva
object[] args = new object[2]{3,4};
int result = dlg.DynamicInvoke(args);
VB.NET
‘Invocazione con associazione tardiva
Dim args As Object() = New Object(1) {3, 4}
Dim result As Integer = dlg.DynamicInvoke(args)
Val la pena di osservare che durante il binding al metodo, nelle invocazioni con late binding, si può subire un certo overhead dovuto
ai controlli sul metodo (es. numero dei parametri, tipo dei parametri, corretta assegnazione degli argomenti, …).
Questo overhead si riduce se non si utilizza il late binding al metodo, ossia se invece
si sfrutta l’early binding o binding anticipato al metodo incapsulato nel delegate. Approfondiamo questo aspetto.
Il metodo Invoke prodotto dal compilatore,
come già osservato in precedenza, dentro la
classe compilata del delegate ha sintassi esattamente coincidente con quella del metodo
target incapsulato nel delegate.
A questo punto è noto anticipatamente (early binding) a tempo di compilazione se, passando direttamente tra parentesi gli argomenti attesi – subito dopo il nome del reference
all’oggetto delegate – sia rispettata o meno la
sintassi del metodo target.
Utilizzando invece, nell’invocare il metodo
target, il metodo DynamicInvoke a tempo di
compilazione, non si ha nessun controllo sulla sintassi d’assegnazione degli argomenti.
Infatti come si vede dal codice sopra questi
vengono passati come semplice array di tipo
object e l’assegnazione viene risolta a tempo
d’esecuzione, ossia con late binding.
L’argomento dei delegate, straordinariamente bello ed interessante, è lungi dall’essere
ultimato. Qui non vado oltre e mi limito ora
ad esporne sinteticamente l’applicazione nel
modello ad eventi del .NET Framework. Per
Il modello di eventi del .NET Framework si
basa su un’idea molto semplice che chiama
in causa i delegate. Ossia, il mittente (o sorgente) dell’evento non è a conoscenza di quale oggetto (ricevente o destinatario) riceverà
e possibilmente gestirà la notifica prodotta.
L’events model .NET prevede in tal senso di
dover fornire un intermediario fra l’oggetto
mittente e l’oggetto ricevente.
Il ruolo d’intermediario viene recitato da un
apposito delegate detto delegato d’evento .NET
che per convenzione presenta una sua sintassi standard codificata nel Framework con due
parametri, l’origine che ha generato l’evento
e i dati trasmessi con la notifica dell’evento.
Torneremo fra breve sull’importante punto
della sintassi del delegato d’evento.
Quindi il delegato d’evento si frappone come
oggetto fra i due terminali di comunicazione
e non gestisce direttamente la notifica, quindi non deve essere riguardato come ricevente. Il delegato si ricorda per la precisa notifica d’evento che riceve, quale oggetto e quale
metodo di quest’ultimo gestirà la comunicazione ricevuta.
Da codice è semplice stabilire il legame tra
il mittente ed il delegato d’evento. Vediamo
come.
Il delegato d’evento viene dichiarato nella
classe del mittente:
C#
//Dichiarazione del delegato d’evento
public delegate EventNameEventHandler
(object s,EventArgs e)
//Dichiarazione dell’evento
public event EventNameEventHandler EventName;
VB.NET
‘Dichiarazione del delegato d’evento
Public Delegate Sub EventNameEventHandler(ByVal s
As Object, ByVal e As EventArgs)
N. 66 - Novembre/Dicembre 2005
VBJ
13
PROGRAMMAZIONE AD EVENTI
Figura 2
IL della classe sorgente d’evento
‘Dichiarazione dell’evento
Public Event EventName As EventNameEventHandler
Se, ad esempio, l’evento nella classe NotifyingClass ha nome StateChange con delegate
StateChange-EventHandler, si avrà il seguente codice di dichiarazione dell’evento:
C#
//Dichiarazione del delegato d’evento
public delegate StateChangeEventHandler
(object s,EventArgs e)
//Dichiarazione dell’evento
public event StateChangeEventHandler StateChange;
VB.NET
‘Dichiarazione del delegato d’evento
Public Delegate Sub StateChangeEventHandler
(ByVal s As Object, ByVal e As EventArgs)
‘Dichiarazione dell’evento
re il codice necessario ad inoltrare la chiamata del mittente
all’opportuno delegate quando
avviene la notifica verso l’esterno. Sin qui però il delegate non
conosce il particolare metodo
che poi dovrà chiamare (handler) quando riceve la notifica. Questa informazione viene
passata, ed era naturale aspettarselo, nel destinatario finale
della notifica d’evento, il quale si registra per ricevere la
notifica.
È interessante approfondire come viene impostata tale informazione.
Ricorriamo ancora una volta al “fedele” Ildasm.exe.
Dalla Figura 2 osserviamo che disassemblando l’assembly della classe sorgente d’evento
notiamo che l’evento StateChange, dal compilatore, è stato scisso in due funzioni pubbliche nascoste add_StateChange e remove_
StateChange.
Lo scopo preciso di queste funzioni, che
non possiamo invocare esplicitamente e direttamente, è quello di favorire al ricevente
un modo di gestire il carico o lo scarico dell’eventuale handler d’evento.
Ho scritto che noi non possiamo invocare
esplicitamente e direttamente queste due
funzioni. In realtà lo facciamo in una forma
particolare che ora evidenzio. Nella classe del
ricevente scriviamo:
Public Event StateChange As StateChangeEventHandler
C#
Questo evento solleva una notifica di cambiamento di stato ogniqualvolta l’unico campo booleano valueState subisce la trasformazione da true a false (True a False) e viceversa.
Dal codice scritto si legge esplicitamente il
collegamento tra la sorgente d’evento ed il delegato che riceve notifica, infatti l’evento è dichiarato proprio come il delegato d’evento, a
significare che quel delegate sarà informato
che è successo qualcosa nel mittente.
Questo è sufficiente al compilatore per scrive-
14
VBJ
N. 66 - Novembre/Dicembre 2005
//Dichiarazione del reference al mittente
MittenteTypeClass reference;
//Valorizzazione del reference al mittente
reference=new MittenteTypeClass([<args>]);
//Dichiarazione del gestore l’evento
reference.EventName+=new MittenteTypeClass.
EventNameEventHandler(
reference_EventName
);
VB.NET
‘ Dichiarazione del reference al mittente
PROGRAMMAZIONE AD EVENTI
Dim reference As MittenteTypeClass
‘ Valorizzazione del reference al mittente
Reference=New MittenteTypeClass([<args>])
‘ Dichiarazione del gestore l’evento
AddHandler reference.EventName, AddressOf
mente chiamato argomenti d’evento è incapsulato dentro una classe derivata dalla classe
System.EventArgs.
La dichiarazione dunque di una classe d’argomenti d’evento da passare con la notifica è:
reference_EventName
C#
Dunque, l’utilizzo della funzione add_EventName passa in C# per l’operatore +=, mentre
in Visual Basic.NET passa per l’utilizzo della
funzione AddHandler.
In modo similare, l’utilizzo della funzione remove_EventName passa in C# per l’operatore -=, mentre in Visual Basic.NET passa per
l’utilizzo della funzione RemoveHandler.
L’implementazione dell’evento, nell’assembly, in due funzioni add_XXX e remove_XXX
offre la possibilità al ricevente di gestire una
propria lista interna, di tipo EventHandlerList,
di handler per l’evento andando ad aggiungere
od a rimuovere item dalla lista stessa a seconda dell’esigenza o meno di gestire particolari
eventi, o di cambiare a runtime un particolare
gestore d’evento a favore di un altro.
Argomenti d’evento
Concludo l’articolo indicando un’altra importante feature OOP degli eventi, spesso
purtroppo sottovalutata dai programmatori:
la possibilità di fornire e gestire classi di informazioni con l’evento.
Se ben ricordiamo, precedentemente ho
scritto che la sintassi del delegato d’evento è
pressochè standard. La funzione nel ricevente che viene richiamata in gestione (handler)
dell’evento notificato ha una sintassi canonica
dove, oltre alla sorgente dell’evento, è presente un parametro all’interno del quale si riceve
con la notifica anche un potenziale insieme di
valori d’informazione, che possono risultare
utili per la gestione dell’evento stesso.
Va detto comunque che non è obbligatorio
associare alla notifica un insieme di valori,
ma in alcuni casi utilizzare informazioni addizionali potrebbe essere di estremo aiuto alla
routine che gestisce l’evento.
Questo insieme di valori che viene comune-
//Classe d’argomenti d’evento
public class EventNameEventArgs : System.
EventArgs{…}
VB.NET
‘ Classe d’argomenti d’evento
Public Class EventNameEventArgs
Inherits System.EventArgs
‘…
End Class
Nell’esempio utilizzato nell’articolo possiamo definire una classe d’argomenti d’evento
StateChangeEventArgs che fornisce addizionalmente nella gestione d’evento il vecchio
valore di stato ed il nuovo (Listato 1).
Quando nel mittente viene sollevato l’evento,
la classe StateChangeEventArgs viene istanziata e caricata delle informazioni da trasmettere al ricevente con la notifica:
C#
//Valorizzazione degli argomenti
StateChangeEventArgs evArgs = new StateChangeEvent
Args(oldValoreBooleano, newValoreBooleano);
//Passaggio degli argomenti con la notifica d’evento
this.StateChange(this,evArgs);
VB.NET
‘ Valorizzazione degli argomenti
Dim evArgs As StateChangeEventArgs = New StateChange
EventArgs(oldValoreBooleano, newValoreBooleano)
‘Passaggio degli argomenti con la notifica d’evento
Me.StateChange(Me,evArgs)
Un’osservazione tratta
dal .NET Framework
La trasmissione di informazioni con la notifica d’evento ha applicazioni già dentro
N. 66 - Novembre/Dicembre 2005
VBJ
15
PROGRAMMAZIONE AD EVENTI
Listato 1
La classe StateChangeEventArgs che fornisce il vecchio
e il nuovo valore di stato
C#
//Classe d’argomenti d’evento associata all’evento StateChange
public class StateChangeEventArgs : EventArgs {
/*
* Vecchio e nuovo valore di stato
*/
bool oldStateValue;
bool newStateValue;
public StateChangeEventArgs(bool oldState,bool newState)
{
//
// TODO: aggiungere qui la logica del costruttore
//
this.oldStateValue=oldState;
this.newStateValue=newState;
}
/// <summary>
/// Valore originale di stato della classe notificante
/// </summary>
public bool Original
{
get
{
return this.oldStateValue;
}
}
/// <summary>
/// Valore corrente di stato della classe notificante
/// </summary>
public bool Current
{
get
{
return this.newStateValue;
}
}
}
VB.NET
‘ Classe d’argomenti d’evento associata all’evento StateChange
Public Class StateChangedEventArgs
Inherits EventArgs
‘ Vecchio e nuovo valore di stato
Dim oldStateValue As Boolean
Dim newStateValue As Boolean
Public Sub New(ByVal oldState As Boolean, ByVal newState As Boolean)
Me.oldStateValue = oldState
Me.newStateValue = newState
End Sub
‘ <summary>
‘ Valore originale di stato della classe notificante
‘ </summary>
Public ReadOnly Property Original() As Boolean
Get
Return Me.oldStateValue
End Get
End Property
‘ <summary>
‘ Valore corrente di stato della classe notificante
‘ </summary>
Public ReadOnly Property Current() As Boolean
Get
Return Me.newStateValue
End Get
End Property
End Class
16
VBJ
N. 66 - Novembre/Dicembre 2005
il .NET Framework stesso.
Si può, fra le altre, evidenziare
la classe di connessione ad un
database XxxConnection dell’infrastruttura ADO.NET.
Questa espone pubblicamente sempre l’evento StateChange che viene notificato
ai client ogniqualvolta è modificato lo stato della connessione fra i diversi valori forniti nell’enumerazione ConnectionState.
Con la notifica dell’evento
di cambiamento di stato della connessione viene passata
un’istanza di classe d’argomenti d’evento di nome StateChangeEventArgs. Il vecchio ed il
nuovo valore di connessione
possono essere recuperati (ed
utilizzati) dal gestore della notifica rispettivamente tramite le
due proprietà pubbliche OriginalState e CurrentState.
Tenendo presente quanto è
stato scritto all’inizio dell’articolo, voglio qui ribadire l’estrema
importanza di pensare gli eventi come notifiche di cambiamento di stato nella sorgente d’evento, e quindi di informare sul valore di vecchio e nuovo stato. A
tale scopo le classi d’argomento d’evento giocano un ruolo da
attori principali e non da semplici comparse, proprio perché
naturalmente parte integrante
della notifica prodotta. Ciò indipendentemente dal fatto che il
ricevente gestisca o meno queste informazioni quando riceve
la notifica.
Conclusioni
In questo articolo ho cercato
di dare una sistematica sep-
PROGRAMMAZIONE AD EVENTI
pur breve e sintetica esposizione dei delegate e del modello di eventi di Microsoft.NET,
fornendo osservazioni d’implementazione che
ritengo utili per tutti i programmatori .NET
Il fondamento di tutto il
modello di eventi di
Microsoft .NET, risiede
nell’importante concetto
di delegate
Non ho inteso evidenziare automatismi reconditi della piattaforma.NET, bensì stimolare i progettisti e programmatori ad utilizzare gli eventi che spesso mancano nelle
classi che progettiamo e realizziamo. Una
classe deve poter comunicare all’esterno il
proprio cambiamento di stato e questa avvenuta notifica deve poter essere gestita da
chi la riceve.
Classi “mute” seppur utili ed interessanti diventano di difficile gestione perché non si ha
modo di sincronizzarsi con loro, in quanto non
richiamano e non notificano nulla all’utilizzatore. In un prossimo articolo approfondiremo
il confronto tra il modello ad eventi di.NET e
di COM, con la speranza che ciò riesca utile
a tutti quei progettisti e programmatori che
in tempi recenti sono migrati dalla piattaforma COM a .NET.
Bibliografia
[1] Jeffrey Richter - “Microsoft .NET Programmazione avanzata”, Mondadori Informatica, 2002
Riferimenti
[2] http://msdn.microsoft.com/library/default.
asp?url=/library/en-us/csref/html/vcrefthedelegatetype.asp
[3] http://msdn.microsoft.com/library/default.
asp?url=/library/en-us/csref/html/vcwlkeventstutorial.asp
TECNICHE
Application e
User Setting
in Visual Basic 2003
di Francesco Balena
U
na delle novità principali di Visual Basic
2005 e C# 2.0 è il supporto per la persistenza delle impostazioni del programma,
a livello di applicazione e a livello utente. Potete
trovare maggiori informazioni su questa importante feature in [1], ma cercherò di riassumere il
concetto in poche righe.
Visual Studio 2005 fornisce un designer (vedi Figura 1) che permette di definire le impostazioni
usate dal programma. Man mano che si definiscono nuove impostazioni, Visual Studio genera il codice della classe Settings (nel namespace My), che
espone tali impostazioni sotto forma di proprietà,
in modo che si possa poi accedervi da codice con
una syntassi strong-typed, ad esempio:
Dim filename As String = My.Settings.LastLoadedFile
In un certo senso, la classe My.Settings assolve
al compito che in passato era riservato alle variabili globali (tipicamente conservate in un modulo), con in più il vantaggio di poterle caricare
e salvare su file.
Le impostazioni possono essere definite a livello di applicazione (ad es. la stringa di connessione al database) oppure a livello di singolo utente (ad es. i colori preferiti o la lista degli ultimi
file caricati). La differenza principale tra i due
tipi di impostazioni è che le impostazioni a livello di applicazioni sono a sola lettura, mentre
quelle a livello utente sono assegnabili.
La classe My.Settings espone anche un metodo Save che salva il valore corrente del-
18
VBJ
N. 66 - Novembre/Dicembre 2005
le impostazioni utente. Le
impostazioni sono caricate al
lancio del programma e possono essere anche salvate automaticamente al suo termine,
semplicemente abilitando una
opzione di progetto.
Le impostazioni a livello utente sono conservate in un file
nel folder C:\Documents and
Settings\Username\Local Settings\Application Data (sono
creati tanti file distinti, uno
per ciascun utente) mentre le
impostazioni a livello di applicazione sono conservate nel
file di configurazione della applicazione.
I limiti di My.Settings
Il meccanismo di Visual Studio 2005 è decisamente potente
ed elegante, ma non risolve affatto tutti i problemi che si possono verificare lavorando con le
impostazioni di un programma,
ad esempio:
a) non è possibile memorizzare
i valori su altri medium, ad
esempio in un campo di database.
USER
TECNICHE
Figura 1
Il designer di Visual Studio 2005 permette di definire le impostazioni usate dal programma
b) non è possibile specificare se l’utente è locale oppure si tratta di un roaming user (in
modo che le impostazioni utente siano ritrovate anche se l’utente esegue il login
da un’altra macchina sulla LAN).
c) a volte diventa importante modificare via
codice anche le impostazioni a livello di
applicazione, ma questo è assolutamente
impossibile con la classe My.Settings, a
meno di non scrivere un parser del testo
XML del file .config.
d) non esiste una proprietà che restituisce il
nome del file contenente le impostazioni
a livello utente, quindi ad es. non è semplice eseguire il backup delle impostazioni
utente, oppure implementare un meccanismo di recovery dei valori com’era nelle precedenti sessioni o ancora migrare le
impostazioni su altre macchine.
Più in generale, dopo un po’ di frequentazione con la classe My.Settings ci si rende
conto che spesso risulta insufficiente per i
propri scopi. Ad esempio, spesso mi capita
di lavorare con applicazioni data-centriche
e vorrei creare dei file di “solution” (concettualmente simili ai file .sln di Visual Studio)
che conservano informazioni sul gruppo di
documenti aperti in un certo momento, e altre informazioni al contorno. Visto che però
non è possibile caricare il contenuto delle impostazioni utente da un file specifico,
per questo tipo di informazioni occorre un
meccanismo differente da quello offerto da
My.Settings.
Insomma, alla fine in molti programmi occorre comunque implementare un meccanismo di persistenza delle variabili globali che è
più raffinato di quello offerto da My.Settings,
sia perchè serve avere maggiore controllo su
dove le informazioni sono memorizzate (file,
database, registry, ecc.) sia perchè è necessario poter lavorare con più istanze della classe che espone tali variabili (ad esempio per
poter fare il merge di opzioni di provenienza differente).
Per questi motivi, ed altri ancora, alla fine
ho deciso di scrivere una serie di classi che
N. 66 - Novembre/Dicembre 2005
VBJ
19
TECNICHE
Listato 2
Le altre due classi UserSettingsBase e ApplicationSettingsBase
‘ ---------------------------------------‘ Base class for user settings
‘ ---------------------------------------Public MustInherit Class UserSettingsBase
Inherits SettingsBase
Sub New()
Dim dirName As String = Environment.GetFolderPath( _
Environment.SpecialFolder.LocalApplicationData)
Dim appName As String = [Assembly].GetExecutingAssembly().GetName().Name
SetFilename(Path.Combine(dirName, Path.ChangeExtension(appName, DefaultExtension)))
End Sub
End Class
‘ ---------------------------------------‘ Base class for Application settings
‘ ---------------------------------------Public MustInherit Class ApplicationSettingsBase
Inherits SettingsBase
Sub New()
Dim dirName As String = _
AppDomain.CurrentDomain.SetupInformation.ApplicationBase
Dim appName As String = [Assembly].GetExecutingAssembly().GetName().Name
SetFilename(Path.Combine(dirName, Path.ChangeExtension(appName, DefaultExtension)))
End Sub
End Class
potessero risolvere una volta per tutte il problema della definizione e della persistenza
delle impostazioni globali del programma, sia
a livello utente che di applicazione. Il codice
è scritto in VB 2003, quindi può essere usato con la precedente versione di Visual Basic, ma il risultato è così flessibile e potente
che sto cominciando ad usarlo anche nei miei
progetti VB 2005.
In definitiva, quindi, lo scopo di questo articolo è mostrare un meccanismo generalizzato
per gestire, salvare, e ricaricare un gruppo di
valori di uno dei seguenti tipi:
•
•
•
20
Impostazioni a livello di applicazione, condivise tra tutti gli utenti della applicazione
Impostazioni a livello di utente, specifiche
per un particolare utente
Impostazioni a livello di progetto (o di soluzione), che possono essere salvate e ricaricate esplicitamente dall'operatore e serVBJ
N. 66 - Novembre/Dicembre 2005
vono per individuare le proprietà dei documenti con cui l’utente sta lavorando in
quel momento.
Grazie alle classi base che introduco in questo articolo, il compito dello sviluppatore che
vuole implementare un gruppo di valori si
riduce a definire una classe che eredita da
una classe base e aggiungere alla nuova classe uno o più campi pubblici (uno per ciascuna impostazione). Le classi implementate in
questo modo esporranno quindi i vari metodi per salvare e ricaricare da file o da stream
il gruppo di impostazioni e verificare se i valori sono stati modificati. Opzionalmente sarà
anche possibile caricare automaticamente le
impostazioni a livello di applicazione o utente
quando parte l’applicazione e salvarli quando l’applicazione viene chiusa. Notare che il
meccanismo implementato in questo articolo funziona solo con le applicazioni Windows
Form, e non ASP.NET.
TECNICHE
se, quindi occorre
copiare i valori dei
campi e delle proPublic Class MySettings
prietà dalla nuova
Inherits UserSettingsBase
istanza all’oggetto
‘ --------------------------------------------------“corrente” (ossia
‘ Instance fields and properties
quello rappresen‘ --------------------------------------------------tato dalla keyword
Public Name As String = “Francesco”
Me). Ma la classe
Public ID As Integer
base non conosce
Public RecentDocs() As String
Public FormLocation As Point = New Point(0, 0)
quali sono i campi
Public FormSize As Size = New Size(100, 200)
che verranno defi‘ These fields aren’t persisted
niti nelle classi de<XmlIgnore()> Public FormTitle As String
rivate. Per risolve<XmlIgnore()> Public TimeStarted As Date
re questo problema
‘ --------------------------------------------------il codice usa reflec‘ Static members
tion per iterare su
‘ --------------------------------------------------tutti i campi e le
‘ The Global instance (optional, but makes usage easier)
proprietà pubbliPublic Shared Global As New MySettings
che di istanza, evi‘ This static constructor is needed only to automatically load the Global
tando però di co‘ instance the first time it is referenced by the application. If the
piare gli elemen‘ application doesn’t use the Global instance or it loads
‘ it explicitly, you can omit this static constructor.
ti marcati con l’atShared Sub New()
tributi XmlIgnore,
If File.Exists(Global.FileName) Then Global.Load()
End Sub
perchè questi eleEnd Class
menti non devono
essere salvati o ricaricati da file.
La classe SettingsBase
Reflection viene usata anche nel metodo HasChanges per confrontare l’istanza corrente
La classe astratta SettingsBase funge da
(Me) con l’oggetto salvato nella variabile priclasse base per la classe UserSettingsBavata OldSettings, in modo da poter determise (base per le impostazioni utente), la clasnare se anche solo una delle proprietà è stase ApplicationSettingsBase (base per le
ta modificata ed è necessario quindi salvare
impostazioni di applicazione), e le altre clasl’oggetto su file.
si che volete usare per conservare un grupIl terzo aspetto interessante della classe Setpo di variabili che devono essere facilmente
tingsBase è la proprietà AutoSave, che se imlette e scritte da file.
postata a True permette di salvare automaIl codice sorgente della classe astratta Setticamente su file il contenuto della istanza
tingsBase è lungo (Listato 1), ma il suo funquando l’applicazione termina (a condiziozionamento è abbastanza lineare.
ne ovviamente che l’istanza sia stata caricaIl meccanismo di persistenza in XML delta in precedenza da file o almeno che la sua
la classe è implementato mediante l’oggetto
proprietà FileName sia stata assegnata corXmlSerializer, che viene usato nei vari overrettamente).
load dei metodi Load e Save. Con il metodo
Questa feature si basa sull’evento ProcesLoad, però, c’è un piccolo problema da risExit dell’oggetto AppDomain, che scatta apsolvere: il metodo Deserialize di XmlSerialipunto quando il processo corrente sta per terzer restituisce una nuova istanza della clasminare.
Listato 3
Una classe che definisce alcune impostazioni a livello utente
N. 66 - Novembre/Dicembre 2005
VBJ
21
TECNICHE
Le classi UserSettingsBase
e ApplicationSettingsBase
Le altre due classi base sono decisamente più semplici. In pratica, esse derivano da
SettingsBase e modificano soltanto il valore di partenza della proprietà FileName. Nel
caso di UserSettingsBase la proprietà punta
al file C:\Documents and Settings\Username\
Local Settings\Application Data\
Data\ApplicationName.xml, mentre nel caso di ApplicationSettingsBase la proprietà punta al file ApplicationName.xml nella stessa directory dell’applicazione.
Dal sorgente della classe SettingsBase si
può notare che le assegnazioni alla proprietà FileName provocano una eccezione se la
classe è del tipo UserSettingsBase o ApplicationSettingsBase, quindi il costruttore di queste classi usa il metodo Protected
Sub SetFileName per aggirare questo problema.
Creare una classe di impostazioni
utente
A differenza della classe My.Settings di VB
2005, che contiene sia impostazioni utente che
di applicazione, l’approccio che propongo in
questo articolo richiede la definizione di due
classi distinte, una per le impostazioni utente e una per le impostazioni di applicazione. Ovviamente, è possibile che uno specifico progetto richieda impostazioni di un solo
tipo, quindi non è obbligatorio definire sempre due classi.
Grazie alla potenza delle classi base preparate in precedenza, il sorgente di una classe per le impostazioni utente è davvero molto semplice. Ecco ad esempio una classe che
definisce alcune impostazioni a livello utente (Listato 3).
Per default tutti i campi e le proprietà pubbliche delle classi che derivano da UserSettingsBase sono scritte e lette da file. Accade spesso, d’altra parte, di voler definire
della variabili globali
Listato 4
Una classe che contiene tutte le impostazioni a livello di applicazione
che non devono essere persistite tra una
sessione e l’altra.
Public Class MyAppSettings
Per ottenere che
Inherits ApplicationSettingsBase
ciò non avvenga, è
‘ --------------------------------------------------sufficiente marca‘ Instance fields and properties
re il campo o la pro‘ --------------------------------------------------prietà con l’attribuPublic DatabaseName As String = “SqlServer”
to XmlIgnore (vedi
Public MainServer As String = “MYSERVER”
ad es. i campi FormTitle e TimeStar<XmlIgnore()>Public FormCount As Integer
ted nell’esempio pre‘ --------------------------------------------------cedente.)
‘ Static members
‘ --------------------------------------------------La prima parte della classe MySettings
‘ The Global instance (optional, but makes usage easier)
cambia da progetto a
Public Shared Global As New MySettings
progetto, mentre la
‘ This static constructor is needed only to automatically load the Global
seconda parte (nel‘ instance the first time it is referenced by the application. If the
la sezione “Static
‘ application doesn’t use the Global instance or it loads
‘ it explicitly, you can omit this static constructor.
members”) è identiShared Sub New()
ca per tutte le clasIf File.Exists(Global.FileName) Then Global.Load()
End Sub
si che derivano da
End Class
UserSettingsBase.
Purtroppo, le rego-
22
VBJ
N. 66 - Novembre/Dicembre 2005
TECNICHE
le della ereditarietà non permettono di spostare questi membri statici nella classe base.
Comunque, sia il campo Global che il costruttore statico sono opzionali, nel senso
che la classe può funzionare anche se questi membri non sono definiti, come spiegano i commenti.
La classe My.Settings
espone anche un
metodo Save che salva
il valore corrente delle
impostazioni utente
re un campo readonly in una classe di questo tipo).
MySettings.Global.FormSize = New Size(10, 200)
Ovviamente, i campi e le proprietà readonly
non sono salvate su file.
Esistono due modi per effettuare il salvataggio delle impostazioni utente: esplicitamente mediante il metodo Save oppure implicitamente mediante la proprietà AutoSave.
Nel secondo caso l’istanza di MySettings
è salvata automaticamente (se vi sono state delle modifiche) al termine della applicazione:
‘ Salvataggio esplicito sul file di default
MySettings.Global.Save()
‘ Attiva l’auto save
MySettings.Global.AutoSave = True
A questo punto potete definire nuove istanze di MySettings come fareste con qualsiasi
altra classe, ma nella maggior parte dei casi
sarà sufficiente usare l’istanza globale static rappresentata dal campo Global. Grazie
al costruttore statico, non è neanche necessario caricare esplicitamente l’istanza Global alla partenza della applicazione, poiché
tale caricamento avverrà automaticamente la prima volta che la classe MySettings è
referenziata:
Private Sub Form1_Load(ByVal sender As Object,
ByVal e As EventArgs) Handles MyBase.Load
Me.Size = MySettings.Global.FormSize
Me.Location = MySettings.Global.FormLocation
lblMessage.Text = “Welcome, “ &
La classe MySettings eredita da UserSettings due ulteriori metodi. Il metodo Refresh ripristina il contenuto delle variabili com’era
al momento dal caricamento o salvataggio
più recente, mentre il metodo HasChanges
restituisce True se uno o più campi o proprietà sono state modificate dopo l’ultimo
caricamento o salvataggio. In questo modo
è possibile salvare i dati solo se strettamente necessario:
If MySettings.Global.HasChanges Then
MySettings.Global.Save()
Creare una classe di impostazioni di
applicazione
MySettings.Global.Name
‘ Crea nuovi elementi per l’array RecentDocs
ReDim MySettings.Global.RecentDocs(9)
End Sub
Tutti i campi e le proprietà della classe
MySettings sono anche assegnabili, a meno
che non siano marcati con la parola chiave
ReadOnly (ma non ha molto senso inseri-
Il meccanismo per creare una classe che
contiene tutte le impostazioni a livello di
applicazione è identico a quello visto per le
impostazioni utente, con la sola differenza
che la classe deve ereditare da ApplicationSettingsBase (anziché UserSettingsBase). Un
esempio di classe di questo tipo è riportata
nel Listato 4.
Valgono per la classe MyAppSettings tut-
N. 66 - Novembre/Dicembre 2005
VBJ
23
TECNICHE
ti i commenti fatti per MySettings, inclusa
la possibilità di marcare campi e proprietà
con l’attributo XmlIgnore. La classe MyAppSettings si usa allo stesso modo:
quanto visto in precedenza è che la classe deriva direttamente da SettingsBase e che non
è necessario un costruttore statico:
Public Class MyProjectSettings
‘ Leggi una proprietà
Inherits SettingsBase
lblServer.Text = “Connecting to “ &
MyAppSettings.Global.DatabaseName
Public Documents() As String
Public StartupDocument As String
A differenza delle impostazioni di applicazione di VB 2005, i campi e le proprietà di
MyAppSettings possono essere assegnate e
l’istanza intera può essere salvata sul file di
default:
‘ altri campi e proprietà
‘ ....
‘ The Global instance (optional, but makes
usage easier)
Public Shared Global As New MyProjectSettings()
‘ Salvataggio esplicito sul file di default
End Class
MySettings.Global.Save()
Ovviamente, tocca al programmatore il
compito di accertarsi che un utente possa
sovrascrivere le modifiche fatte da un altro
utente. Nella pratica, questa feature dovrebbe essere abilitata soltanto per gli utenti
con i privilegi di amministratore, che possono quindi influire sulle modalità con cui
altri utenti usufruiscono della applicazione stessa.
In confronto, l’approccio imposto da Visual
Studio 2005 richiede che l’amministratore modifichi manualmente il file di configurazione,
quindi direi che la mia tecnica è sicuramente
più user friendly.
Impostazioni di progetto o soluzione
In questo caso non serve definire un costruttore statico, perchè la proprietà FileName non ha un valore di default iniziale, come accade per le impostazioni utente
e di applicazione. Per questo motivo, una
istanza di MyProjectSettings si carica con
un overload del metodo Load che accetta
un argomento (o in alternativa, con il metodo Load senza argomenti, ma solo dopo
aver assegnato una valore non nullo alla
proprietà Filename)
‘ Leggi una proprietà
Dim startDoc As String = MyProject
Settings.Global.StartupDocument
‘ Assegna una proprietà
MyProjectSettings.Global.Documents(0) =
In precedenza accennavo al fatto che spesso occorre salvare e ricaricare un gruppo di
variabili che non sono legate nè alla applicazione nel suo complesso nè al singolo utente. Per immaginare come questo possa essere utile, basti pensare ai file .sln o .vbproj che
crea Visual Studio per immagazzinare i dati
di un progetto o soluzione VB.NET. Questi
file di soluzione sono poi utilizzabili da più
utenti, possono essere spostati facilmente su
un’altra macchina, ecc.
Definire una classe di questo tipo è davvero
semplice. Le uniche due differenze rispetto a
24
VBJ
N. 66 - Novembre/Dicembre 2005
“c:\mydoc.txt”
‘ Salva (1° metodo)
MyProjectSettings.Global.Save
(“c:\mysettings.xml”)
‘ Salva (2° metodo)
MyProjectSettings.Global.FileName =
“c:\mysettings.xml”
MyProjectSettings.Global.Save()
Anche in questo caso è possible impostare
a True la proprietà AutoSave, in modo che il
TECNICHE
valore sia salvato automaticamente all’uscita
della applicazione, anche se in genere il salvataggio delle impostazioni di progetto dovrebbe essere fatto soltanto dopo aver chiesto all’utente se le impostazini correnti devono essere effettivamente salvate.
Alternative alla istanza Global
Se si preferisce non usare l’istanza static
Global occorre definire una variabile che sia
visibile dall’intero progetto, ad esempio una
variabile Public in un modulo oppure una variabile Shared in una classe o nel form principale. In questo caso è possibile eliminare
il codice nella sezione “Static Members”, ma
diventa necessario caricare esplicitamente la
variabile globale in questione:
Public Module MainModule
‘ L’istanza globale usata in tutta
l’applicazione
Public MySettings As New MySettings
Sub Main()
‘ Caricamento esplicito
MySettings.Load()
tionSettingsBase è abbastanza evidente: esse
forniscono tutta la flessibilità dell’oggetto
My.Settings senza dover passare a .NET 2.0,
e anzi otteniamo anche qualcosa in più, in
particolare la possibilità di caricare e salvare
le impostazioni da qualsiasi file o stream, e la
possibilità di modificare anche le impostazioni
di applicazione.
Certo, Visual Studio 2005 permette di definire la classe Settings in modo visuale, usando
l’apposito designer, però mi chiedo se davvero
questo approccio visuale sia meglio che non
scrivere una classe che contiene tutti i campi che ci interessano. Anzi, direi che preferisco di gran lunga l’uso di una classe scritta ad hoc, su cui ho certamente un controllo maggiore.
Il meccanismo
di persistenza in XML
della classe
è implementato
mediante l’oggetto
XmlSerializer
MsgBox(“Welcome, “ & MySettings.Name)
‘ ...(continua)
End Sub
End Module
Il vantaggio di questo modo alternativo di
usare MySettings è che i campi e le proprietà
si raggiungono con un solo “punto” anzichè
due (ad es. MySettings.Name anzichè MySettings.Global.Name).
Questo meccanismo vale ovviamente anche
per le classi di impostazioni di applicazione
(derivate da AppSettingsBase) e di soluzione
(derivate direttamente da SettingsBase).
Vantaggi e limiti
Il vantaggio della tecnica basata sulle classi SettingsBase, UserSettingsBase e Applica-
Il limite principale di questa versione della classe ApplicationSettingsBase è che le
impostazioni sono salvate in un file XML distinto dal file di configurazione “ufficiale”
della applicazione. Con un po’ di sforzo in
più questo problema potrebbe essere risolto, anzi se lo risolvete mandatemi pure il vostro codice.
Un altro limite, un po’ più serio, è che alcuni oggetti non possono essere serializzati in
XML. In particolare, gli oggetti che non hanno un costruttore senza argomenti e che le
cui proprietà sono a sola lettura hanno questo problema.
Tra i tipi che soffrono di questo problema, i
più comuni sono System.Drawing.Color e System.Drawing.Font: se provate a definire una
N. 66 - Novembre/Dicembre 2005
VBJ
25
TECNICHE
proprietà di questo tipo, nel file XML va a finire un elemento senza contenuto:
‘ Altri campi e proprietà
‘ ...
End Class
Public Class MySettings
Inherits UserSettingsBase
Public ForeColor As Color = Color.Blue
Public Structure ColorXml
Public A As Byte
Public R As Byte
End Class
Public G As Byte
Public B As Byte
Un modo poco elegante ma efficace per risolvere questo problema è marcare l’elemento
con l’attributo XmlIgnore e definire una proprietà pubblica che provvede alla serializzazione del valore in formato stringa. Ecco un
esempio:
<XmlIgnore()> _
Public Property Color() As Color
Get
Return Color.FromArgb(A, R, G, B)
End Get
Set(ByVal Value As Color)
Public Class MySettings
A = Value.A
Inherits UserSettingsBase
R = Value.R
G = Value.G
<XmlIgnore()> _
B = Value.B
Public ForeColor As Color = Color.Blue
End Set
End Property
Public Property ForeColorText() As String
End Structure
Get
Return String.Format(“{0},{1},{2},{3}”,
ForeColor.A, ForeColor.R,
Il codice nella applicazione può usare la proprietà BackColor come segue:
ForeColor.G, ForeColor.B)
End Get
Set(ByVal Value As String)
MySettings.Global.BackColor.Color =
New ColorXml(Color.White)
Dim parts() As String = Value.Split(“,”c)
ForeColor = Color.FromArgb(CInt(parts(0)),
CInt(parts(1)), CInt(parts(2)), CInt(parts(3)))
End Set
End Property
End Class
Se la classe MySettings contiene numerose proprietà di un certo tipo non serializzabile, allora può convenire definire una classe accessoria il cui unico scopo è permettere
la serializzazione del tipo in questione. Ecco
un esempio:
Public Class MySettings
Il vantaggio di usare una struttura anzichè
una classe per ColorXml è duplice: prima di
tutto non occorre ricordare di usare l’operatore New quando si definisce un elemento di
questo tipo nella classe MySettings. Secondo, e più importante, tutte le strutture ridefiniscono il metodo Equals per restituire True
solo se tutti gli elementi di due strutture coincidono. In questo modo il metodo HasChanges definito in SettingsBase continua a funzionare bene anche se la classe MySettings
contiene elementi di tipo struttura.
Bibliografia
Inherits UserSettingsBase
Public BackColor As ColorXml
26
VBJ
N. 66 - Novembre/Dicembre 2005
[1] http://msdn.microsoft.com/library/en-us/
dnvs05/html/vbmysettings.asp
APPLICAZIONI
Controllo Remoto
in Visual Basic .NET
Terza puntata
di Stefano Corti
I
n questa terza parte del nostro progetto di Controllo Remoto in Visual Basic .NET ci occuperemo della progettazione ed implementazione
del codice della classe RemoteProcesses. Mediante le
proprietà e i metodi pubblici che definiremo, sarà
possibile eseguire operazioni come creare nuove
cartelle sulla macchina remota, avviare, controllare
e monitorare i processi in esecuzione, stampare determinate tipologie di file utilizzando la stampante
remota ed infine impartire comandi DOS mediante un’interfaccia del tutto simile al Prompt dei Comandi di Windows (cmd.exe). Si tratta quindi di una
classe abbastanza complessa che, per ragioni che tra
breve illustreremo, dovrà ereditare anche proprietà
e metodi precedentemente definiti nella classe RemoteStructure, di cui abbiamo parlato nella precedente puntata. La nostra nuova classe prevede invece un semplice metodo costruttore, una proprietà
pubblica, dieci metodi pubblici e due metodi privati. Incominciamo subito ad illustrare il primo metodo createNewDir(), che oltre a fornirci immediatamente l’occasione per un primo cenno sul meccanismo dell’ereditarietà, ci consente di creare una
nuova Directory su Target. Osserviamo nel Listato
1 che il percorso completo della nuova Directory viene passato al metodo per valore mediante la stringa path. Sarà dunque necessario verificare se la Directory specificata non sia attualmente già presente
sulla macchina Target. Creiamo una variabile di tipo
Stefano Corti si occupa di programmazione PHP e JSP
lato server, della piattaforma .NET e di integrazione di
sistemi legacy con le nuove realtà del web, soprattutto in
ambito gestionale, bancario e finanziario. È attualmente alle
dipendenze di un primario gruppo bancario italiano. Può
essere contattato via email: [email protected].
28
VBJ
N. 66 - Novembre/Dicembre 2005
boolean che chiameremo flagD e
impostiamo tale variabile a True.
Se il metodo statico Exists() restituisce True, significa che la Directory specificata è già presente nella macchina remota, altrimenti dovrà essere creata invocando il metodo statico createDirectory, passando come argomento il valore stringa della variabile path. Le righe di codice
seguenti vengono eseguite solo
a condizione che la variabile di
tipo boolean flagD abbia assunto il valore True, ovvero soltanto nel caso che sia stata effettivamente creata la nuova nuova Directory. In tale condizione, provvediamo subito ad individuare la
posizione dell’ultimo carattere \
(backslash) presente nella stringa path. Questo ci consente (mediante la funzione di Visual Basic Left) di ottenere una nuova
stringa contenente un numero
di caratteri specificato scorrendo la stringa a partire dall’estrema sinistra. In questo contesto,
otteniamo quindi la Directory di
livello superiore che conterrà la
nostra nuova Directory appena
creata, unitamente ad altre cartelle e file eventualmente presenti. Mediante la parola chiave
MyBase, possiamo assegnare il
valore stringa alla proprietà Pro-
APPLICAZIONI
file precedentemente definita all’interno della classe RemoteStructure che risulta essere,
in definitiva, la classe “genitore” della classe
RemoteProcesses, essendo le due classi legate
attraverso l’istruzione Inherits, che consente
alla classe o interfaccia corrente di ereditare
gli attributi, i campi, le proprietà, i metodi e
gli eventi da un’altra classe o interfaccia. Nello stesso modo invochiamo anche il metodo
getFilesAndDirectories() per ottenere tutto il
contenuto della Directory superiore.
Ovviamente dobbiamo anche definire il codice preposto all’intercettazione del relativo comando proveniente dall’applicativo Controller,
unitamente al parametro ad esso associato.
Case Is = “MAKEDIR”
writer.Write(objProc.createNewDir(job(1)))
sentText.Clear()
sentText.Text = “Creazione di Directory su
Dischi Logici”
Iniziamo ora a prendere confidenza con le
classi del .NET Framework che ci consentono
Imports System.IO
di avviare, monitorare ed arrestare i processi e
Imports System.Diagnostics
le applicazioni in esecuzione. La classe Process
espone un elevato numero di membri per conPublic Class RemoteProcesses
trollare i processi in esecuzione sulle relative
Inherits RemoteStructure
macchine. Esamineremo poco per volta queste
funzionalità, ricordando che tale classe appar...
tiene allo spazio dei nomi System.Diagnostics
Sub New()
che dovrà quindi essere importato nel nostro
Me.mProc = “IExplore”
progetto, mediante l’istruzione Imports nelle
End Sub
prime linee di codice della classe.
Passiamo subito ad un esempio
Listato 1
Creazione di nuove cartelle su Target
concreto. Poniamo il caso che nella scheda File Manager nella GUI
Public Function createNewDir(ByVal path As String) As
Controller venga selezionato nel
String
ListView remoto un determinato
Dim dirReturn As String
file e poi venga premuto il pulsanDim flagD As Boolean = True
te ‘Avvia File Selezionato’. Questo
If Directory.Exists(path) = True Then
evento viene intercettato e gestidirReturn &= vbCrLf & “Attenzione: la Directory esiste già...”
to dal codice presente nel ListaElse
to 2. Al relativo comando STARTTry
Directory.CreateDirectory(path)
SELECTEDFILE viene associato
dirReturn &= vbCrLf & “Directory creata con successo
il parametro stringa contenente il
su PC remoto.”
Catch e As Exception
percorso completo del file individirReturn &= vbCrLf & “Errore: “ & e.Message
duato. Le procedure che consenflagD = False
End Try
tono di estrarre e trasformare in
End If
valore String un determinato elemento presente all’interno di un
If flagD Then
Dim found As Integer = 0
ListView, le analizzeremo in detfound = path.LastIndexOf(“\”)
taglio quando ci occuperemo delMyBase.Profile = Left(path, found) & “\”
dirReturn &= vbCrLf & “Contenuto attuale directory radice: “ & _
la progettazione delle interfacce
MyBase.getFilesAndDirectories()
utente dei programmi Controller e
End If
Target. Per ora ci basta notare che
Return dirReturn
al server viene inviato il comando
definito, unitamente al suo paraEnd Function
metro associato nelle consuete moImports System
N. 66 - Novembre/Dicembre 2005
VBJ
29
APPLICAZIONI
Listato 2
Codice associato alla pressione del pulsante Avvia Processo su Controller
Private Sub startProcByFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles startProcByFile.Click
Dim messGo As String = “STARTSELECTEDFILE>”
Dim list_x As ListView.SelectedListViewItemCollection
list_x = Me.ListView1.SelectedItems
Dim i_list_x As ListViewItem
For Each i_list_x In list_x
messGo &= i_list_x.Text.ToString()
Next
If (messGo = “STARTSELECTEDFILE>”) Then
MessageBox.Show(“Dovete prima selezionare un file da avviare.”, _
“Messaggio di Errore”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
Try
writer.Write(messGo)
inMessages.Clear()
inMessages.Text &= vbCrLf & “Comando Inviato: “ & vbCrLf & _
messGo
Catch exception As SocketException
inMessages.Text &= “Errore di comunicazione con il computer remoto: “ & _
exception.Message & vbCrLf
Catch ex As Exception
inMessages.Text &= “Errore: “ & ex.Message & vbCrLf
End Try
End If
End Sub
dalità. Vediamo adesso come il comando viene
interpretato e gestito dal server Target.
...
Private objProc As RemoteProcesses =
New RemoteProcesses()
...
Case Is = “STARTSELECTEDFILE”
objProc.AppliFile = job(1)
...
writer.Write(objProc.beginProc())
...
Abbiamo istanziato un oggetto (privato) dalla classe RemoteProcesses e lo abbiamo chiamato objProc. Nella struttura Select End Select, abbiamo previsto il caso che il comando ricevuto sia appunto STARTSELECTEDFILE. Alla proprietà AppliFile passiamo
il valore dell’array Job(1) che contiene appunto il percorso completo del file da avviare. Non ci resta ora che invocare il metodo
beginProc() per l’oggetto objProc che provocherà immediatamente l’avvio dell’applicazione associata a quella particolare estensio-
30
VBJ
N. 66 - Novembre/Dicembre 2005
ne di file, determinando l’avvio del processo
ad esso collegato.
Il codice dei metodi pubblici beginProc() ed
endProc() sono riportati nel Listato 3, mentre
il codice di implementazione della proprietà
Applifile è visibile nel Listato 4. Esaminiamo
ora più in dettaglio queste porzioni di codice.
Prima di tutto ci occorrono alcune variabili di
classe private. Dichiariamo xPro e p come variabili di tipo Process unitamente alle variabili
nProc, sProc ed mProc di tipo String. Dichiareremo poi altre variabili che ci serviranno per
ulteriori funzionalità che vedremo in seguito.
In particolare, per adesso, si presti attenzione
alle variabili sParam di tipo String (impostata
subito a Nothing) e alle variabili stringa mDelim, mDelemiter e all’array stringa pJob().
Private xPro, p As Process
Private oRemoteAll As Process()
Private oEnvi, oS As String
Private oDrives As String
Private oCurrentEnvironment As String
Private nProcArray As String
Private nProc As String
APPLICAZIONI
Private sProc As String
Private mProc As String
Private oArrow As String
Private sParam As String = Nothing
Private mDelim As String = “|”
Private pJob As String() = Nothing
Private mDelimiter As Char() = mDelim.ToCharArray()
Private iterator As Integer
stringa privata mProc. Successivamente viene
verificata la presenza del carattere | all’interno
della stringa. Il metodo IndexOf() restituisce il
valore negativo -1 se nella stringa esaminata
non viene individuato il carattere presente nell’argomento del metodo stesso. In questo caso è
semplice dedurre che unitamente al nome dell’applicativo non è stato passato alcun parametro aggiuntivo oppure è stato specificato il percorso assoluto di un file. La variabile privata
sProc, che ritroveremo nei metodi beginProc()
ed endProc(), viene posta uguale a mProc. In
caso contrario verrà determinato l’array pJob()
che conterrà in indice 0 il nome del processo
da avviare e in indice 1 il parametro specificato. Ovviamente il valore (stringa) di job(0) viene passato a sProc. Nel metodo beginProc(), all’interno di un blocco Try Catch End Try, viene
verificato se il valore della variabile privata sParam è rimasto uguale a Nothing oppure ha subi-
Nella GUI del Controller abbiamo infatti previsto diverse modalità di accesso ai processi remoti. Come abbiamo visto, il modo più semplice per avviare un processo consiste nel selezionare un determinato file nel file manager remoto e innescare quindi l’avvio dell’applicativo
associato a quella particolare estensione di file.
Abbiamo previsto inoltre un’altra scheda dove
l’utente ha a sua disposizione una serie di campi di testo e controlli più completi. Tale Control Tab è visibile nella Figura 1. Come si può
osservare, il Controller può avviare o terminare un’applicazione remota sempliceListato 3
Metodi pubblici beginProc ed endProc
mente specificando il nome dell’applicazione ed inserendo un eventuale parametro. Un semplice esempio potrebbe esPublic Function beginProc()
sere IExplore con parametro http://www.
Try
infomedia.it/. Questo provoca l’avvio imIf sParam Is Nothing Then
xPro = Process.Start(sProc)
mediato del browser Internet Explorer
Return vbCrLf & “Processo avviato...” & _
su Target e il recupero della URL spevbCrLf & “Processo attualmente in corso.”
cificata. Il parametro che il Controller
Else
invierà a Target sarà dunque, nel caso
xPro = Process.Start(sProc, sParam)
dell’esempio di cui sopra, strutturato in
Return vbCrLf & “Applicazione avviata...” &
vbCrLf & “Processo attualmente in corso.”
questo modo: LAUNCH>IExplore|http://
www.infomedia.it/. In pratica, nel codiEnd If
Catch e As Exception
ce che gestisce l’evento associato al pulReturn vbCrLf & “Errore: “ & e.Message
sante premuto, viene ricostruita la strinEnd Try
ga per mezzo delle righe riportate nel LiEnd Function
stato 5. Vediamo ora come tutto questo
viene gestito a livello Target.
Public Function endProc()
Try
Osserviamo ancora una volta il Listato
xPro.CloseMainWindow()
4. Abbiamo visto che il parametro inviaxPro.Close()
to deve essere ulteriormente splittato in
Return vbCrLf & “Processo chiuso...” & _
vbCrLf & “Applicazione attualmente chiusa.”
due parti per estrarre il nome del procesCatch e As Exception
so da avviare e il relativo parametro. Le
Return vbCrLf & “Errore Intercettato: “ &
e.Message
due porzioni sono divise da un carattere
End Try
separatore (| carattere pipe). Prima di tutEnd Function
to vediamo che il valore passato alla proprietà viene memorizzato nella variabile
_
N. 66 - Novembre/Dicembre 2005
VBJ
31
APPLICAZIONI
Listato 4
Proprietà AppliFile della classe Remote
Processes
Public Property AppliFile() As String
Get
Return mProc
End Get
Set(ByVal Value As String)
mProc = Value
If (mProc.IndexOf(mDelim) = -1) Then
sProc = mProc
Else
pJob = mProc.Split(mDelimiter, 2)
sProc = pJob(0)
sParam = pJob(1)
End If
End Set
End Property
alla corrispondente finestra principale e restituisce il valore Boolean True se il messaggio è
stato inviato correttamente, False se il processo associato non dispone di una finestra principale o se la finestra principale è disattivata.
Successivamente il metodo Close() libera tutte
le risorse associate al componente.
Vediamo ora come implementare un servizio
completo che consente di impartire, in un’apposita scheda dell’interfaccia Controller del
tutto simile al Prompt dei Comandi di Windows XP, qualsiasi comando DOS e fare in
modo che questo comando venga eseguito su
Target, ottenendo infine il relativo output. Con
determinati metodi di alcune importanti classi
del .NET framework, tutto questo può essere
progettato e realizzato con estrema semplicità.
Definiamo dunque il metodo pubblico promptInputOutput(), il cui codice è visibile nel Listato 6. Dobbiamo anzitutto creare un nuovo
componente Process che, in questo caso, chiameremo myPdos. Successivamente occorre utilizzare la proprietà StartInfo che rappresenta
l’insieme di parametri che servono per avviare un processo, impostando quindi le proprietà
da passare al metodo Start() del componente
Process. La seguente proprietà FileName è di
fatto l’unico membro necessario di StartInfo,
to cambiamenti. Nel primo caso, viene invocato
il metodo Start() della classe Process, passando
come argomento il valore di sProc, che conterrà
il nome dell’applicazione da avviare oppure il
percorso completo del file da eseguire. Si tratta
di un metodo di overload che, se viene determinato un solo valore String, avvia una risorsa di
processo, specificando il nome di un documento o un file di applicazione e associa la risorsa
a un nuovo componente Process. Diversamente (vedi codice dopo la Else) il metodo accetta
anche due parametri sempre di tipo String, innescando l’avvio di una risorsa di processo (in
questo caso viene specificato
il nome di un’applicazione e
un insieme di argomenti della riga di comando) e associa
la risorsa ad un nuovo componente Process. All’interno del
metodo endProc() vengono invocati invece i metodi pubblici
CloseMainWindow() e Close().
La chiamata di questi metodi
è relativa a xPro, che abbiamo visto essere di tipo Process a cui è stata appena associata la risorsa corrispondente
al processo precedentemente avviato. Più precisamente,
CloseMainWindow() chiude
un processo che dispone di
un’interfaccia utente invian- Figura 1 Scheda contenente i controlli dei Processi nella GUI del Controller
do un messaggio di chiusura
32
VBJ
N. 66 - Novembre/Dicembre 2005
APPLICAZIONI
Listato 5
Evento associato alla richiesta di avvio processo con parametro
Private Sub startProcButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles startProcButton.Click
If (txtStartProc.Text = “” Or txtStartProc.Text Is Nothing) Then
MessageBox.Show(“Dovete specificare un processo oppure il percorso di un file da lanciare.”, _
“Messaggio di Errore”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
Dim messGo As String
If (txtStartParam.Text = “” Or txtStartParam.Text Is Nothing) Then
messGo = “LAUNCH>” & txtStartProc.Text
Else
messGo = “LAUNCH>” & txtStartProc.Text & “|” & txtStartParam.Text
End If
Try
writer.Write(messGo)
inMessages.Clear()
inMessages.Text &= vbCrLf & “Comando Inviato: “ & vbCrLf & _
messGo
Catch exception As SocketException
inMessages.Text &= “Errore di comunicazione con il computer remoto: “ & _
exception.Message & vbCrLf
Catch ex As Exception
inMessages.Text &= “Errore: “ & ex.Message & vbCrLf
End Try
End If
End Sub
laddove l’avvio di un processo mediante specifica della proprietà FileName è analogo alla
digitazione delle informazioni nella finestra di
dialogo Esegui del menu di avvio di Windows.
In questo caso il nome del file da specificare
non può che essere l’eseguibile .exe che attiva il prompt dei comandi, ovvero cmd.exe. Impostiamo quindi la proprietà CreateNoWindow
su True, al fine di non determinare la comparsa della finestra GUI sulla macchina Target,
quando invocheremo il metodo Start(). Le successive tre proprietà richiedono più attenzione. Quando un processo viene avviato, supponiamo un eseguibile come appunto cmd.exe,
i canali di input predefiniti sono in genere la
tastiera o eventualmente periferiche di puntamento quali il mouse, mentre il canale di
output principale è quasi sempre lo schermo
collegato alla macchina sulla quale il processo
è in esecuzione. Per ottenere la remotizzazione di un processo è necessario variare o meglio reindirizzare i flussi di input/output verso
il processo medesimo. Le proprietà RedirectStandardInput e RedirectStandardOutput si
occupano proprio di questo specifico aspetto.
In particolare, RedirectStandardInput imposta
un valore che indica se l’input di comando del
processo deve essere letto dal membro StandardInput dell’istanza di Process, consentendo in tal modo la lettura dei dati da un’origine diversa dal flusso di input standard. RedirectStandardOutput opera in modo del tutto
analogo per i dati in uscita dal processo, provocando la scrittura del flusso su una destinazione differente dal flusso di output standard
che abbiamo visto essere, nella quasi totalità
dei casi, lo schermo del PC.
Prima di potere utilizzare queste proprietà, è necessario impostare la proprietà UseShellExecute su True, poiché è indispensabile segnalare al CLR del .NET framework che
non intendiamo utilizzare la shell del sistema
operativo per avviare il processo, bensì intendiamo creare il processo direttamente dal file
eseguibile. L’impostazione di questa proprietà
su False consente di reindirizzare i flussi di
input, output e di errore. Impostiamo quindi
le proprietà RedirectStandardInput e RedirectStandardOutput su True. Ora che abbiamo ottenuto il reindirizzamento dei canali di
I/O, dobbiamo innescare l’avvio del processo
mediante la chiamata del metodo Start() e vei-
N. 66 - Novembre/Dicembre 2005
VBJ
33
APPLICAZIONI
scrivere e leggere dati dell’applicazione. Ricordiamo che queste proprietà
ritornano valori di tipo StreamWriter e
Public Function promptInputOutput(ByVal ooPP_dos As
StreamReader, quindi dobbiamo anzitutString) As String
Dim DOS_report_output As String = Nothing
to creare una prima istanza della clasDim myPdos As New Process()
se StreamWriter che chiameremo DOS_
myPdos.StartInfo.FileName = “cmd.exe”
sss a cui verrà passato il valore (di tipo
myPdos.StartInfo.CreateNoWindow = True
StreamWriter) restituito dalla proprietà
myPdos.StartInfo.UseShellExecute = False
myPdos.StartInfo.RedirectStandardInput = True
StandardInput dell’oggetto myPdos di
myPdos.StartInfo.RedirectStandardOutput = True
tipo Process. Le classi StreamWriter e
myPdos.Start()
StreamReader gestiscono l’input e l’ouDim DOS_sss As StreamWriter = myPdos.StandardInput
tput di caratteri in una particolare codiDOS_sss.WriteLine(ooPP_dos)
fica (in genere UTF8 Encoding, se non
DOS_sss.Close()
DOS_report_output = myPdos.StandardOutput.ReadToEnd()
diversamente specificato) e differiscomyPdos.WaitForExit()
no sostanzialmente dalle classi derivamyPdos.Close()
te da Stream che gestiscono invece le
Return “DOSPROMPTREPLY>” & DOS_report_output
operazioni di I/O a livello di byte. Abbiamo così ottenuto l’oggetto DOS_sss
End Function
per il quale invochiamo il metodo WriteLine() (ereditato da TextWriter) che si
colare i dati in transito. Il metodo che stiamo
occupa della scrittura dei dati, come previsto
realizzando accetta in ingresso valori di tipo
dai parametri di overload, seguiti da un termiString passati per valore alla variabile strinnatore di riga. Il valore stringa passato al mega ooPP_dos. Chiaramente ooPP_dos assumetodo non è altro che il contenuto della variabirà il valore di job(1) il quanto il metodo verrà
le ooPP_dos. Invochiamo quindi il metodo cloinvocato nella struttura Select Case EndSelect
se() per determinare la chiusura del flusso. Ora
all’interno del ciclo principale del server conche abbiamo inviato il comando al processo,
tenuto nella classe Form1.
dobbiamo immediatamente estrarne l’output
opportunamente reindirizzato. Inizializziamo
Case Is = “GETDOSPROMPT”
una variabile stringa DOS_report_output e le
Try
assegniamo subito il valore restituito dal metosentText.Clear()
do ReadToEnd() invocato per l’oggetto di tipo
sentText.Text = “ESECUZIONE CMD.EXE” &
StreamReader ritornato dalla proprietà StanvbCrLf & job(1)
dardOutput di myPdos. Il metodo sovraccariwriter.Write(objProc.promptInputOutput(job(1)))
co WaitForExit() che invochiamo subito dopo
Catch exception As SocketException
la chiamata di ReadToEnd() è molto importansentText.Clear()
te in quanto indica al componente Process di
sentText.Text = “Errore di Comunicazione: “ &
attendere in modo indefinito la terminazione
exception.Message & _
del processo associato, al fine di consentire al
vbCrLf
thread corrente di attendere che tale processo
Catch ed As Exception
venga terminato. Infine il metodo Close() libera
sentText.Clear()
la memoria allocata al processo terminato.
Listato 6
Remotizzazione del Prompt dei Comandi DOS
sentText.Text = “Errore: “ & ed.Message & vbCrLf
End Try
A questo punto dobbiamo utilizzare le proprietà StandardInput e StardardOutput che
ottengono flussi utilizzati rispettivamente per
34
VBJ
N. 66 - Novembre/Dicembre 2005
Conclusioni
Nel prossimo appuntamento completeremo
la trattazione di questa importante classe e
realizzeremo un completo sistema di chat testuale tra Controller e Target.
.NET
Interprocess Communication
con MSMQ e .NET
Quasi tutte le applicazioni oggigiorno devono comunicare con l’esterno; vediamo un approccio alla comunicazione fra processi utilizzando la libreria MSMQ, che permette di scrivere applicazioni sicure e
aperte ai futuri sviluppi delle tecnologie
MSMQ
di Carlo Pagliei
E
sistono molti modi per far comunicare fra
loro due o più applicazioni: il modo classico è quello di definire un protocollo per lo
scambio dei dati e un tipo di trasporto. Ad esempio
potremmo utilizzare una socket TCP come trasporto
e il protocollo HTTP o FTP, oppure una porta seriale con un protocollo proprietario creato ad-hoc. Esistono poi altre soluzioni che possono risultare valide in casi particolari: ad esempio l’uso di file condivisi o di messaggi di sistema come WM_COPYDATA. Tutti questi approcci sono ugualmente validi
ma hanno un problema in comune: richiedono di
concentrarsi sia sul modo in cui sono scambiati i
dati, sia sul loro significato, costringendoci a spendere tempo e risorse su questioni “secondarie”: cosa
succede se la connessione cade? come garantire la
consegna dei messaggi? come fare per crittografare i messaggi o gestire permessi e utenti?
A queste esigenze ha risposto Microsoft con una
libreria che permette di ignorare tutti i problemi di
“basso livello” e di concentrarci sul lavoro che più
ci interessa: chattare e scambiarci informazioni.
Carlo Pagliei è laureato in ingegneria elettronica, lavora con
Etere srl dal 1996 e si occupa dello sviluppo di software per
il mondo del broadcast (TV, Radio). I suoi interessi principali
sono l’analisi e la progettazione di sistemi e l’applicazione
pratica della programmazione object oriented. Nel tempo
libero, bimbi permettendo, si dedica alla lettura e alle nuove
tecnologie.
36
VBJ
N. 66 - Novembre/Dicembre 2005
In questo articolo analizzeremo
gli aspetti principali di Microsoft Message Queue (MSMQ),
facendo in particolare riferimento alla libreria System.Messaging del .NET Framework, ne
vedremo un’applicazione pratica e infine daremo un’occhiata ai piani che Microsoft ha in
serbo per l’infrastruttura di comunicazione prevista su Windows Vista.
Introduzione a Microsoft
Message Queue
MSMQ non è una tecnologia
particolarmente nuova, esiste
ormai da diversi anni ed è disponibile per quasi tutte le versioni
di Windows: XP, 2000, NT4 (SP4),
95, 98, ME. Esistono comunque
delle differenze nelle funzionalità supportate dai vari sistemi
operativi; per semplificare, tutto quello che diremo in seguito
si riferirà a Windows XP Professional.
L’installazione è molto semplice: basta andare nel Pannel-
.NET
lo di Controllo, Installazione Applicazioni,
Windows Components, selezionare Message
Queuing e confermare. Appena terminato
possiamo aprire Gestione Computer, e alla
voce Servizi e Applicazioni troveremo anche
Message Queuing: da questa console possiamo amministrare le varie code e dare un’occhiata alla loro organizzazione.
La prima cosa che salta all’occhio è la presenza di tre tipi principali di code: di sistema,
private e pubbliche. Le code di sistema sono
utilizzate da MSMQ per scopi che in questo
articolo non approfondiremo (ad esempio memorizzare messaggi non consegnati). Le code
private sono accessibili solo sul PC dove risiedono a patto di conoscere l’intero percorso d’accesso. Le code pubbliche infine sono
“replicate” su ogni PC e disponibili su tutta
la rete attraverso la mediazione di Active Directory, ma non sono accessibili se non si è
parte di un dominio.
Proviamo a creare una coda ed apriamone
la form delle proprietà: le varie impostazioni
riguardano quelle relative alla sicurezza,
agli utenti, ecc. Ecco il primo aspetto notevole della libreria: MSMQ utilizza le procedure di autenticazione di Windows per verificare il mittente (certificati digitali, Kerberos V5, NTLM), può criptare i messaggi
in modo da renderli sicuri, può registrare
tutte le operazioni effettuate su una certa
coda. Tutto questo in maniera completamente trasparente allo sviluppatore: non dobbiamo preoccuparci direttamente di queste
funzionalità, la libreria ce le fornisce “gratuitamente”, inoltre funzionano nello standard di Windows, perciò un qualsiasi amministratore sarà in grado di configurare il
sistema egregiamente.
Caratteristiche principali di MSMQ
Se vogliamo spedire un messaggio di posta
elettronica ci basta conoscere l’indirizzo del
destinatario, senza preoccuparci se il ricevente sia effettivamente online: sarà il nostro
motore di invio che si occuperà del lavoro di
spedizione e consegna. MSMQ funziona esat-
tamente con lo stesso principio: qualcuno (il
ricevente) si connette ad una coda e attende
l’arrivo di messaggi, qualcun altro (il mittente) può spedire messaggi a quella coda senza
che sia fisicamente collegato ad essa.
I due oggetti con cui abbiamo a che fare sono
perciò le code ed i messaggi [1]: La coda è un
semplice contenitore di messaggi, un messaggio a sua volta è un contenitore di dati con
una serie di informazioni associate che indicano come trattarli: priorità, sicurezza, ricevute, temporizzazioni. Chiunque può leggere
(o spedire) messaggi da una coda a cui ha accesso, anche in multiutenza.
MSMQ è disponibile
per quasi tutte
le versioni di Windows:
XP, 2000, NT4,
95, 98, ME
MSMQ prevede due tipi di utenti: dipendenti e indipendenti. Un utente viene definito
dipendente quando ha necessità di un collegamento stabile e sempre attivo con la coda,
per cui se questa non è disponibile non è in
grado di funzionare correttamente. Gli utenti
indipendenti invece non necessitano di tale
collegamento e comunicano in modalità disconnessa: inviano messaggi che saranno poi
effettivamente consegnati quando la connessione alla rete sarà disponibile. Quest’ultima
è la modalità predefinita ed è chiaramente
quella per noi più interessante.
Accenniamo infine a due caratteristiche di
MSMQ che è difficile trovare in altre librerie:
il supporto alle transazioni e i trigger.
Grazie al supporto alle transazioni possiamo
associare N messaggi fra loro collegati in una
singola transazione: MSMQ garantisce che i
messaggi verranno consegnati una sola volta nell’ordine stabilito e prelevati tutti dalla
coda di destinazione, e che se anche uno solo
N. 66 - Novembre/Dicembre 2005
VBJ
37
.NET
dei passi causerà un errore, tutta la transazione verrà cancellata.
Ultima caratteristica interessante sono i trigger: è possibile specificare una serie di azioni (trigger) che MSMQ esegue quando su
una coda si verificano determinate condizioni (rules). Ad esempio potremmo impostare
un trigger che, quando nella coda X arriva un
messaggio che contiene una stringa predefinita, lancia un eseguibile, un file batch, uno
script o un oggetto COM. Con questa tecnica
è ad esempio possibile realizzare dei server
che vengono lanciati semplicemente inviando un messaggio su una coda, senza che nessuna applicazione sia in esecuzione.
La libreria System.Messaging
Il .NET Framework, fra le sue tante librerie,
ne ha una che corrisponde al namespace System.Messaging. Questo namespace contiene
una serie di classi che servono a creare, amministrare e monitorare code, a inviare e ricevere messaggi: l’attuale implementazione della
libreria si appoggia proprio su MSMQ [2].
MSMQ supporta
funzionalità avanzate
come la gestione
degli utenti, le transazioni
e i trigger
La classe più importante è MessageQueue,
che espone metodi per inviare e ricevere in
modalità sia sincrona che asincrona, controllare la presenza di messaggi, ricercare code
secondo vari criteri.
L’altra classe fondamentale è Message, che
contiene il messaggio vero e proprio insieme a
svariati parametri di controllo: Timeout, Acknowledgements, Authentication. I dati da inviare vengono inseriti nella proprietà Body:
al suo interno può essere inserita qualsiasi
38
VBJ
N. 66 - Novembre/Dicembre 2005
cosa, da una semplice stringa ad un oggetto serializzato.
Da queste poche righe si intuisce subito la
potenza dell’accoppiata MSMQ + .NET Framework: con poche righe di codice, senza
preoccuparci praticamente di nulla, possiamo
prendere un oggetto complesso quanto si vuole, serializzarlo, impacchettarlo in un messaggio e spedirlo a qualcun altro senza neanche
la necessità di controllare che il destinatario
sia collegato. Ci basterà attendere la risposta
o una ricevuta di MSMQ che ci informerà di
eventuali problemi.
Una semplice applicazione pratica:
MSMQ-Chat
Cominciamo finalmente a scrivere qualche
riga di codice per vedere come funzionano le
cose in pratica. Realizziamo una semplice applicazione dimostrativa, tipo Messenger, che serve
a scambiare messaggi con un amico o collega:
abbiamo una finestra che visualizza gli utenti
registrati (code), ne scegliamo uno, ci colleghiamo con l’utente, e iniziamo a chattare.
Problema 1: come fare per avere la lista degli
utenti? Ipotizziamo che ad ogni utente corrisponda una diversa coda ed utilizziamo il metodo statico MessageQueue.GetPublicQueues()
che ci fornisce la lista delle code pubbliche
registrate. Se non siamo in un dominio possiamo usare GetPrivateQueuesByMachine()
per ottenere le code private di un particolare
PC. Ottenuta la lista riempiamo un TreeView
per visualizzarla.
Problema 2: come gestire lo scambio di messaggi? Utilizziamo due classi: IpcQueue e IpcMessage. IpcQueue farà per noi il lavoro del
postino trasmettendo un oggetto IpcMessage
che, oltre al messaggio vero e proprio, conterrà l’indirizzo del mittente e altre informazioni supplementari.
Per quanto riguarda la classe IpcQueue abbiamo bisogno di poche cose: due metodi per
avviare e chiudere la connessione con la coda,
StartListening() e StopListening(), un metodo che ci consente di inviare un messaggio
ad una coda qualsiasi, SendTo(), e un even-
.NET
to che ci avverte dell’arrivo di un messaggio.
Internamente la classe utilizza due oggetti
MessageQueue, uno per la ricezione ed uno
per l’invio. Nel costruttore IpcQueue() su entrambe le code va inizializzato il formatter che
codificherà il messaggio, specificando esplicitamente la classe IpcMessage da serializzare.
Sulla classe “ricevente” va anche impostato
l’evento che informerà dell’arrivo di un nuovo
messaggio. Il frammento di codice che segue
descrive le operazioni da effettuare:
private IAsyncResult _listening;
...
private void _receiveCompleted(object sender,
System.Messaging.ReceiveCompletedEventArgs e) {
try {
Message m = _rxQueue.EndReceive(e.AsyncResult);
IpcMessage msg = new IpcMessage();
msg = (IpcMessage)m.Body;
/* codice che gestisce l’evento */
...
}
_rxQueue = new MessageQueue();
finally {
Type[] typArray = new Type[]{typeof(IpcMessage)};
_listening = _rxQueue.BeginReceive();
_rxQueue.Formatter = new XmlMessageFormatter
}
(typArray);
_rxQueue.ReceiveCompleted +=
return;
}
new ReceiveCompletedEventHandler
(this._receiveCompleted);
Vediamo ora brevemente come fare per inviare un messaggio: basta creare l’oggetto
IpcMessage, inizializzarlo e passarlo al metodo Send() che internamente ed in maniera
trasparente provvederà a formattarlo come
XML e inviarlo al destinatario:
public void SendTo(string address,
string aSubject,
string aMessage){
IpcMessage msg = new IpcMessage();
msg.SenderPath = _rxQueue.PathName;
msg.Subject = aSubject;
msg.Body = aMessage;
Non c’è molto da commentare: il codice è
semplice e abbastanza intuitivo anche se non
si ha una approfondita conoscenza di System.
Messaging. Quello che si nota è l’estrema semplicità e pulizia del codice che nasconde quasi
tutti i dettagli; l’unica scelta da fare è quella
del serializzatore, che naturalmente deve essere lo stesso per il mittente ed il destinatario.
L’applicazione completa è disponibile sul sito
Ftp di Infomedia, inoltre su [3] potete trovare
una trattazione di MSMQ e System.Messaging
discretamente approfondita e con svariate applicazioni di esempio.
Il futuro: Windows Communication
Foundation
_txQueue.Path = address;
_txQueue.Send(msg);
}
Resta infine da vedere come implementare
la ricezione; utilizziamo quella asincrona, altrimenti l’applicazione sarebbe bloccata fino
alla ricezione di un messaggio. Il codice è
molto semplice; appena arriva un messaggio viene attivato l’evento _receiveCompleted(), che internamente provvede a deserializzare il messaggio, ad eseguire su di esso
le operazioni richieste ed infine a riavviare
la ricezione:
Chi decide di appoggiarsi al .NET Framework
per scrivere applicazioni lo fa per diversi motivi, uno dei quali è sicuramente il fatto che
Microsoft sta spingendo su di esso in maniera
molto forte. Il prossimo anno dovrebbe essere
molto importante dal punto di vista dello sviluppatore, a fine 2006 è previsto infatti il rilascio della prima versione ufficiale di Windows
Vista: il sistema operativo, ora allo stadio di
beta 1, che prenderà il posto di XP.
Insieme a Windows Vista verrà rilasciato anche WinFX: una serie di librerie di alto livello (Foundations), basate sul .NET framework,
N. 66 - Novembre/Dicembre 2005
VBJ
39
.NET
che costituiranno il nuovo modello di programmazione Windows. L’SDK ed il runtime
di WinFX saranno disponibili, sempre a fine
2006, anche per Windows XP e Windows Server 2003.
In questo scenario di cambiamenti un posto in prima fila se l’è guadagnato Windows
Communication Foundation (nome in codice
Indigo): la nuova infrastruttura di comunicazione di Windows. Si tratta di un insieme di
tecnologie, costruite attorno ai Web Service,
che sono la naturale evoluzione delle tecnologie attualmente disponibili: COM, COM+,
MSMQ, ASMX, ecc. Tutte le funzionalità di
Indigo sono basate sul .NET Framework,
esposte come managed API e sono estensioni delle attuali librerie di Remoting, Web
Service, Messaging ed Enterprise Services.
Su [6] si può leggere un buon articolo che
introduce le idee alla base di Indigo, spiega
i nuovi concetti di servizi e contratti e infine descrive la libreria System.ServiceModel.
Attraverso l’uso di codice dimostrativo scende nel dettaglio abbastanza da capire come
viene progettata e costruita una nuova applicazione e come interagisce col mondo che la
circonda. Attualmente WinFX è in versione
beta 1, quindi potrebbero ancora esserci dei
cambiamenti di un certo rilievo, ma comunque chi scrive oggi dovrebbe essere in grado
di utilizzare quelle nuove cambiando poco o
nulla. Siamo pian piano arrivati alla parte interessante che dovrebbe far ingolosire qualsiasi sviluppatore: Microsoft afferma infatti
che tutti i sistemi basati su Indigo saranno in
grado di comunicare con sistemi che supportano MSMQ e viceversa [5], perciò chi oggi
sceglie di appoggiarsi alla libreria System.
Messaging si trova nella condizione ideale
di poter scrivere codice multipiattaforma in
grado di supportare sia sistemi legacy basati sulle API MSMQ (COM), sia sistemi futuri basati su Indigo. Chi fosse interessato ad
approfondire l’argomento, sul Windows Vista
Developer Center [4] può trovare informazioni preliminari, FAQ, approfondimenti, libri,
white paper, blog e quant’altro. Si possono
infine scaricare sia Windows Vista in versio-
40
VBJ
N. 66 - Novembre/Dicembre 2005
ne beta 1 che i vari SDK di tutte le Foundation anche per Windows XP.
Conclusioni
Abbiamo mostrato come sia possibile scrivere applicazioni che comunicano in maniera
semplice ed affidabile appoggiandosi a MSMQ
utilizzando, in maniera trasparente e senza il
diretto intervento dello sviluppatore, sia tutte
le caratteristiche di sicurezza che offre Windows (gestione degli utenti, crittografia, autenticazioni, ecc.) sia caratteristiche avanzate
quali il supporto alle transazioni, che sarebbero di difficile implementazione utilizzando
ad esempio una socket TCP.
Abbiamo anche scoperto che utilizzando
MSMQ attraverso la libreria System.Messaging del .NET Framework siamo in grado
di supportare sia sistemi legacy basati sulle API MSMQ, sia i futuri sistemi basati su
Indigo.
Bibliografia
[1] MSDN - “Message Queuing (MSMQ)”,
http://msdn.microsoft.com/library/default.
asp?url=/library/en-us/msmq/msmq_overview_4ilh.asp
[2] MSDN – “System.Messaging namespace”,
http://msdn.microsoft.com/library/default.
asp?url=/library/en-us/cpref/html/frlrfsystemmessaging.asp
[3] Adrian Turtschi et al. – “C# Book, Cap. 7:
Message Queuing using MSMQ”, Syngress,
2002
[4] Microsoft – “Windows Vista Developer Center”, http://msdn.microsoft.com/windowsvista/
[5] Microsoft - “Indigo Frequently Asked Questions”, http://msdn.microsoft.com/windowsvista/support/faq/communication/
[6] Clemens Vasters – “Introduction to Building Windows Communication Foundation
Services”, http://msdn.microsoft.com/webservices/indigo/default.aspx?pull=/library/
en-us/dnlong/html/introtowcf.asp#introt_
topic1
.NET
La GAC in salsa XML
Convertiamo la GAC in un file XML per recuperare velocemente le
informazioni su assembly, versioni e namespace
di Filippo Bonanni
N
ell’articolo sul motore di scripting in .NET
presente nel numero 64 abbiamo lasciato
in sospeso la questione di referenziare assembly di terze parti registrati nella GAC. Più in
generale possiamo porci questa domanda: esiste
un modo per scoprire, partendo da un namespace,
l’assembly in cui è contenuto?
La risposta che mi era venuta in mente consisteva nell’utilizzare la Reflection (vedi [1]) per ispezionare gli assembly alla ricerca del namespace in
questione, ma questa non è certo un’operazione
comoda da eseguire frequentemente; se però “sincronizziamo” la GAC con un file, magari XML, nel
quale esportare l’elenco degli assembly con le informazioni sulle versioni e sui namespace in essi
contenuti, diventa tutto più accessibile.
In questo articolo vedremo come realizzare un
“GACInspector”, un componente che possa essere
utilizzato per questo scopo: generare un file XML
riproduzione fedele della GAC.
La GAC in breve
La Global Assembly Cache non è altro che una directory, chiamata GAC, di solito contenuta nel percorso %windir%\assembly.
Per ogni assembly registrato, viene creata una
directory sotto tale percorso chiamata con lo stes-
GAC
so nome dell’assembly e quindi
un’ulteriore directory dal nome
di tipo Version__PublicKeyToken
per ogni versione registrata.
Ad esempio il percorso dell’assembly System.Data.dll, potrebbe apparire così
Directory di C:\WINDOWS\assembly\
GAC\System.Data
17/03/2005 11.35
<DIR> .
17/03/2005 11.35
<DIR> ..
17/03/2005 11.35
<DIR>
1.0.5000.0__b77a5c561934e089
0 File
0 byte
3 Directory 17.287.491.584
byte disponibili
Il file è contenuto nella cartella 1.0.5000.0__b77a5c561934e089
e se ci fossero state più versioni
avremmo trovato altre cartelle
analoghe a questa. In ogni cartella è presente anche un file
__AssemblyInfo__.ini riportante un riepilogo delle informazioni sull’assembly:
[AssemblyInfo]
Signature=a3ba424f0b663038e86ae83
Filippo Bonanni si occupa di informatica dal 1998 ed al
momento i suoi interessi sono rivolti principalmente all’ambiente .NET. Attualmente lavora nel team di sviluppo della
D&T Informatica di Sesto Fiorentino in progetti web-based
di interfacciamento a sistemi AS/400 e di archiviazione
ottica in ASP.NET.
42
VBJ
N. 66 - Novembre/Dicembre 2005
f72ffe2a5c2722466
MVID=c5596d3e16daa840b86afe4a5bf
c9e06
URL=file:///C:/WINDOWS/Microsoft.NET/
Framework/v1.1.4322/System.Data.dll
.NET
DisplayName=System.Data, Version=1.0.5000.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089
name=“calcr.dll”
>
<ver
Signature è la firma digitale ottenuta da una
combinazione dell’hash del file e della chiave
privata (facente parte della coppia Private/Public
Key utilizzate per l’assegnazione dello Strong
Name), MVID (Module Version ID) è un GUID
generato durante la compilazione, URL indica la
posizione dell’assembly ed infine DisplayName
fornisce informazioni sullo Strong Name.
Ecco la rappresentazione dello stesso assembly nel file XML:
id=“5.0.0.0__a1690a5ea44bab32”
DisplayName=“CalcR”
Version=“5.0.0.0”
Culture=“neutral”
PublicKeyToken=“a1690a5ea44bab32”
>
<namespaces>
<name>Samples.Math.Calculator</name>
</namespaces>
</ver>
<ver
<assembly
id=“6.0.0.0__a1690a5ea44bab32”
folder=“system.data”
DisplayName=“CalcR”
name=“System.Data.dll”
Version=“6.0.0.0”
>
Culture=“neutral”
PublicKeyToken=“a1690a5ea44bab32”
<ver
id=“1.0.5000.0__b77a5c561934e089”
DisplayName=“System.Data”
Version=“1.0.5000.0”
Culture=“neutral”
PublicKeyToken=“b77a5c561934e089”
>
<namespaces>
<name>Samples.Math.Calculator</name>
</namespaces>
</ver>
</assembly>
>
<namespaces>
<name>System</name>
<name>System.ComponentModel</name>
<name>System.Data</name>
<name>System.Data.Common</name>
<name>System.Data.Odbc</name>
<name>System.Data.OleDb</name>
<name>System.Data.SqlClient</name>
<name>System.Data.SqlTypes</name>
Con una struttura del genere, per risalire dal
namespace all’assembly che lo contiene è sufficiente una semplice query XPath (vedi [2])
che restituirà uno o più nodi, a seconda che
esistano una o più versioni o che il namespace sia ripetuto in più assembly.
L’algoritmo di sincronizzazione
<name>System.Xml</name>
</namespaces>
</ver>
</assembly>
Come si può vedere, la struttura replica le
informazioni contenute nella GAC ed in più
offre un elenco di tutti i namespace presenti nel file. Se invece il file ispezionato avesse
avuto più versioni, il risultato sarebbe stato
come il seguente esempio:
<assembly
folder=“calcr”
In Figura 1 è riportato il flusso decisionale
del nostro componente: se l’assembly (nella figura “Asm”), non è più in GAC viene rimosso dal file XML, se invece c’è e non è ancora stato censito viene aggiunto; lo stesso
procedimento è eseguito per le versioni, aggiungendo le nuove e rimuovendo quelle non
più presenti. A questo punto, per ogni nuova versione trovata si procede con l’ispezione dell’assembly relativo, alla ricerca di tutti i namespace in esso contenuti che vengono successivamente aggiunti nel documento XML.
N. 66 - Novembre/Dicembre 2005
VBJ
43
.NET
Struttura del programma
VerifyAssembly()
end set
Prima di iniziare l’ispezione dell’assembly
occorre fornire al programma alcune informazioni basilari: il percorso della GAC (che
non dimentichiamoci può essere modificato), il percorso completo del file XML di output ed infine il nome dell’assembly da analizzare
end Property
Public Sub New(GACPath as String, OutputFile
as String)
If GACPath Is Nothing Then GACPath=String.
Empty
If OutputFile Is Nothing Then OutputFile=
String.Empty
public Property AssemblyName as String
get
_GACPath=GACPath.Trim()
If _GACPath=String.Empty Then Throw New Exception
Return _AssemblyName
(“Specifica il percorso della GAC”)
end get
If Not(_GACPath.EndsWith(“\”)) Then _GACPath+=“\”
set
_AssemblyName=Value.Trim()
_filename=OutputFile.Trim()
If _AssemblyName=String.Empty Then
If _filename=String.Empty Then Throw New Exception
__AssemblyName=Nothing
(“Specifica un nome di file di output valido”)
assemblyFolder=_AssemblyName.Substring(0,
__AssemblyName.LastIndexOf(“.”))
assemblyFolder=assemblyFolder.toLower()
Me.LoadXml()
End Sub
completeAssemblyPath=_GACPath & assemblyFolder
If Not(completeAssemblyPath.EndsWith(“\”))
Then completeAssemblyPath+=“\”
Figura 1
Il flusso del GACInspector
Il percorso della GAC e il file di output
sono dichiarati come parametri da passare
al costruttore poiché rimarranno invariati
durante il ciclo di vita del componente mentre il nome dell’assembly è una proprietà
che potrà essere aggiornata per passare all’elemento successivo registrato in GAC.
Nel costruttore richiamiamo il metodo LoadXml() che crea il file XML nel caso non esista ancora e lo carica in un XmlDocument
(vedi [1]); nella proprietà AssemblyName recuperiamo il nome della cartella contenente l’assembly (che come abbiamo detto è il
nome del file stesso senza il suffisso .dll o
.exe) e, con il metodo VerifyAssembly(), verifichiamo la sua esistenza nella GAC. Da notare che partendo dall’analisi della GAC, quello che avremo immediatamente a disposizione sarà il nome della directory e non quello del file, quindi dotiamo il componente di
un ulteriore metodo che permetta di fare il
percorso inverso:
Public Function GetAssemblyName(assemblyFolder
as String) as String
Dim Result as String
44
VBJ
N. 66 - Novembre/Dicembre 2005
.NET
assemblyFolder=_GACPath & assemblyFolder
Result=Directory.GetFiles(Directory.GetDirectories
(assemblyFolder)(0))(0)
ziamo un oggetto XmlNode per la creazione
del nodo e un oggetto XmlAttribute per l’inserimento degli attributi:
Result=Result.SubString(Result.LastIndexOf
(“\”)+1)
Return Result
If findNode Is Nothing Then
newNode=doc.CreateNode(XmlNodeType.Element,
End Function
“assembly”, ““)
Da una cartella di tipo %GACPATH%\assemblyFolder recuperiamo dalla prima sottodirectory (una delle tante possibili versioni dell’assembly) il primo file (poiché i
file saranno solo due e __AssemblyInfo__
.ini sarà sempre al secondo posto per via
del suo nome, il primo sarà sempre quello
voluto) per ottenere il nome da utilizzare
nella proprietà.
attrib=doc.CreateNode(XmlNodeType.Attribute,
Rimozione di assembly non più in GAC
doc.DocumentElement.AppendChild(newNode)
“folder”,”“)
attrib.value=assemblyFolder
newNode.Attributes.Append(attrib)
attrib=doc.CreateNode(XmlNodeType.Attribute,
“name”,”“)
attrib.value=_AssemblyName
newNode.Attributes.Append(attrib)
doc.save(_filename)
Se la verifica dell’esistenza dell’assembly in
GAC ha dato esito negativo può essere dovuto al fatto che tutte le versioni sono state rimosse e quindi occorre rimuovere tutte le referenze anche dal file XML:
Private Sub RemoveAssembly()
Dim findNode as XmlNode=doc.SelectSingleNode(“/GAC/
assembly[@folder=’“ & assemblyFolder & “‘]”)
If Not(findNode Is Nothing) Then
doc.DocumentElement.RemoveChild(findNode)
End If
Ricerca di nuove versioni
Scendiamo di un livello ed incominciamo ora
l’analisi delle versioni dell’assembly. Per verificare la presenza di nuove versioni memorizziamo in un Array di stringhe tutte le possibili
directory di versione (quelle di tipo Version_
_PublicKeyToken) presenti in GAC mediante
il metodo Directory.GetDirectories():
doc.save(_filename)
End If
Private Function GetAssemblyVersions(AbsolutePath
End Sub
as boolean) as String()
Dim Result() as String
Con una query XPath proviamo a recuperare il nodo <GAC><assembly> con attributo
folder uguale alla directory non più presente
in GAC e se lo troviamo procediamo alla sua
eliminazione.
Aggiunta di nuovi assembly
Dim i as integer
Result=Directory.GetDirectories(complete
AssemblyPath)
If Not(AbsolutePath) Then
For i=0 to Result.Length-1
Result(i)=Result(i).Replace(completeAssembly
Path,”“)
Next
L’aggiunta prevede, come per la rimozione,
una ricerca dell’assembly all’interno del file
XML; stavolta però, se l’elemento non viene
trovato, si procede alla creazione del nodo con
l’assegnazione degli attributi; per farlo utiliz-
End If
Return Result
End Function
La variabile booleana AbsolutePath permet-
N. 66 - Novembre/Dicembre 2005
VBJ
45
.NET
te di decidere se memorizzare il percorso assoluto o relativo all’interno dell’Array.
Fatto ciò, confrontiamo le versioni presenti in
GAC con quelle del file XML sempre tramite
una query XPath, stavolta però a partire dal
nodo <assembly> (passato come parametro a
valore nella variabile Node), memorizzando in
un’ArrayList tutte quelle nuove:
Next
doc.save(_filename)
End Sub
Le versioni presenti nel file XML sono memorizzate in un XmlNodeList estraendo tutti
i nodi-figlio <ver> dall’elemento <assembly>
fornito come parametro alla funzione.
Aggiunta di nuove versioni
Private Sub FindNewVersion(ByRef alVer as Array
List, ByVal Node as XmlNode)
Le nuove versioni sono tutte quelle presenti nell’ArrayList generata dal metodo FindNewVersion(), quindi per ogni suo elemento
dobbiamo creare un nodo <ver> con i relativi attributi id, DisplayName, Version, Culture e PublicKeyToken.
Dim arrTemp() as String
Dim i as Integer
Dim findNode as XmlNode
arrTemp=GetAssemblyVersions(True)
For i=0 to arrTemp.Length-1
findNode=Node.SelectSingleNode(“ver[@id=’“ &
arrTemp(i).SubString(arrTemp(i).LastIndexOf
(“\”)+1) & “‘]”)
If findNode Is Nothing Then alVer.Add
(arrTemp(i))
Next
End Sub
Esiste un modo per
scoprire, partendo da
un namespace, l’assembly
in cui è contenuto
Eliminazione di Versioni
Con un confronto inverso – tramite il metodo
Array.IndexOf() che consente di ricercare un
oggetto all’interno di una matrice di oggetti,
cioè partendo dalle versioni presenti nel file
XML e verificandone l’esistenza nella GAC,
eliminiamo tutti i nodi non più necessari:
Mentre il primo è esattamente il nome della cartella di versione, gli altri attributi devono essere recuperati dal file __AssemblyInfo__
.ini che leggiamo attraverso uno StreamReader (vedi [1]) recuperando il rigo che inizia
per “DisplayName” e dividendolo successivamente in una matrice:
Private Sub ClearRemovedVersion(ByVal Node as
XmlNode)
Private Sub AddNewVersion(alVer as ArrayList,
Dim arrTemp() as String
Node as XmlNode)
Dim i as Integer
[Dichiarazione delle variabili]
Dim verNodeList as XmlNodeList
For i=0 to alVer.Count-1
Esci=False
arrTemp=GetAssemblyVersions(False)
sr=New StreamReader(alVer(i).ToString & “\”
verNodeList=Node.SelectNodes(“ver”)
& iniFile)
Do
For i=0 to verNodeList.Count-1
If Array.IndexOf(arrTemp,verNodeList.Item(i).
tempString=sr.ReadLine
If tempString.StartsWith(“DisplayName”)
Attributes.ItemOf(“id”).Value)=-1 Then
Node.RemoveChild(verNodeList.Item(i))
46
VBJ
N. 66 - Novembre/Dicembre 2005
Then Esci=True
Loop Until Esci or sr.Peek=-1
.NET
Una volta recuperati tutti i namespace li
aggiungiamo sotto l’elemento <ver> completando l’estrazione delle informazioni sull’assembly.
sr.Close()
arrTemp=tempString.Split(“,”)
[Creazione del nodo <ver>]
For j=0 to arrTemp.Length-1
[Creazione degli attributi]
Conclusioni
Next
[Chiamata alla funzione che recupera i
namespace]
[Aggiunta del nodo <ver> sotto il nodo
<assembly>]
Next
doc.save(_filename)
End Sub
Ispezione dell’assembly in cerca dei
namespace
A questo punto ci resta soltanto da ispezionare l’assembly alla ricerca dei namespace
in esso contenuti e per farlo ricorriamo alla
indispensabile Reflection. Dopo aver caricato
il file mediante la classe Assembly (vedi [1])
basta semplicemente estrarre in una matrice
tutti i tipi contenuti in esso e scorrerli alla ricerca dei namespace:
Dim a as [Assembly]
Dim arrT() as Type
Dim alNamespace as New ArrayList()
a=[Assembly].LoadFile(verPath & _AssemblyName)
arrT=a.GetTypes
For i=0 to arrT.Length – 1
If Not(arrT(i).NameSpace Is Nothing) And
(alNamespace.IndexOf(arrT(i).NameSpace)=-1)
Then alNamespace.Add(arrT(i).NameSpace)
Next
Il metodo Assembly.GetTypes() ci permette
di recuperare tutti i tipi e utilizzando nel ciclo
il confronto con la proprietà Type.Namespace, che restituisce il namespace del Tipo, individuiamo le informazioni volute. Da notare
che in un assembly ci possono tranquillamente essere molti Tipi appartenenti allo stesso
namespace, quindi per evitare di recuperare
il medesimo nome più volte verifichiamo con
il metodo ArrayList.IndexOf che l’elemento
non sia già presente nell’ArrayList.
Affinché il file XML possa essere di una qualche utilità, occorre che sia sempre allineato
con la GAC, quindi è necessario utilizzare il
componente descritto all’interno di un programma che la monitorizzi costantemente.
Nel codice allegato a questo articolo è disponibile un file d’esempio “GACWatcher.vbns”
(eseguibile con il motore di scripting per
.NET descritto nel numero precedente) che
tramite un oggetto FileSystemWatcher (vedi
[1]) controlla costantemente la GAC passando al GACInspector il nome della cartella modificata da ispezionare mediante due metodi,
OnChanged() e OnDeleted(), aggiunti proprio
per questo scopo; la soluzione più idonea sarebbe quella di utilizzare questo codice per la
realizzazione di un servizio che parta all’accensione del computer e resti quindi sempre
attivo in background.
Il file d’esempio “BuildGACFile.vbns” invece, permette di creare il file XML con la situazione attuale della GAC.
Per utilizzare il file XML all’interno del “motore di scripting per .NET” è sufficiente aggiungere una funzione che, partendo dal nome
dell’assembly, con una query XPath recuperi
il nodo contenente l’assembly stesso e quindi ricostruisca il percorso completo da utilizzare per referenziare il file al momento della
compilazione. Il nome dell’assembly potrebbe essere fornito come elemento del tag <references> con intestazione <gac> per distinguerlo da quelli <prv>.
Riferimenti
[1] .NET Framework SDK – http://msdn.microsoft.com/netframework/support/documentation
[2] XPath Tutorial – http://www.w3schools.
com/XPath
N. 66 - Novembre/Dicembre 2005
VBJ
47
SOFTWARE ENGINEERING
Model Driven
Architecture
Model Driven Architecture (MDA) è il termine con cui OMG identifica il suo tentativo di creare una architettura per lo sviluppo di applicazioni indipendenti dalla
piattaforma
di Lorenzo Vandoni
C
on il nome di Model Driven Architecture
(MDA) viene indicato quello che, ultimamente, è diventato uno degli argomenti maggiormente dibattuti tra i progettisti legati ai metodi object-oriented.
La proposta arriva dall’Object Management Group
(OMG), e l’idea è quella di separare completamente
la logica applicativa dalla piattaforma sulla quale
l’applicazione dovrà essere implementata.
Il modello dell’applicazione viene sviluppato utilizzando linguaggi di alto livello come UML, CWM
(Common Warehouse Metamodel) e MOF (MetaObject Facility), e viene quindi mappato su diverse piattaforme, come CORBA, COM, Java o .NET.
Il beneficio di MDA è costituito dal fatto che il modello applicativo rimane costante e può essere utilizzato anche per sfruttare le successive evoluzioni della tecnologia sottostante, o nuove piattaforme applicative non ancora esistenti nel momento
in cui il programma è stato sviluppato.
La conversione dal modello generico, indipendente dalla piattaforma, ad un modello più specifico,
legato a una particolare tecnologia, viene aiutata da
linee guida specifiche, e può essere automatizzata
almeno parzialmente da strumenti CASE.
Lorenzo Vandoni è laureato in Informatica, ed è uno specialista
di progettazione e sviluppo con tecniche e linguaggi objectoriented. Attualmente è direttore del Laboratorio di Ricerca e
Sviluppo di Emisfera e coordina un progetto di ricerca sulle
Reti Wireless finanziato nell’ambito del Sesto Programma
Quadro (FP6) della Comunità Europea. Può essere contattato
tramite e-mail all’indirizzo [email protected]
48
VBJ
N. 66 - Novembre/Dicembre 2005
OMG e MDA
OMG è un’organizzazione che
si occupa principalmente di
standard nel mondo dell’analisi, della progettazione e della
programmazione object-oriented (OOA, OOD, OOP). Uno degli scopi di OMG è quello di
creare meccanismi che permettano di applicare le medesime
tecniche di sviluppo, indipendentemente dal linguaggio di
programmazione e dal sistema
operativo utilizzato.
Questi sforzi hanno portato,
negli anni ’90, alla definizione
di CORBA, uno standard per
l’implementazione e l’utilizzo di
oggetti distribuiti, multilinguaggio e multipiattaforma. CORBA
ha avuto un buon successo, soprattutto in ambiente Unix, ed
è tuttora molto utilizzato, anche
se l’attenzione degli sviluppatori, soprattutto in ambiente Windows, è stata principalmente rivolta su meccanismi alternativi,
come COM, DCOM e, ultimamente, i Web Service.
Con MDA, OMG cerca di andare un passo al di là di CORBA,
suggerendo un metodo di lavoro
SOFTWARE ENGINEERING
che si renda indipendentente da
qualunque dettaglio implementativo. Applicando MDA, diviene
teoricamente possibile definire
il comportamento dell’applicazione in modo del tutto astratto, delegando poi la conversione
degli schemi in codice a meccanismi di traduzione più o meno
automatizzati.
L’architettura di MDA
Nella definizione di MDA,
OMG ha cercato di considerare
in modo pragmatico lo stato dell’arte nell’ambito di OOA, OOD
e OOP. Per quanto riguarda analisi e progettazione, ovviamente,
il linguaggio di riferimento è ormai da anni UML.
A questo linguaggio, però, Figura 1 L’architettura di MDA
OMG ha affiancato i meno noti
CWM (Common Warehouse Metamodel), un linguaggio per la
creare un modello UML dell’applicazione, inmodellazione di database e data warehouse,
dipendente da ogni specifica piattaforma di
MOF (Meta Object Facility), per la gestione
implementazione, basandolo su un determidegli oggetti all’interno di repository, e XMI
nato core model. Un core model, o UML profi(XML Metadata Interface), come formato di
le, è una specie di meta-modello, che descriscambio dei modelli.
ve le principali caratteristiche comuni ad una
In pratica, questo significa che i modelli podeterminata classe di applicazioni, come ad
tranno essere costruiti con UML, scambiati
esempio i programmi gestionali.
con XMI, e memorizzati in repository MOF.
Come si può vedere dalla Figura 1, UML,
MOF e CWM sono gli elementi centrali dell’architettura di MDA, e consentono di mascherare le differenze fra i vari “middleware”
indicati nell’anello più esterno, e cioè Java,
.NET, Web Service e CORBA. Tutti questi sistemi, anche se in modi diversi, forniscono un
insieme di servizi che vengono utilizzati dalle
applicazioni, tra cui funzionalità di sicurezza,
transazioni e servizi di directory.
In teoria, questo compito sarà riservato agli
Lavorare con MDA
analisti “puri”, mentre il passo successivo,
costituito dalla conversione del modello geCostruire un’applicazione conforme all’arnerico in un modello specializzato che facchitettura di MDA significa, come prima cosa,
cia riferimento ad una specifica piattaforma
L’ultimo passo
è costituito
dalla generazione
del codice applicativo
N. 66 - Novembre/Dicembre 2005
VBJ
49
SOFTWARE ENGINEERING
di implementazione, come Java o .NET, sarà
ad appannaggio di personale che conosca i
dettagli della piattaforma prescelta. Questo
passaggio sarà però guidato da alcune regole
di conversione, e quindi potrà essere almeno in parte automatizzato da parte di strumenti CASE.
Questo primo processo di conversione potrà
produrre, oltre ad un modello UML specializzato, anche del codice o altri elementi che potranno servire direttamente all’implementazione, come ad esempio dichiarazioni di interfacce o schemi di tabelle relazionali.
Il modello UML specializzato potrà contenere elementi “dialettali” specifici della
piattaforma di implementazione prescelta, per meglio identificare come un determinato aspetto dell’applicazione dovrà essere implementato con gli strumenti messi a disposizione. Avremo quindi, a seconda dei casi, elementi che rappresenteranno
un Web Service, un componente EJB, o una
stored procedure.
L’ultimo passo è costituito dalla generazione del codice applicativo a partire dal modello specializzato. Questo processo viene facilitato dalla disponibilità degli elementi “dialettali” appena citati, che potranno essere facilmente messi in corrispondenza con parti
di codice.
L’obiettivo di quest’ultima conversione è
quello di ottenere in modo automatico una
percentuale significativa del codice applicativo effettivamente richiesto, in modo da ridurre il lavoro da effettuare, la probabilità
di commettere errori, e quindi il costo dello sviluppo.
Modellare i domini
L’idea di creare dei meta-modelli di un particolare dominio applicativo non è nuova, ed in
OMG era stata finora tradotta in pratica tramite la definizione di interfacce IDL che permettessero di creare servizi CORBA standardizzati per ogni specifico settore applicativo.
Questa esperienza, che non ha avuto una
grande risonanza, è servita però a genera-
50
VBJ
N. 66 - Novembre/Dicembre 2005
re una profonda conoscenza di alcuni domini applicativi, che con MDA è stata messa in
pratica creando dei modelli UML che traducono questa conoscenza in qualcosa di più facilmente condivisibile.
Per ognuno di questi domini applicativi è
stato creato un modello UML astratto, indipendente dalla piattaforma, ed un modello
UML specifico, che serva anche come esempio di traduzione.
Sfruttare i servizi delle piattaforme
Ognuna delle piattaforme target considerate per l’implementazione offre una serie
di servizi, tra cui quelli più comuni sono la
gestione delle transazioni, della persistenza
e della sicurezza. Ognuno di questi servizi
viene definito in MDA in modo indipendente dalla piattaforma, in modo che possa essere referenziato nel modello generico dell’applicazione.
L’idea è quella di
separare completamente
la logica applicativa
dalla piattaforma sulla
quale l’applicazione dovrà
essere implementata
In questo modello, cioè, si potrà specificare di volere utilizzare in modo generico una
transazione. Saranno poi le regole di mapping specifiche per ogni piattaforma che tradurranno questa generica richiesta in un modello più specifico, che sfrutterà l’implementazione del servizio disponibile sulla piattaforma prescelta.
Anche le applicazioni già sviluppate con tecnologie precedenti (il cosiddetto codice “legacy”) possono essere incorporate in MDA tramite uno strato di codice esterno, in modo da
SOFTWARE ENGINEERING
permettere, da una parte, di integrarle con le
nuove applicazioni sviluppate e, dall’altra, di
migliorare il vecchio codice sfruttando i servizi resi disponibili dalle nuove piattaforme
e modellati con MDA.
Potenziali benefici di MDA
I potenziali benefici che questo approccio
porta con sé sono molti, e principalmente legati alla netta separazione che viene imposta tra logica applicativa e tecniche di implementazione.
Anzitutto, MDA risulta essere un’architettura del tutto aperta ed estendibile, anche in
vista di possibili future evoluzioni tecnologiche. Ogni nuovo linguaggio, sistema operativo o piattaforma, è infatti potenzialmente
incapsulabile in questa architettura, che promette il porting delle applicazioni tra piattaforme diverse, con uno sforzo di implementazione minimo.
Ci saranno poi benefici a livello di normalizzazione e standardizzazione del codice, che favoriranno la possibilità da parte di più persone
di intervenire sulla stessa applicazione.
La stesura dei core models, inoltre, potrà favorire una migliore comprensione di vari domini applicativi, trasformando conoscenze più
o meno vaghe in modelli condivisibili.
Conclusioni
L’idea alla base di MDA è sicuramente interessante, anche se non originalissima. Molti
sono stati infatti i tentativi di portare la definizione delle applicazioni ad un livello di astrazione più alto, in modo da svincolarsi dai pericoli e dai costi della codifica.
È interessante notare però, tra le motivazioni
citate, il fatto che la presenza di molti diversi linguaggi e piattaforme di sviluppo viene
ormai vista come un dato di fatto non superabile. L’obiettivo non è più, quindi, quello di
trovare un linguaggio migliore degli altri, che
possa imporsi come standard, quanto quello
di trovare un modo per svincolare l’applicazione dal linguaggio sviluppato.
Non si può negare che questo sia un problema reale. Molti di noi hanno vissuto nella
propria carriera transizioni più o meno traumatiche da Dos a Windows, da 16 a 32 bit, da
VB6 a .NET, per non parlare dei “veri” porting tra linguaggi e sistemi operativi diversi.
A molti, più volte, è capitato di dover riscrivere la stessa applicazione.
Il modello
dell’applicazione viene
sviluppato
utilizzando linguaggi
di alto livello
come UML
Forse MDA non sarà la soluzione definitiva
per questi problemi, però il fatto di essere basata su standard e, soprattutto, la maggiore
sensibilità emersa negli ultimi anni relativamente alla necessità di aderire ad essi, danno a questo approccio concrete possibilità di
realizzazione.
Va anche notato, d’altra parte, che MDA va
quasi in direzione opposta rispetto ad altri
movimenti emergenti, quelli delle metodologie agili. Da una parte abbiamo un’attenzione
quasi ossessiva per le fasi di analisi, dall’altra un atteggiamento più pragmatico, e una
maggiore attenzione per gli aspetti umanistici
del proprio lavoro, con particolare enfasi per
il ruolo dei programmatori.
Chi avrà ragione? Sarà politicamente troppo
corretto, ma forse potrebbero averla entrambi gli approcci, in quanto MDA è sicuramente più adatto per i grossi progetti, mentre le
metodologie agili possono essere vincenti per
applicazioni medio-piccole.
Non è escluso, poi, che non si possano cogliere spunti da entrambi gli approcci. Per eventuali approfondimenti vi rimando al sito ufficiale, ovvero www.omg.org/mda.
N. 66 - Novembre/Dicembre 2005
VBJ
51
REPORTAGE
Il nuovo umanesimo
tecnologico
a cura di Alberto Rosotti
L’ing. Umberto Paolucci, Senior Chairman
di Microsoft Europa, Medio Oriente e Africa
e Vice President Microsoft Corporation, è
stato ospite dei Giovani Imprenditori di Assindustria nel corso di un dibattito dal tema
“Il nuovo umanesimo tecnologico” tenuto il
16 settembre presso Assindustria di Pesaro.
Dal suo osservatorio privilegiato Paolucci ha
illustrato l’attuale scenario della globalizzazione, i rapporti umani, la comunicazione,
la formazione e la ricerca. L’impatto della
globalizzazione e delle moderne tecnologie è
fonte di grandi cambiamenti che devono essere
vissuti dalle imprese consapevolmente. L’incontro di questi due fattori incide ed inciderà
in maniera crescente sulla vita dell’uomo, dal
momento che l’uomo è il fulcro dell’impresa
competitiva. Ciò che ha più colpito dell’uomo
Paolucci è stata la sua umiltà, dote ormai rara
nei manager di successo. Invitato a parlare,
Paolucci ha rotto il ghiaccio esordendo: “Per
essere credibile davanti ad un pubblico di
giovani devo essere come loro, quindi permettetemi di togliere giacca e cravatta. Il mio
amico Bill si mette sempre comodo prima di
parlare ai giovani, la platea che preferisce”.
Un incipit semplice, seguito da una “lezione”
che ha catturato gli astanti. Ciò che segue
è la trascrizione integrale, fatta eccezione
per minimi adattamenti giornalistici, di un
discorso appassionante durato più di un’ora
e ricco di ottimismo; un esempio illuminante
ricco di spunti sui quali riflettere.
Il tema che ci vede qui riuniti è entusiasmante: vi parlerò
innanzitutto del nuovo mondo,
per il quale sto lavorando da 35
anni, poi parlerò dell’Europa e
dell’Italia che mi sta a cuore in
modo particolare e per terminare parlerò dell’uomo, che è la
cosa in cui dobbiamo credere di
più. Ma andiamo per ordine.
Il nuovo mondo
Alberto Rosotti è consigliere dell’Ordine degli Ingegneri di PesaroUrbino e dirigente sistemi informativi presso il gruppo di mobilifici
FBL - Della Rovere.
52
VBJ
N. 66 - Novembre/Dicembre 2005
Cominciamo dal software.
Sono convinto che oggi, dopo
tanti anni d’evoluzione, siamo
arrivati ad un punto in cui si
possono fare cose inimmaginabili, un punto in cui la complessità si riesce a delegare al sof-
REPORTAGE
tware e non alle persone. Questo è l’obiettivo
che abbiamo e l’obiettivo della mia azienda
in questi anni è stato quello di dare maggiore semplicità alle cose, di dare la possibilità
ad un numero sempre più ampio di persone
di usare questi strumenti, con un modello di
business basato su volumi alti e prezzi bassi:
tanti computer che costano poco, tanta gente che li può avere.
Ovviamente affinchè ciò si avveri è necessario che i computer siano ragionevolmente
facili da usare; di fatto il potenziale di trasformazione che i computer hanno è diventato così forte che si è creata una situazione
di divisione tra coloro che possono avvalersene - che sanno avvalersene - e gli altri, sia
a livello di aziende che a livello di persone.
Si è creato quindi un fossato; noi dobbiamo
cercare di colmare questo fossato, soprattutto per coloro che, senza volerlo, stanno dalla
parte sbagliata.
Quello che si è creato è un nuovo mondo che
in alcuni casi ci è sfuggito di mano; per esempio la bolla della new economy ha proiettato
entusiasmi eccessivi sul fatto ci fossero tanti nuovi modelli di business strani, che poi
non c’erano. Pensavamo che si potesse avere
un enorme incremento della quantità di traffico telefonico, e l’effetto lento di questa “illusione” è stato la creazione di un’infrastruttura ancora poco utilizzata, una banda larga
di comunicazione sulla quale si basa il nostro modo di gestire e anche di subire oggi
la globalizzazione.
La globalizzazione si è sviluppata prepotentemente grazie al fatto che esistono le reti
informatiche, ed esistono per tutti. Noi abbiamo creato delle porte di ingresso importantissime, larghissime, realizzate in un periodo di ottimismo eccessivo, che oggi possono e devono essere date a coloro che non
ne hanno la disponibilità. Questo è il risvolto positivo legato al “disincanto” della bolla di internet.
Quindi abbiamo un mondo globale nel quale, grazie alla tecnologia, si possono fare cose
come lavorare insieme, indipendentemente
da dove siamo, come dare valore all’informa-
zione, al lavoratore, alla conoscenza, e possiamo farlo con professionalità che prima non
esistevano; si sono creati degli stili di vita
nuovi, degli stili di lavoro inimmaginabili fino
a poco tempo fa. Avete visto, soprattutto voi
giovani, la convergenza tra i diversi “oggetti” nella loro trasformazione al digitale (come
fotografia, video, musica); questi oggetti si ritrovano a lavorare su piattaforme simili, si ritrovano a lavorare con modalità tali che non
è più un obiettivo irraggiungibile pensare di
metterli insieme nelle case e nelle aziende.
Questo definisce delle modalità nuove con le
quali viviamo la nostra giornata di persone,
modalità nuove con le quali viviamo la nostra giornata di lavoratori.
Noi possiamo stabilire dove vogliamo abitare, dato che è ormai semplice avere una forza lavoro globale sparsa sul pianeta, di fatto
sempre connessa alla rete. Grazie alla larga
banda condividiamo il nostro lavoro senza
limiti geografici.
Vi do qualche dato. Le email si sono mediamente moltiplicate per dieci dal 1997, ed
è previsto che si moltiplichino ancora per
cinque nel giro di tre, quattro anni. C’è una
spinta su questo sempre più forte, legata alla
produttività. Strumenti di questo tipo, che all’inizio erano opzionali, oggi sono indispensabili perché se i miei concorrenti li usano,
rendono obbligatorio ciò che prima era solo
opzionale. Semplicemente: l’asticella della
produttività è diventata più alta.
La sfida a cui siamo chiamati è quella di guidare questi strumenti, vedendoli come mezzi
e mai come fini nell’ambito della concorrenza, per non farci superare dagli altri e, possibilmente, per essere noi a superare gli altri.
Esistono ormai delle modalità operative con
le quali il software può accompagnare le nostre giornate, ci può gestire l’agenda in maniera raffinata, può capire cosa stiamo facendo in un certo istante e quindi non interrompere il nostro lavoro, può coordinare tutte le
possibili sorgenti che abbiamo di fronte, dai
telefoni agli sms, quindi darci un insieme di
stimoli produttivi ordinati e gestibili in funzione delle nostre priorità.
N. 66 - Novembre/Dicembre 2005
VBJ
53
REPORTAGE
Questi strumenti sono applicati a qualsiasi
tipo di azienda, a qualsiasi tipo di ricerca, non
solo nell’Information Communication Tecnology: se noi pensiamo per esempio che la ricerca automobilistica è basata per l’85% sull’uso avanzato dell’ICT, e che nella medicina
siamo al 70%, come nella biologia ed in qualunque disciplina dove occorre ricerca applicata, comprendiamo che c’è una convergenza fortissima fra ICT e scienza.
L’effetto netto della pervasività dell’ICT e del
progresso del computer è che noi tutti abbiamo accelerato il passo; ciò ha portato ad una
ricerca più veloce, ad una crescita maggiore,
ha favorito l’occupazione e potenzialmente
potrebbe essere ancora maggiore. Rispetto
all’occupazione l’ICT è come un’onda: un’onda lascia dietro di sé delle cose (anche negative) ma davanti a sé crea delle opportunità.
Quello che – in molti casi – abbiamo già visto
nei secoli scorsi.
La velocità alla quale ci siamo abituati, che è
cresciuta relativamente nell’ultimo secolo, oggi
è diventata tre volte, quattro volte superiore e
noi ci ritroveremo nel 2030 in un mondo molto diverso da quello di adesso. Se siamo stati
per quattro secoli, dal cinquecento in avanti,
in Europa ed in particolare in Italia, avanti a
tutti, gli Stati Uniti sono stati avanti a tutti nel
secolo scorso. Cosa accadrà nei prossimi anni?
Se paesi emergenti o già emersi come la Cina
e l’India riusciranno a proiettare quello che già
si vede oggi, avremo davanti un secolo - o per
lo meno una metà di secolo - nel quale saranno loro ad avere un ruolo preminente.
L’Europa e l’Italia
Qui dobbiamo decidere, ed entro nel tema
dell’Europa, se questi strumenti, se le nuove
tecnologie, sono così forti, se hanno cioè un
potenziale così forte da cambiare le cose. Dobbiamo cercare di capire se queste cose sono
per noi - in Europa - una arma competitiva
oppure un boomerang. In questo momento
l’ICT per l’Europa sembra un boomerang che
ci sta arrivando sulla fronte, non solo nell’ambito della tecnologia.
54
VBJ
N. 66 - Novembre/Dicembre 2005
Vorrei toccare il tema dell’Europa in senso lato, perché non si può parlare di tecnologia in Europa se non si parla della visione
che l’Europa ha di se stessa. L’Europa in questo momento una visione di se stessa non ce
l’ha. Messa in crisi nel corso del tempo, crisi
esplosa in maniera più visibile con il rifiuto di
avallare la Costituzione da parte di Paesi importanti, in primis la Francia, adesso l’Europa sta cercando una visione di se stessa, deve
capire dove vuole andare. Purtroppo l’Europa non è riuscita a darsi delle regole di governance quando era facile darsele, cioè quando
eravamo in 15; adesso siamo in 25 a lavorare con le vecchie regole, all’unanimità. Oggi
è più difficile sistemare le cose, però dobbiamo assolutamente farlo.
Vorrei spezzare una lancia sull’idea di non
buttarla via l’Europa, perché credo che sia
un’ottima opportunità per tutti: l’Italia ad
esempio da sola non può competere, pertanto sarà meglio che si inserisca in un sistema.
Per l’Italia e per altri paesi è indispensabile
arrivare alla convinzione che la nostra strada
per la crescita e lo sviluppo passi prevalentemente dall’Europa.
Ma permettetemi di parlare un po’ d’identità.
Qual è l’identità dell’Europa oggi? Andiamo
indietro nella storia: la Grecia, Roma, l’idea
di uguaglianza, di fratellanza che è venuta dal
mondo laico, poi cristiano, i nuovi valori che
sono venuti fuori dal Medioevo, dal Rinascimento, l’illuminismo, la rivoluzione francese
e a seguire quello che è successo nell’800 con
le guerre ed il sangue che ci ha portato dove
siamo arrivati adesso, agli Stati che abbiamo
adesso, con forti ideali di democrazia, libertà.
Non possiamo dimenticare tutto questo.
Voglio condividere con voi la mia visione e
la mia esperienza leggendovi una frase che ha
scritto il sociologo Domenico De Masi, che applica questo pensiero ai Paesi di cultura greco-latina; dice: “ la nostra naturale inclinazione all’interclassismo, alla separazione dei poteri, alla tolleranza, alla solidarietà, alla flessibilità, alla sensualità, all’allegria, all’estroversione, alla prevalenza dei bisogni radicali dell’amicizia, del gioco e della convivialità,
REPORTAGE
dei bisogni di ricchezza e di dominio”. Ecco
ciò che siamo.
Questi concetti non sono applicabili alla
realtà americana, dove la carriera è la priorità; in Europa la priorità è la famiglia. Non
voglio dire chi sia nel giusto o chi sbagli, ma
mi permetto di sottolineare di fronte a voi che
gli europei sono coscenti di chi e cosa sono.
Per questo, forse, di fronte al cambiamento
noi cerchiamo di cambiare il meno possibile,
cerchiamo di conservare il livello di benessere che abbiamo.
Ma oggi se non abbiamo idee ambiziose
rischiamo di perdere. Oggi spesso L’Italia
ed anche l’Europa è vista in chiave meramente politica, un paese che non ha deciso cosa vuole fare. L’Europa era partita alla
grande con la strategia introdotta dai temi
di Lisbona, la strategia che l’Europa aveva
adottato negli anni dell’ottimismo, all’inizio
del 2000, per creare l’economia della conoscenza più dinamica e competitiva al mondo. Purtroppo dal 2000 ad oggi ci siamo accorti che siamo invece andati indietro. Ora
dobbiamo riprendere quasta strada: la strategia di Lisbona era basata sulla Information Technology come anche l’economia della conoscenza: noi dobbiamo vincere questa corsa puntando sull’innovazione, sulla
conoscenza, sul valore, caratteristiche che
abbiamo e sulle quali non ci dobbiamo arroccare. Non possiamo vincere sul prezzo o
sul massimo ribasso.
Pensiamo al mondo dell’islam, quanto arroccato spesso sia nella difesa della sua cultura.
Io non riesco a fare affari con una persona
che tiene troppo alla sua religione; noi dobbiamo essere invece interoperabili. I Cinesi
ad esempio hanno la loro religione ed hanno
anche il loro modo di lavorare ma sono molto
interoperabili e ci stanno battendo.
La separazione tra il business, lo Stato, la
Chiesa, la religione sono dogmi ai quali siamo
abituati e con i quali dobbiamo fare i conti.
L’Italia sta andando male, lo dimostra la nostra percentuale nell’ambito dell’economia
mondiale; stiamo andando indietro, veniamo
diluiti nel PIL mondiale perché entrano altri
concorrenti, perché perdiamo terreno, perché
la nostra capacità di produrre non ha tenuto il
passo. Quindi certamente dobbiamo darci da
fare di più, esplorare tutte le strade, non buttare via niente per raggiungere i nostri obiettivi futuri, che non sono maggiore disoccupazione o i figli che stanno peggio dei padri, ma
esattamente l’inverso.
Quando abbiamo delle situazioni facili da
raggiungere o - come dicono gli americani - “i
frutti bassi”, non li raccogliamo. Ad esempio,
il turismo. Il turismo è un frutto basso per
l’Italia: il 60-70% del patrimonio mondiale è
qua; ma la Spagna ci ha battuto.
Queste cose non le possiamo più accettare.
Dobbiamo capire che cosa ci serve per vincere, qual è lo scenario nel quale l’Italia può
vincere. Dobbiamo fare in modo che coloro
che ci guidano abbiano chiara questa visione, dobbiamo fare in modo che le domande
che gli imprenditori si fanno abbiano una risposta, dobbiamo avere una visione su ciò che
vogliamo essere.
Io sono un uomo di business, non sono io
che devo dare una risposta se non per le componenti che mi riguardano. Certamente sto
cercando di offrire il mio punto di vista a coloro che hanno l’obiettivo politico, nel senso
più nobile della parola, coloro che devono indicarci la strada. Non possiamo assistere ad
uno scenario nel quale i Paesi che costano di
meno in tutto quello che fanno, come la Cina
e l’India, diventino anche i più bravi nell’investire sulla ricerca, abbiano gli ingegneri migliori dei nostri, siano il punto di attrazione
degli investimenti.
La Microsoft - sono fiero di comunicarlo - a
dicembre inaugurerà un centro di ricerca di
base a Trento, realizzato con l’università di
Trento. Vi assicuro che non è stato facile farlo in Italia. Da alcuni anni Microsoft ne ha
realizzato uno ben più grande in Cina, a Pechino, ed uno in India, con minor difficoltà.
Ecco la partita che ci giochiamo. Dobbiamo
fare in modo che esista una strada, un piano
per il nostro Paese che risolva i problemi che
abbiamo citato. Ormai di fotografie della situazione e di diagnosi ce ne sono infinite; lo
N. 66 - Novembre/Dicembre 2005
VBJ
55
REPORTAGE
stesso Presidente Montezemolo vi ha parlato sicuramente di queste cose, ma di terapie
possibili non ce ne sono poi tante, quindi noi
dobbiamo pretendere che queste “terapie” ci
vengano indicate.
Io credo che i temi di cui parlo siano temi
di lunga gittata, la strategia di Lisbona che
possiamo riassumere come “capitale umano, infrastrutture, conoscenze” è una cosa
che dà frutti nel tempo, però se non si comincia mai non si arriva mai, quindi bisogna
lavorare per il lungo periodo. L’imprenditore
se ne rende conto immediatamente, sa che
servono certezze sul lungo periodo, pertanto
è necessario che ci sia una condivisione politica su alcuni temi centrali che sono quelli della ricerca, dell’innovazione, del capitale umano, e che ci sia garanzia di lungo periodo sulle cose che portano il nostro Paese
verso il futuro.
Abbiamo bisogno che questi temi siano
compresi e quindi io spendo un poco di tempo anche per parlare di questi argomenti.
Secondo classifiche fatte su base mondiale,
l’università italiana è al 121° posto. Le università devono competere sulla base dell’eccellenza non sulla base dell’ubiquità. L’università italiana non ha obiettivi di eccellenza.
Dobbiamo pretendere una partnership forte
fra università ed impresa, ci deve essere una
osmosi, una condivisione della finalità della
ricerca tra imprese ed università, soprattutto verso le imprese medie e piccole che non
hanno capacità di fare ricerca, che non hanno
capacità di guardare oltre l’orizzonte di breve. L’università deve avere un valore, deve
avere una ricaduta imprenditoriale, questa
deve essere la misura del suo successo, in
modo che l’università sia una benedizione
in termini di creazione di posti di lavoro, in
tema di creazione di imprese. Microsoft ha
un centro di ricerca a Cambridge; intorno a
Cambridge c’è un fiorire di imprese che nascono dall’università e qui fiorisce l’eccellenza e la meritocrazia.
In Italia i cervelli di qualità non mancano, dobbiamo fare anche noi i campioni del
mondo, quindi meritocrazia ed eccellenza
56
VBJ
N. 66 - Novembre/Dicembre 2005
devono essere i nostri stendardi. Dobbiamo smetterla di accettare l’emorragia che
ci fa perdere le persone, la cosiddetta fuga
dei cervelli.
Il limite della nostra crescita sono le persone: la capacità di portare a termine un progetto non dipende tanto dalla quantità di
soldi investiti nella ricerca ma piuttosto dal
talento delle persone coinvolte nella ricerca. Abbiamo tanti esempi di ragazzi italiani
che vanno in America, per fermare l’emorragia dobbiamo ricostruire un ecosistema
che tenga conto di come siamo, del tipo di
aziende che abbiamo, del tipo di università
che abbiamo, e decidere quali siano le priorità di lungo periodo. Altrimenti le persone
non hanno più fiducia.
Bombardare i giovani con messaggi deprimenti non si può, bisogna cambiare le cose
fornendo contributi positivi, in particolare nei
momenti di crisi in cui questi contributi rappresentano qualcosa di concreto. A me non
sta bene che l’Italia vada indietro, bisogna
dare delle priorità e fare delle scelte, come
in una azienda.
Leggevo recentemente che un ragazzo americano di livello scolastico non eccellente aveva possibilità di carriera maggiori rispetto ad
un genio in Cina; adesso non è più così, adesso un genio in Cina ha più possibilità di quelle che aveva prima. Ed in Cina di genî probabilmente ce ne sono tanti altri, quindi non
dobbiamo perdere tempo.
Sembra assurdo parlare di crescita dell’occupazione quando oggi un imprenditore che
può va ad investire in altre parti, de-localizza. Una impresa deve fare ciò che è giusto per
l’impresa, però un sistema Paese deve trovare una strada comune con l’obiettivo finale di
mettere insieme i nostri punti di forza in un
profilo, in una miscela vincente settore per
settore. Alcuni punti di forza li abbiamo solo
noi e possiamo vincere. Purtroppo dovremo
renderci conto che qualche settore lo dovremmo perdere, perché per fare bene alcune cose,
altre ne dovremo dismettere. Dobbiamo darci
delle identità, dei brand che siano vendibili e
possano avere successo.
REPORTAGE
L’uomo e l’umanesimo tecnologico
L’uomo è al centro di tutto, le cose dette finora sono importanti ma contano relativamente poco rispetto all’uomo che c’è dentro,
in particolare la categoria dei giovani. Chi
ha in mano le cose che contano - per esempio la tecnologia – deve saper parlare ai giovani. La Microsoft è fatta di giovani, mentre
le forze politiche spesso non parlano ai giovani. La Confindustria è un punto di grande
eccellenza, riesce a lavorare con i giovani, a
dargli spazio e tenerli nel sentiero principale. Dobbiamo fare in modo che a nessun giovane venga detto “devi cercare lavoro altrove
se vuoi scappare da un declino”.
I Giovani vanno attirati con una visione,
con entusiasmo, con un obiettivo raggiungibile che non sia una sciocchezza. Se non c’è
un obiettivo capace di entusiasmare le persone - io lo vedo in Microsoft - che sia capace di catalizzare gli entusiasmi che hanno, tirare fuori le energie che hanno e che spesso
non sanno neanche di avere, non si va da nessuna parte. Bisogna che prendiamo in mano
la responsabilità che abbiamo perché lo scenario che ci troviamo di fronte è di cambiamento; ci sono delle indagini che dicono che
oggi un giovane di 25 anni quando avrà finito di lavorare avrà cambiato lavoro 15 volte.
Quindi bisogna fare in modo che si capisca
a cosa va incontro, per poter vivere con successo questo cambiamento, essere flessibile
e diventare davvero l’architetto della propria
vita. Serve potere guardare al lungo anziché
al breve, a capire quello che è importante rispetto a quello che è urgente. È necessario
potere costruire le pietre angolari sulle quali edificare il resto della casa; ci deve essere
passione, divertimento, attrazione ed anche
sacrificio, oltre al lavoro duro.
Se una persona non ha la possibilità di vivere bene nell’azienda ha tre possibilità: o si
rassegna, o cambia l’azienda da dentro oppure
cambia azienda. La prima possibilità, rassegnarsi, non la consiglio e dico a chi ha la possibilità di guidare gli altri come i manager, di
essere consapevoli del privilegio che hanno ed
anche della grande responsabilità da assumere. Immaginate i danni che può fare un manager che dà messaggi sbagliati ai suoi, che
gli smorza gli entusiasmi, che uccide la creatività, che danneggia l’autostima. Di questo
dobbiamo renderci conto fino in fondo, perché altrimenti noi, i nostri collaboratori e le
nostre aziende non vanno da nessuna parte.
Le cose più importanti per un manager sono
tre: la crescita dell’azienda, la crescita delle
persone e la crescita della soddisfazione dei
suoi collaboratori. Queste tre cose sono collegate tra di loro, ed il giudice di questo non
sono i capi superiori: sono i collaboratori stessi che devono aiutare il loro capo ad essere un
capo migliore, che devono guidarlo, che devono dire quello che non va bene, perché è
facile lamentarsi ma serve a poco. Quindi bisogna che ciascuno di noi - anche rischiando
- contribuisca al miglioramento dell’ambiente nel quale lavora, perché nessuno può avere niente per niente. Noi dobbiamo meritarci un lavoro migliore ed un capo migliore, il
nostro stesso successo.
Molti studi dimostrano che le aziende che
lavorano veramente sui valori hanno più successo e crescono più delle altre. Se solo guardassimo come si vive dentro queste aziende,
le cose che si fanno, come si passa la giornata, non c’è proprio confronto con il nostro contesto. In Italia ad esempio siamo più concentrati sui processi, le procedure, sulla catena
di comando e controllo; si cerca di prevedere
tutto ciò che è prevedibile: in questo caso fai
così, in quell’altro fai cosà, ma in quell’altro
caso ancora - a cui nessuno aveva pensato come ti comporti se non hai un riferimento
valoriale? Se non c’è una cultura aziendale
che ti possa guidare, per la quale puoi dare,
una volta che l’hai capita, il meglio di quello che hai dentro, ed anche di più, scoprendo con meraviglia che non sapevi di poter
dare di più, non valorizzi te stesso. In questo
modo posso condividere con voi ancora una
volta i valori nei quali io sto vivendo in Microsoft, valori nei quali ogni persona crede,
valori verso i quali ogni persona riporta sistematicamente il suo progresso.
N. 66 - Novembre/Dicembre 2005
VBJ
57
REPORTAGE
Se diamo ad un persona un obiettivo che
non è solo quello di raggiungere il suo fatturato, otteniamo la crescita personale ed il rispetto dei valori. I valori principali sono: l’integrità, l’onesta, la passione per quello che si
fa e la passione nel farlo insieme agli altri e
nel farlo per gli altri, il rispetto e l’apertura che non sono necessariamente la stessa cosa
- perché io posso essere aperto e brutale verso una persona oppure aperto e rispettoso. Io
preferisco dire ad un collaboratore che non ha
svolto bene il suo lavoro in maniera rispettosa, costruttiva, perché l’obiettivo è farlo vivere meglio e di più insieme a me. Mi impegno
a rendere gli altri migliori quindi a vivere il
successo degli altri: il successo di un manager non è il suo, ma quello delle persone che
gestisce, la capacità di prendersi grandi sfide e saperle vivere ogni giorno, spezzettarle
in pezzi eseguibili nei quali si possa vedere
ogni giorno l’autocritica.
Occorre saper mettere in dubbio le certezze
consolidate: io dico sempre a nostri ragazzi di
non essere allineati ma di cercare di generare un po’ di caos costruttivo, perché se siamo
tutti d’accordo e si passa la maggior parte del
tempo a cercare di capire cosa il capo pensa
e vuol sentirsi dire - come spesso succede in
molte aziende - allora andremo a finire tutti
verso il muro.
Occorre mettere impegno verso l’eccellenza, capire quello che si può fare meglio, dedicare impegno per i risultati, per la qualità; chi è in una posizione di particolare forza
deve essere particolarmente attento nel dosare la sua forza, nel rispettare i concorrenti, nel creare spazi anche per gli altri. Microsoft dosa continuamente la sua forza perché
se le sue innovazioni fossero troppo invasive potrebbero avere un effetto brutale, mal
accettato sia dai concorrenti che dalle persone in genere.
La complessità, la semplicità,
la diversità e la compassione
Affrontiamo adesso il grande tema di ridurre la complessità verso la semplicità, ovvero
58
VBJ
N. 66 - Novembre/Dicembre 2005
non nascondersi nella complessità ma ridurre le cose in termini semplici, perché le persone possano capirle, possano condividerle,
possano viverle insieme. Quindi non nascondersi ma chiarirsi. La semplicità, la convinzione di quello che si fa e l’autenticità sono
valori basilari: non si può simulare per molto
tempo, bisogna essere convinti delle cose che
si fanno, bisogna essere autentici. Le persone di fronte a noi sono il nostro comune denominatore, quindi dobbiamo rendere le cose
semplici per poter parlare ed agire in modo
comune. La tecnologia, anche la più complicata, l’obiettivo più raffinato, il messaggio più
difficile, devono essere assolutamente comprensibili, a misura dell’uomo che abbiamo
di fronte perché in tutti i possibili messaggi
si mantenga il rispetto, l’equilibrio e l’integrità della persona.
Poi il grande valore della diversità. Una cosa
bella tra le tante che ho vissuto è quella del
tirare fuori il valore della diversità: punti di
vista diversi, persone diverse, obiettivi diversi, storie diverse, paesi diversi e anche semplicemente uomo verso donna. La donna spesso
è discriminata perché viene tenuta indietro,
perché giustamente ha delle priorità diverse dagli uomini nella vita. Quindi tirar fuori il valore dalle donne è complesso e molte
aziende non sono capaci di farlo. Quando si
parla di diversità non è un discorso di tollerarla, perché tollerare è “accetto, però un po’
mi da fastidio”, bensì bisogna valorizzare la
diversità.
Poi se posso permettermi un altro suggerimento: cercare di non essere monomaniaci,
monocordi, monotematici, totalmente focalizzati su una cosa. Certo, soprattutto quando
una azienda è leader di settore, ha bisogno
di persone che siano altamente specializzate
e paranoiche in quello che fanno, però non
basta. Bisogna lavorare con gli altri, bisogna
essere aperti, avere altri interessi, essere delle persone vere a 360 gradi, essere delle persone che sanno stare con gli altri. Una bella
parola è “compassione” che significa avere
passione insieme agli altri ma anche compassione degli altri. E qui irrompe anche il
REPORTAGE
tema dell’autostima, il tema della difficoltà
che gli altri possono avere ed anche il tema
della compassione di se stessi. È lecito che
io mi ponga degli obiettivi irraggiungibili che distruggano la mia vita? Posso accettare il rischio se sono convinto di farcela però se poi in realtà non ci arrivo distruggo
la mia vita e quella di chi crede in me; allora meglio essere realistici fin dall’inizio: questo è il tema della compassione e della comprensione di se stessi. Certo ci vuole anche
la paranoia: molte aziende di successo sono
nate anche dalla paranoia, dalla forza, dalla
capacità di lavorare duro, dalla capacità di
concentrarsi del loro leader. Però il successo
deve essere anche accompagnato dalle altre
cose. Io credo che noi possiamo cambiare non in tempi brevissimi - questo scenario, se
avremo la fortuna di essere guidati dai leader giusti con i programmi giusti. Ma certamente non cambieremo governing di notte,
ci vorrà tempo.
I protagonisti di questo cambiamento sono
inevitabilmente i giovani e credo che si debba identificare un insieme di forze positive
per il cambiamento, in qualunque realtà, in
una realtà aziendale, in una realtà di associazione, di federazione, come in una realtà politica. Un insieme di forze che operano per i
cambiamenti in una direzione nella quale c’è
una terapia condivisa. Serve che lo facciano
con determinazione, con convinzione, con
spirito di servizio, con umiltà, con autenticità, con rispetto, con fiducia, con umiltà e con
fede nel futuro.
Credo che il nostro compito sia avanzare verso un mondo migliore di quello che noi abbiamo trovato, creare opportunità per gli altri, generare un cambiamento positivo; voglio
concludere con una frase, citando una canzone di Bono Vox, “Un uomo viene per cambiare
le cose”. Io credo che noi non siamo qui per
accettare passivamente quello che accade, ma
siamo qui per cambiare le cose.
I N O F F E R TA V B J 6 6
Imperfect XML Rants,
Raves, Tips, and Tricks ...
from an Insider
di D. Megginson
Prentice Hall
ISBN 0131453491
256 pp - 35,95 €
Pro MSMQ:
Microsoft Message
Queue Programming
di A. Redkar et al.
Apress
ISBN 1590593464
423 pp - 50,24 €
Model Driven Architecture
Foundations
and Applications
di A. Hartman
D. Kreischei
Springer
ISBN 3540300260
349 pp - 55,64 €
Scrivi a
[email protected]
specificando
nell’oggetto della
e-mail:
IN OFFERTA
VBJ n. 66
OPPURE
inviaci il coupon
sottostante
al numero di fax
0587/732232
Potrai acquistare
i libri qui riportati con
uno
SCONTO
ECCEZIONALE
del 10% anche se
acquisti solo un
libro
OPPURE
del 20% se acquisti
XML in a Nutshell,
Third Edition
di E. Harold
W. Scott Means
O’Reilly
ISBN 0596007647
712 pp - 39,95 €
Core C# and .NET
The Complete and Comprehensive Developer’s
Guide to C# 2.0 and .NET 2.0
di S. Perry
Prentice Hall
ISBN 0131472275
1008 pp - 46,95 €
Model-Driven Software
Development
di S. Beydeda et al.
Springer
ISBN 354025613X
464 pp - 85,55 €
3 libri
VBJ 66
.NET TOOLS
Mail2Rss
di Raffaele Di Natale
Posta elettronica e news feed in formato RSS 2.0:
due modi differenti di distribuire informazioni,
ma che grazie a Mail2Rss possono interagire in
totale sicurezza consentendoci di trarre il massimo dalle due tecnologie.
Immaginiamo una casella di posta elettronica
utilizzata da un gruppo d’utenti come una sorta
di repository di novità e aggiornamenti vari da
condividere quotidianamente. Supponiamo inoltre che gli utenti non vogliano ricevere i messaggi inviati dal resto del gruppo all’interno della
propria casella, ma semplicemente vogliano visualizzarli tramite una pagina web. In altre parole vorremmo che i messaggi di posta elettronica fossero semplicemente “visti” in maniera differente e distribuiti in un formato standard solo
se richiesti (come il testo che scorre in una porzione della home page del gruppo di utenti, per
esempio). Mail2Rss è proprio lo strumento adatto a risolvere questo particolare tipo di problema e non solo.
Un cenno su RSS 2.0
RSS 2.0 [Really Simple Syndication] è un formato orientato alla distribuzione d’informazioni
sul web (content syndication) basato su un dialetto di XML [1]. Un documento RSS è costituito
da un elemento <rss> che specifica la versione,
in questo caso la 2.0, ed un elemento <channel>
che contiene tutte le informazioni sui metadata
ed i loro contenuti.
Mai2Rss v.07 esegue una conversione dei messaggi di posta elettronica, presenti all’interno di
una prefissata casella, aggregandoli in formato
RSS. La particolarità dell’applicazione consiste
nel fatto che i messaggi elaborati restano ancora non letti per l’utente che successivamente li
apre tramite un normale client di posta elettronica. Si tratta di un web service .NET sviluppato in C# basato su due moduli denominati rispettivamente mai2rss.dll e Org.Mentalis.Security.dll [2]. Il primo rappresenta l’applicazione
principale; il secondo gestisce gli aspetti legati alla sicurezza nella connessione ed è un modulo esterno.
L’applicazione gira in ambiente Microsoft Windows 2000 Professional/Server, Windows XP Professional, Windows 2003 Server con IIS 5.0 e .NET
Framework 1.1.
È necessario predisporre almeno un account tra
POP3, Passport-MSN, Yahoo o GMail. Il file XML
prodotto dal servizio va poi manipolato mediante un software idoneo all’aggregazione di news
feed: il tool è stato testato con SharpReader [3],
FeedReader [4] e FeedDemon [5], ma funziona
correttamente anche con altri news aggregator
come RSSBandit [6]. L’applicazione descritta in
quest’articolo è stata installata su Windows XP
SP2 con IIS 5.0, .Net Framework 1.1, Internet
Explorer 6.0.
Installazione
La procedura d’installazione è sorprendente se
pensiamo di aver a che fare con un tool completamente gratuito. Dopo aver portato a termine il download dal sito [7] basta seguire il wizard che in pochi passi effettua l’installazione
del web service con relativa creazione della directory virtuale all’interno della cartella Inetpub
di default. Nel caso in cui la cartella Inetpub non
dovesse coincidere con quella dell’installazione, l’unico modo è quello di eliminare in IIS la
N. 65 - Settembre/Ottobre 2005 VBJ
61
.NET TOOLS
directory virtuale presente e ricrearla in corrispondenza della locazione preferita, ricopiandovi il contenuto della vecchia cartella. Una volta
installata è possibile rimuoverla dal pannello di
controllo di Windows. Al termine dell’installazione Mail2Rss può essere richiamata digitando ad esempio http://localhost/mail2Rss/ se localhost è il web server in cui abbiamo installato il web service.
Criptare la password
All’interno della home page è presente una sezione “Encrypt the password” che consente all’utente di criptare agevolmente la password relativa al proprio account di posta. Prima di lanciare
il web service è necessario infatti criptare la password, in quanto il servizio è richiamato tramite
una richiesta http di tipo get con testo in chiaro
e quindi facilmente intercettabile. Dopo aver inserito all’interno della testbox “password” la password ed aver premuto il tasto “Encrypt” sarà
visualizzata una stringa che rappresenta appunto la password criptata attraverso i servizi specifici dell’applicazione.
Mail2Rss può essere utilizzata senza alcuna variazione dei parametri di default: la home page
descrive sinteticamente la modalità d’utilizzo del
servizio. Si supponga che l’account di posta utilizzato sia di tipo Pop3, con username = nomeutente, password = miapassword e server POP3 =
dominioposta.it. Seguendo le indicazioni dell’home page, per ottenere le news feed relative alle
email presenti all’interno dell’account d’esempio
digitiamo sul browser:
l’interno della casella è presente un solo messaggio così costituito:
1.
2.
3.
4.
5.
Nome mittente: Paolo Rossi;
Indirizzo: [email protected]
Oggetto: Test Mail2RSS
Corpo del messaggio: Prova per Mail2Rss.
Allegato: allegato1.doc
il corrispondente file xml visualizzato è così
strutturato:
<?xml version=”1.0” encoding=”utf-8”?>
<rss xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
version=”2.0”>
<channel>
<title>RSS 2.0 email feed from dominioposta.
it</title>
<link>http://mail2rss.sourceforge.net</link>
<description>RSS 2.0 email feed from
dominioposta.it</description>
<generator>mail2rss v0.7</generator>
<copyright>Copyright (C) 2004 by Charlie
Kostov</copyright>
<ttl>60</ttl>
<language>en-us</language>
<lastBuildDate>Sat, 22 Oct 2005 16:40:10
GMT</lastBuildDate>
<item>
<title>Test Mail2RSS</title>
<link />
<description>Prova per Mail2Rss ===[ mail2rss
detected attachment(s) list ]====== Attachment
filename: allegato1.doc </description>
<author>”Paolo Rossi” &lt;
http://localhost/mail2rss/mail2rss.asmx/GetMail?mail
Server=dominioposta.it&userID=nomeutente&userPass=
[email protected]&gt;</author>
<pubDate>Fri, 21 Oct 2005 19:45:29 +0200</pubDate>
password_criptata
<category>mail message</category>
</item>
Dopo qualche secondo d’attesa, in relazione alla
velocità di connessione, al tempo di risposta del
server, al numero di messaggi ed al contenuto degli stessi, vedremo apparire un file XML in formato RSS 2.0 che aggrega tutti i messaggi trovati.
Nel caso in cui un messaggio preveda uno o
più file allegati, il loro nome verrà elencato all’interno della news relativa. Se per esempio al-
62
VBJ N. 66 - Novembre/Dicembre 2005
</channel>
</rss>
Tralasciando le tipiche intestazioni XML si possono notare l’elemento rss, il channel con i riferimenti all’applicazione e più in basso l’unico item
di tipo mail message che rappresenta il messaggio in precedenza descritto.
.NET TOOLS
All’interno di tale item si riconoscono:
l title, l’oggetto del messaggio;
l description, corpo del messaggio, con gli even-
tuali allegati;
l author, il mittente con nome e indirizzo;
l pubDate, data d’invio del messaggio
All’interno della casella di posta elettronica possono essere presenti più messaggi; in questo caso
si avrà una sequenza finita di item di tipo mail
message.
Utilizzando un visualizzatore di news feed, nel
nostro esempio RSSBandit, il risultato finale avrà
un aspetto simile a quello descritto in figura.
Conclusioni
Mail2Rss è un’applicazione che svolge un compito semplice, ma lo fa senz’altro in maniera
soddisfacente.
Come anticipato nell’introduzione, Mail2Rss
risolve un problema specifico, ma considerando l’abuso che spesso si fa oggi con mailing
list o strumenti simili, questo web service,
grazie all’interazione tra due servizi apparentemente slegati tra loro rappresenta una valida alternativa nella distribuzione d’informazioni nel web.
Riferimenti
[1] http://blogs.law.harvard.edu/tech/rss
[2] http://www.mentalis.org/soft/projects/ssocket/
[3] http://www.sharpreader.net/
[4] http://www.feedreader.com/
[5] http://www.bradsoft.com/feeddemon
[6] http://www.rssbandit.org/
[7] http://sourceforge.net/projects/mail2rss
Prodotto
Configurazione
Come detto in precedenza non è necessario variare alcun parametro per utilizzare il servizio
in maniera standard. In ogni caso, come in tutti
i web service .NET, il file denominato web.config permette di personalizzare la configurazione
dell’applicazione. È possibile variare il numero massimo di messaggi elaborati per ogni account, la dimensione massima del singolo messaggio e gli indirizzi standard per i gateway relativi ai principali servizi di webmail come yahoo,
msn, hotmail. Sempre all’interno del file di configurazione si può modificare la chiave utilizzata per crittografare la password, creandone una
personale.
La visualizzazione delle news feed può avvenire
offline utilizzando un tool specifico oppure online.
Nel secondo caso la strada senz’altro più agevole
è quella di utilizzare i moduli standard, presenti
ormai nella maggior parte dei CMS, atti alla visualizzazione di news feed da un URL specifico.
Mail2Rss
Url di riferimento
http://sourceforge.net/projects/mail2rss
Stato Release
v. 07
Semplicità d’uso ★★★★✩
Eccellente la procedura d’installazione. Applicazione semplice da
usare. L’unica limitazione è legata alla necessità di modificare manualmente l’indirizzo di connessione. Nella prossima release sarà
fornito un supporto per testare i parametri di connessione.
Utilità ★★★★✩
L’applicazione del servizio avviene in una nicchia della distribuzione dell’informazione. Risolve in maniera soddisfacente il problema di pubblicare come news feed RSS i messaggi di un dato account di posta elettronica.
Qualità prodotto ★★★★✩
Buona, nei test effettuati il servizio ha sempre completato il suo
compito nonostante alcuni casi di lentezza nella connessione al
server di posta. Sono gestiti anche gli eventuali errori di connessione al server di posta.
Qualità documentazione ★★★✩✩
Scarsa, se si eccettua la documentazione della home page del
servizio
N. 66 - Novembre/Dicembre 2005 VBJ
63
.NET TOOLS
DevPartnerProfiler
di Fabio Perrone
Uno strumento gratuito per misurare le prestazioni di applicazioni .NET
Considerare le prestazioni quando si scrive del codice dovrebbe sempre essere una priorità per ogni
programmatore che si rispetti. Il problema è che
non sempre si possiedono le conoscenze necessarie
o una quantità sufficiente di tempo a disposizione
per risolvere il problema alla radice, vale a dire scrivere codice che utilizzi le migliori pratiche offerte
dal linguaggio in cui si sta sviluppando, per ottenere applicazioni prestazionalmente soddisfacenti. È
ovvio che in applicazioni particolarmente complesse le cattive prestazioni di un programma non dipendono solamente dal codice scritto male, ma influscono negativamente altri fattori quali la lentezza della rete, l’accesso a database remoti e così via.
Per ottimizzare il codice esistono tuttavia strumenti quali DevPartner Profiler della Compuware (che
rientra nella categoria dei cosiddetti “Performance
analysis tool”) che possono aiutare a scoprire eventuali colli di bottiglia nell’applicazione.
DevPartner Profiler viene installato come addin per Visual Studio 2003, quindi lo ritroviamo
nel menu Strumenti, sotto la voce DevPartner.
La prima operazione da fare, se si vogliono effettivamente testare le prestazioni solo della propria
applicazione, è quella di escludere il tempo trascorso in thread d’altre applicazioni che possono essere in esecuzione in quell’istante e considerare solo i thread del nostro programma. Così
facendo, i dati che saranno prodotti risulteranno
totalmente indipendenti da ingerenze esterne. La
flessibilità di questo prodotto si denota anche dal
fatto che può raccogliere dati da chiamate effettuate verso componenti scritti da terze parti, inclusi componenti COM per cui non si ha a disposizione il codice sorgente. È possibile effettuare
test sia su applicazioni Windows Forms sia su applicazioni Web ASP.NET, sia su applicazioni native scritte in C++ utilizzando la tecnologia ATL,
con l’unica limitazione che tutto venga eseguito
su un’unica macchina, in pratica non in remoto
(questa opzione viene superata se si acquista la
versione non Community del prodotto).
64
VBJ N. 66 - Novembre/Dicembre 2005
Per attivare la procedura di raccolta dei dati, è sufficiente selezionare la voce di menu che abbiamo
menzionato in precedenza e premere Ctrl+F5 per
avviare l’applicazione senza debug – ciò non perché
il programma non sia in grado di raccogliere dati
anche in sessioni di debug, ma perché ci sono meno
dati da raccogliere e meno interferenze. Una volta
lanciata l’applicazione, è possibile effettuare qualsiasi genere d’operazione che si desidera, tutto sotto
l’“occhio vigile” di DevPartner Profiler che registrerà (senza che lo sviluppatore se ne accorga, e quindi
abbastanza rapidamente) ogni singola operazione effettuata. Per ultimare l’analisi è sufficiente terminare l’applicazione, ottenendo così, direttamente nell’IDE di Visual Studio.NET, un file con estensione
.dpsession che conterrà una notevole mole d’informazioni, utili per capire quali metodi o addirittura
quali singole istruzioni sono più lente.
I dati raccolti sulle prestazioni includono informazioni sugli assembly, sulle classi e sul codice sorgente. Una volta che si hanno questi dati a disposizione,
spetta in ogni caso al programmatore intervenire e
correggere eventuali anomalie nel codice.
Le unità di misura delle prestazioni possono essere cicli macchina, microsecondi, millisecondi o
secondi, a scelta dell’utente. Le statistiche generali che ci vengono presentate variano a seconda se
si analizzano i metodi o se si analizzano le singole righe d’ogni metodo. Nel primo caso si ha a disposizione una grande quantità di dati, i cui principali sono la percentuale del tempo trascorso in
quel metodo rispetto al tempo totale trascorso, il
numero di volte che un metodo è stato chiamato, il
tempo medio d’esecuzione, l’esecuzione più rapida
e più lenta del metodo, il tempo reale impiegato,
in pratica considerando anche thread d’altre applicazioni (ignorando quindi l’impostazione accennata in precedenza) e il tempo percepito, vale a dire
una misurazione che comprende anche il leggero
sovraccarico dovuto alla presenza di DevPartner
Profiler durante l’esecuzione della nostra applicazione. Nel secondo caso, cioè quando si analizzano
le singole righe di codice, abbiamo a disposizione
il numero di volte che una riga è stata eseguita, la
percentuale di tempo relativo a quella riga rispetto al metodo che la contiene, il tempo totale della
riga incluse le chiamate ad eventuali metodi e il
numero di metodi richiamati da quella riga.
.NET TOOLS
Un ulteriore metodo visuale a nostra disposizione
è rappresentato dal grafo delle chiamate effettuate verso un metodo e da quel metodo verso altri
metodi: tramite i cosiddetti percorsi critici (cioè
la successione di metodi figli chiamati da un metodo, che hanno accumulato la maggiore quantità di memoria) è possibile mettere in evidenza i
cosiddetti “colli di bottiglia”.
Nonostante alcune limitazioni della versione
Community, quali l’impossibilità di confrontare
due sessioni di test, l’esecuzione dei test all’esterno di Visual Studio.NET, l’obbligo dell’esecuzione
delle applicazioni da un’unica macchina, DevPartner Profiler risulta un ottimo prodotto per chi ha
esigenze urgenti di miglioramento di prestazioni. Utilizzato con altri tool quali FxCop, dovrebbe
permettere di ottenere buoni risultati in poco tempo. È comunque bene ricordare che se le prestazioni di un’applicazione vengono monitorate sin
dall’inizio dello sviluppo, i risultati saranno decisamente migliori che tentare di “mettere delle
pezze” ad applicazioni già ultimate.
Prodotto
Compuware DevPartnerProfiler Community Edition
Url di riferimento
http://www.compuware.com/products/devpartner/profiler/default.asp
Stato Release
Stabile
Semplicità d’uso ★★★★✩
La gran quantità di dati analizzati richiede un piccolo sforzo per
poterli interpretare al meglio.
Utilità ★★★★✩
Strumento fondamentale per chi desidera migliorare le prestazioni.
Qualità prodotto ★★★★✩
Affidabile, nei test sul prodotto eseguiti non ha mai mostrato particolari problemi.
Qualità documentazione ★★★✩✩
Presente help integrato con Visual Studio.NET molto esaustivo
(in lingua inglese).