Sei sulla pagina 1di 68

ED I T O R I A L E online.infomedia.

it
n. 64 - luglio/agosto 2005
bimestrale - anno undicesimo

Direttore Responsabile
Marialetizia Mari (mmari@infomedia.it)
Direttore Esecutivo

Libri in versione Francesco Balena (fbalena@infomedia.it)


Managing Editor

upgrade
Renzo Boni (rboni@infomedia.it)
Collaboratori
Andrea Benedetti
Filippo Bonanni
Stefano Corti

Q ualche giorno fa ho partecipato, insieme al mio co-autore


Giuseppe Dimauro, alla presentazione del nostro libro
Microsoft .NET Framework: Regole di stile e best practice,
in una libreria di Milano. L’incontro è stato particolarmente in-
teressante perchè ha messo a confronto due autori, un editore
Andrea Ferendeles
Davide Mauri
Lorenzo Vandoni
Vito Vessia

(Mondadori), e alcuni lettori che avevano idee abbastanza chiare


su quello che un libro “tecnico” deve offrire.

Una delle proposte più largamente condivise è che un libro dovreb-


be avere una vita parallela al software – in particolare, il software
a cui il libro fa riferimento – e in particolare le edizioni successive del libro dovrebbero
essere fornite sotto forma di upgrade, a prezzi contenuti. Ad esempio il lettore potrebbe
Direzione
portare la sua copia presso la libreria e ritirare la nuova versione a un prezzo scontato, Natale Fino (nfino@infomedia.it)
un po’ come si fa con la rottamazione degli autoveicoli. È un’idea molto interessante, ma
purtroppo ci sono un paio di impedimenti che ne ostacolano l’attuazione.
Marketing & Advertising
Segreteria: 0587/736460
marketing@infomedia.it
Il primo problema è che produrre un libro costa tantissimo, in termini di tempo e de-
Amministrazione
naro. Esistono le spese del materiale (la carta), di stampa, di rilegatura, di spedizione, e Sara Mattei
di magazzino. In Italia si aggiunge spesso il compenso del traduttore, che non è affatto (amministrazione@infomedia.it)
trascurabile. L’idea di offrire la seconda edizione di un libro a prezzi da upgrade si scontra Grafica
con tutte queste spese fisse, a cui occorre sommare le spese necessarie per mettere su la Manola Greco (mgreco@infomedia.it)
macchina organizzativa che ritira i libri usati per poi mandarli al macero. Alla fine della
Technical Book
fiera, lo sconto che si potrebbe offrire sarebbe talmente piccolo – nell’ordine del 10-15
Lisa Vanni (book@infomedia.it)
percento – da non fare una differenza sostanziale per le tasche dei lettori.
Segreteria
Enrica Nassi
Una buona parte di queste spese si potrebbero facilmente superare offrendo la nuova (info@infomedia.it)
versione del libro sotto forma di e-book da distribuire via Internet. Questo meccanismo
risolverebbe alcuni problemi ma ne creerebbe altri. In particolare, la storia della musica
in formato MP3 e degli strumenti peer-to-peer insegna che basta che u no solo dei lettori Stampa
metta il proprio e-book a disposizione sulla rete per vanificare quasi completamente i TIPOLITOGRAFIA PETRUZZI
Citta’ di Castello (PG)
legittimi guadagni dell’autore e dell’editore. Io parlo per esperienza personale: una volta
l’e-book del mio libroProgrammare Microsoft Visual Basic 6 finì su un sito Web italiano
Ufficio Abbonamenti
e fu scaricato da quasi 10.000 persone in meno di due settimane! Se si fosse trattato di un Tel. 0587/736460 - Fax 0587/732232
sito in lingua inglese probabilmente il numero sarebbe stato dieci volte maggiore. e-mail: abbonamenti@infomedia.it
www.infomedia.it
In conclusione, occorre sicuramente trovare un meccanismo che “premi” i lettori che Gruppo Editoriale Infomedia srl
decidono di acquistare le edizioni successive di un libro; per il momento, tuttavia, non Via Valdera P., 116 - 56038 Ponsacco (PI) Italia
esiste una soluzione che soddisfi tutti gli “attori” coinvolti. Se qualcuno ha qualche idea Tel. 0587/736460 - Fax 0587/732232
red_vbj@infomedia.it
in proposito, mi può scrivere via email o tramite il mio blog personale. Sito Web www.infomedia.it

Manoscritti e foto originali anche se non pubblicati,


Francesco Balena non si restituiscono. È vietata la riproduzione
anche parziale di testi e immagini.
fbalena@dotnet2themax.it
www.dotnet2themax.it/blog Si prega di inviare i comunicati stampa e gli inviti stampa per
la redazione all’indirizzo: comunicatistampa@infomedia.it
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
4 VBJ N. 64 - Luglio/Agosto 2005
I N O F F E R TA V B J 6 4
Scrivi a
Maximizing ASP.NET: Mastering
Real World, book@infomedia.it Visual Studio .NET
Object-Oriented specificando di I. Griffiths et al.
Development
di J. Putz nell’oggetto della
Addison Wesley
e-mail: O’ Reilly
ISBN 0321294475
336 pp - 46,95 €
IN OFFERTA ISBN 0596003609
416 pp - 39,95 €
VBJ n. 64
OPPURE
inviaci il coupon
Professional DotNetNuke Head First
ASP.NET Portals sottostante Design Patterns
di S. Walker et al. al numero di fax di Eric Freeman et al.

0587/732232
John Wiley Potrai acquistare O’Reilly
ISBN 0764595636 ISBN 0596007124
456 pp - 45,80 €
i libri qui riportati con 688 pp - 44,95 €
uno
SCONTO
Design Patterns
ECCEZIONALE C# Essentials
del 10% anche se
di E. Gamma - R. Helm - di B. Albahari et al.
R. Johnson - J. Vlissides
acquisti solo un
libro
Addison Wesley Italia O’ Reilly
ISBN 887192150X OPPURE ISBN 0596003153
414 pp - 39,00 € 216 pp - 24,95 €
del 20% se acquisti
3 libri

VBJ 64
SOMMARIO
L U G L I O / A G O S T O

N.64
SPECIALE

Performance con GAC e NGEN in ASP.NET 8


Perchè il primo avvio di una applicazione ASP.NET impiega più tempo? Cosa accade dietro le quinte?
Ecco le risposte che cercate ed i test che avreste sempre voluto fare.
di Andrea Ferendeles

VB.NET
Le novità VB nel nuovo Visual Studio 19
La nuova versione di Visual Studio porta con sé diverse interessanti novità relative al linguaggio Visual Basic.
di Lorenzo Vandoni

Un motore di scripting per l’ambiente .NET 22


Realizziamo un programma in grado di compilare ed eseguire al volo file di testo contenenti codice VB.NET.
di Filippo Bonanni

DATABASE
SQL Server 2005 ed il CLR: un matrimonio annunciato 29
SQL Server 2005 rappresenta una grande evoluzione nella gestione di database. Vediamo come l’integrazione con il fra-
mework .NET possa migliorare la vita degli sviluppatori e dei DBA.
di Andrea Benedetti

SOFTWARE ENGINEERING
I design pattern più famosi implementati in VB.NET (terza puntata) 39
Il pattern Factory Method consente di delegare a una sottoclasse la scelta di quale oggetto istanziare.
di Lorenzo Vandoni

6 VBJ N. 64 - Luglio/Agosto 2005


RUBRICHE

Editoriale 4
.NET Tools 61
a cura di Davide Mauri

TECNICHE
Verificare la disponibilità di un control OCX con VB6 42
A volte è necessario che un programma si comporti in modo diverso a seconda della disponibilità di un determinato control OCX.
di Lorenzo Vandoni

APPLICATIVI
Controllo Remoto in Visual Basic .NET (prima puntata) 44
di Stefano Corti

I MITI
XML Class Generator 52
Come utilizzare il potente motore di scripting di Windows per produrre ed istanziare classi COM completamente costruite
a runtime, per usufruire di tante interessanti potenzialità.
di Vito Vessia

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.

N. 64 - Luglio/Agosto 2005 VBJ 7


GAC

Performance con GAC


e NGEN in ASP.NET
Perchè il primo avvio di una applicazione ASP.NET impiega più tempo?
Cosa accade dietro le quinte?
Ecco le risposte che cercate ed i test che avreste sempre voluto fare.

Performance
di Andrea Ferendeles

I
l tema delle performance in un’applicazione Un aspetto che mi è rima-
ASP.NET è sempre stato un tema caldo, non tan- sto sempre oscuro e/o difficile
to per un’effettiva mancanza di prestazioni da par- da digerire e da capire è il “pri-
te del .NET Framework, quanto invece per la ric- mo avvio” di una applicazione
chezza dei tool messi a disposizione nell’SDK del ASP.NET.
framework stesso. In ufficio ho provato per gioco a
In particolar modo il tool ngen.exe, presen- fare una serie di interviste ai miei
te nel SDK, è stato sempre oggetto di dispu- colleghi di lavoro, ascoltando le
te e discussioni a me personalmente mai trop- loro reazioni al “primo avvio” di
po chiare e soprattutto di difficile verificabilità. una applicazione ASP.NET.
Vi siete mai chiesti perché il “primo avvio” di una ap- Ecco alcune reazioni: “lento”,
plicazione ASP.NET impiega più tempo dei successi- “lentissimo”, “ma che sta facen-
vi? Cosa succede dietro le quinte? do …”, “sul mio PC non è così
Scopo di questo articolo è quello di indagare, at- lento” (sviluppatore junior), “ba-
traverso semplici test ed osservazioni, nelle directory sta … vado a casa” (aficiona-
Temporary ASP.NET Files, con un’attenzione parti- do ASP).
colare ai tempi di risposta ed alla concomitanza di Quindi ho chiesto loro cosa si
altri fattori quali la GAC (Global Assembly Cache). poteva fare per migliorare i tempi
di risposta del primo avvio (stes-
Premessa so ordine di cui sopra):
“niente”, “assolutamente nien-
Quando nell’ormai lontano 2002 fu rilasciato per la te”, “proviamo sul mio PC”, “fac-
prima volta ASP.NET, una delle feature che colpì tut- ciamola in ASP … però a casa
ti noi, poveri sviluppatori ASP ☺, fu proprio la capa- mia”. Ovviamente sto scherzan-
cità di una applicazione ASP.NET di essere compila- do, però una cosa ho deciso di
ta, anziché interpretata da IIS. Col passare del tem- farla; mi sono rimboccato le ma-
po ci siamo abituati a questa feature e ci siamo spin- niche (alla Peter Norton), mi sono
ti sempre più alla ricerca delle migliori performance armato di “cacciavite” e di “cro-
possibili in applicazioni Web. nometro” ed ho deciso, in prima
persona, di testare e capire il “pri-
mo avvio”: i risultati sono stupe-
facenti!
Andrea Ferendeles ha iniziato ad appassionarsi di informatica
dai tempi del VIC20. Ha sviluppato in BASIC, Logo, Turbo
Pascal, C, C++, Visual Basic, fino ad arrivare ai linguaggi
GAC
.NET, quali VB.NET e C#. È certificato MCP, MCAD, MSF,
MCSD (.NET), MCDBA, MCT. Può essere contattato via email: Facciamo una breve pre-
aferendeles@infomedia.it. messa su GAC, ngen.exe e

8 VBJ N. 64 - Luglio/Agosto 2005


GAC

sui tempi di risposta del “primo


avvio” in ASP.NET.
Questi metodi ci consentono, di
fatto, di evitare una duplicazione
inutile oltrechè dannosa alle fasi
di debbugging, testing e deploy-
ment, che viceversa, con il mo-
dello side-by-side di .NET non
sarebbe possibile evitare.
Il requisito per registrare un as-
sembly in GAC è uno: rendere
l’assembly Strong Named, cioè
dotarlo di nome (ovvio), versione,
culture e di una coppia di chiavi
pubblica-privata generabile con
il tool sn.exe passandogli il pa-
rametro –k. Fatto ciò possiamo
Figura 1 Come appare la struttura di C:\WINDOWS\Assembly dal o utilizzare l’altro tool dell’SDK
prompt MS-DOS
(gacutil.exe con il parametro – i)
oppure (evviva Microsoft) fare
drag&drop nella directory della
la directory dai mille misteri C:\WINDOWS\ GAC (in genere c:\windows\assembly). Ecco un
Microsoft.NET\Framework\v1.1.4322\ esempio di generazione di una coppia di chiavi
Temporary ASP.NET Files. La Global Assem- pubblica-privata con il tool sn.exe
bly Cache sembra essere il repository di tutti
quegli assembly .NET “condivisi” che rivesto- Prompt MS-DOS di Visual Studio .NET
no il ruolo di estensori del .NET Framework. C:\>sn.exe –k mykeys.snk
In realtà per condividere un assembly sarebbe
sufficiente, anzi auspicabile, utilizzare un file di Ecco invece una porzione del file AssemblyInfo
configurazione con all’interno il tag <codeBase per indicare al compilatore la nostra intenzione di
version=”1.0.0.0” href=”file://C:\myabsolutepath\ rendere l’assembly in questione Strong Named.
MyAssembly.dll”/>.
Possiamo però, con una forzatura, registrare in C#
GAC anche assembly di business logic, solo per [assembly: AssemblyVersion(“1.0.0.0”)]
fare piccoli test ed osservare eventuali benefici [assembly: AssemblyKeyFile(“c:\\mykeys.snk”)]

Riquadro 1 Il meccanismo di compilazione può essere modificato attraverso il tag <compilation>

<!-- compilation Attributes:


tempDirectory=”directory”
debug=”[true|false]” // Default: false
strict=”[true|false]” // Default: false
explicit=”[true|false]” // Default: false
batch=”[true|false]” // Default: true
batchTimeout=”timeout in seconds” // Default: 15 seconds
maxBatchSize=”max number of pages per batched compilation” // Default: 1000 classes
maxBatchGeneratedFileSize=”max combined size (in KB) of the generated source files per batched compilation” // Default: 3000KB
numRecompilesBeforeAppRestart=”max number of recompilations before appdomain is cycled” // Default: 15 recomplations
defaultLanguage=”name of a language as specified in a <compiler/> tag below” // Default: VB
-->
<compilation debug=”false” explicit=”true” defaultLanguage=”vb”>

N. 64 - Luglio/Agosto 2005 VBJ 9


GAC

Quando la culture non è indicata è per default sembly che risiedono in GAC, in quanto que-
neutral. sti sono Strong Named e dunque già univoca-
mente identificabili. Inoltre gli assembly in GAC,
Per finire ecco invece come registrare il nostro a fronte di una chiamata, non devono essere
assembly – supponiamo MyAssembly.dll – nel- portati, cioè copiati, nella directory dell’assem-
la GAC attraverso il tool gacutil.exe sempre del bly chiamante ma possono restare tranquilla-
.NET Framework SDK. mente lì dove sono; sono appunto condivisi.
Quest’ultimo punto sarà proprio uno degli ele-
Prompt MS-DOS di Visual Studio .NET menti chiave, che rendono la GAC ed il mec-
C:\>gacutil.exe /i MyAssembly.dll canismo di condivisione degli assembly tramite
codebase, due meccanismi privilegiati dal pun-
Ovviamente abbiamo detto che la registra- to di vista delle performance di start-up. Per il
zione in GAC può essere effettuata più sem- momento fermiamoci qui e passiamo ad ana-
plicemente facendo drag&drop dell’assembly lizzare un altro tool fondamentale per le nostre
MyAssembly.dll nella directory c:\windows\ prove: ngen.exe.
assembly.
Osserviamo inoltre che dalla esplorazione (tra- NGEN
mite explorer.exe di Windows) della directory
c:\windows\assembly ci si accorge che questa Dato che nel .NET framework è previsto que-
non sembra essere una vera e propria “folder” sto meccanismo di compilazione Just-In-Time,
del file system che contiene file (provate a fare si è pensato bene di inserire nell’SDK una utility
click con il tasto destro del mouse su un file di pre-compilazione in codice nativo allo scopo
qualunque, non ci sono i classici comandi co- di migliore le performance di “primo avvio” (le
pia, taglia, incolla, ecc… del menu contestuale). richieste successive abbiamo detto infatti “pe-
Infatti quando installiamo il .NET Framework vie- scherebbero” dalla RAM).
ne applicata una patch ad explorer.exe per dare Il tool in questione si chiama ngen.exe; vedia-
un nuovo aspetto alla cartella Assembly ed at- mo un esempio di generazione di immagine na-
tribuire un diverso significato al drag&drop qui tiva, sempre con il nostro assembly esemplifi-
operato (registrazione appunto e non copia ed cativo MyAssembly.dll.
incolla). Proviamo ora invece a curiosare con il
prompt MS-DOS proprio nella cartella in que- Prompt MS-DOS di Visual Studio .NET
stione. Come si vede dalla Figura 1 in realtà C:\>ngen.exe MyAssembly.dll
la struttura del file system per questa cartella
è nascosta e ben diversa.
Proprio nella sotto-directory
GAC troveremo diverse altre
cartelle – una per ogni assem-
bly presente in GAC – ed alla fine
della struttura, una copia origina-
le in IL (Intermediate Language
generato da uno dei compilato-
ri .NET) dell’assembly registrato
in GAC. Quando da una applica-
zione ASP.NET, il code-behind ri-
chiede i servizi di un assembly,
questo deve essere innanzitutto
individuato e solo dopo una se-
rie di procedimenti che portano
alla sua individuazione univoca,
esso viene compilato just-in-time
e quindi portato in RAM. Questo
meccanismo di identificazione è Figura 2 Alcuni file sono identificati come "Native Images"
estremamente più facile e dun-
que più veloce per tutti gli as-

10 VBJ N. 64 - Luglio/Agosto 2005


GAC

Il tool ngen.exe non richiede che l’assembly


debba essere per forza Strong Named, moti- Listato 1 L'applicazione per effettuare i test

vo per il quale sotto la directory c:\windows\


assembly (vedi Figura 1) ci sono due folder
distinte: GAC e NativeImages1_xxxxx. private void Page_Load(object sender, System.EventArgs e)
{
StreamWriter sw=new StreamWriter(“c:\\times.txt”,true);
Gli stessi progettisti del .NET Framework
DateTime startdate=new DateTime(1900,1,1,0,0,0,0);
hanno allora, loro stessi, applicato questo DateTime enddate=DateTime.Now;
concetto ad alcuni assembly (vedremo più TimeSpan diff=(TimeSpan)enddate.Subtract(startdate);
avanti perché solo alcuni e non tutti) del .NET sw.Write(“{0};”,diff.TotalMilliseconds.ToString());
Framework ed in particolare a quelli mostra- this.TextBox1.Text=new Generate.Class100().Method();
ti in Figura 2.
Guardate dove nella colonna Type compa- enddate=DateTime.Now;
re la dicitura Native Images. diff=(TimeSpan)enddate.Subtract(startdate);
sw.WriteLine(“{0}”,diff.TotalMilliseconds.ToString());
sw.Close();
La soluzione sembra arrivare: compiliamo }
tutto in nativo e le performance saranno as-
sicurate. Prima di tirare conclusioni affretta-
te proviamo a porci tre domande: a generare tutta una serie di folder ed assem-
bly sotto la cartella di sistema, di solito deno-
• perché NON tutti gli Assembly del .NET minata C:\WINDOWS\Microsoft.NET\Framework\
Framework hanno un’immagine nativa? v1.1.4322\Temporary ASP.NET Files.
(ad es. System.Web.dll, System.Data.dll, Ora, possiamo sicuramente affermare che tale
ecc…) processo porterà via senz’altro alcuni preziosi
• l’utility ngen.exe potrà a native-compile-time millisecondi al nostro “primo avvio”; vedremo in
applicare le stesse ottimizzazioni che il JIT seguito se e come evitare in qualche modo que-
compirà a JIT-compile-time? sto processo e guadagnare quindi millisecondi
• come si comportano le immagini native nel o addirittura secondi utili. Scriviamo una mini
caso di più di un Application Domain (caso applicazione ASP.NET (Listato 1) composta da
di ASP.NET)? un’unica Web Form (WebForm1.aspx) con all’in-
terno un controllo TextBox chiamato TextBox1 e
Obiettivi e scenari di riferimento con il seguente codice in risposta all’evento Page
Load della Web Form stessa. Come si vede, al
Vado allora ad illustrarvi quelli che saranno gli caricamento della WebForm andremo a creare
obiettivi dei test che seguiranno. Lo scenario nella root del nostro disco (ricordatevi di dare i
di riferimento è ASP.NET del .NET Framework permessi necessari all’applicazione o cambiate
1.1 ma le stesse considerazioni possono esse- application pool) un semplice file di testo deno-
re applicate con alcuni correttivi anche ad altre minato times.txt, con dentro due valori numeri-
tipologie di applicazioni, tipo Smart Client, Win- ci, la cui differenza algebrica fornirà il numero
dows Services e così via. di millisecondi necessari a eseguire il metodo
Ricordiamo che l’obiettivo principe è capire ed Method() di una istanza della classe Class100.
ottimizzare il “primo avvio” di una applicazione N.B.: il 1 gennaio 1900 ore 00:00:00 è un qual-
ASP.NET senza far ovviamente decadere le per- siasi DateTime noto e di riferimento.
formance nelle successive richieste. Innanzi tutto Vediamo nel Listato 2 la definizione della classe
vi rimando ad un articolo molto ben fatto di Dino Class100. Il metodo Method() si limiterà a resti-
Esposito - vedi bibliografia - che spiega tra le tuire il risultato di un’altro metodo chiamato an-
altre cose, il meccanismo che ASP.NET utilizza ch’esso Method() ma di una istanza della clas-
per “fondere”, durante la fase di pre-compilazio- se Class99 che creerà on-the-fly.
ne, il codice di scripting lato server nelle pagine Come avrete già intuito ripeteremo questo mec-
.aspx, .asmx, .ascx ecc., con il code-behind che canismo, scrivendo in tutto 100 classi dove la
risiede invece nell’assembly della nostra appli- classe 100 invoca la 99, la 99 invoca la 98, e
cazione ASP.NET (cartella bin). Mi limiterò sem- così via fino ad arrivare all’ultima invocazione
plicemente a dire che tale meccanismo porta di un metodo Method() di una classe Class1,
il .NET Framework (ed ASP.NET in particolare) la cui definizione è riportata nel Listato 3.

N. 64 - Luglio/Agosto 2005 VBJ 11


GAC

Ovviamente questo meccanismo non rispecchia Supponendo di aver compilato tutte e 100
assolutamente la realtà e non vuole essere nem- le classi e di aver aggiunto un riferimento al-
meno la base di partenza per un benchmark; l’assembly Class100.dll nella nostra applica-
vuole invece portare all’esasperazione i mecca- zione ASP.NET, che d’ora in avanti chiame-
nismi di JITting e pre-compilazione nella Tempo- remo per comodità WebGenerate, vediamo i
rary ASP.NET Files proprio per rendere evidenti differenti scenari in cui fare i test e per i quali
le differenze nei tempi di risposta e consentirne sarà interessante osservare in termini di tem-
dunque una facile valutazione. po quanti millisecondi trascorrono prima di
Tutte e 100 le classi sono dotate dell’attribu- vedere la stringa “something” apparire nella
to AssemblyKeyFile in modo da rendere tutti e nostra TextBox.
100 gli assembly generati (uno per ogni classe,
Class1.dll, Class2.dll, … Class100.dll) Strong Na- Scenario 1 - “No Gac, No Native Images”
med e pronti dunque ad essere registrati all’oc- Nel primo scenario non metteremo nulla in
correnza dei nostri test, in GAC. Inoltre, tutte le GAC e non genereremo alcuna immagine nati-
chiamate si riducono alla fine alla restituzione di va. Insomma la situazione tipica di una applica-
una stringa “something” (che vedremo apparire zione ASP.NET con esecuzione side-by-side.
sul controllo textbox della nostra WebForm1.aspx Effettueremo in questo scenario, così come
al termine del caricamento della pagina stessa. nei successivi, un totale di tre 3 prove.
Nel codice allegato all’articolo troverete una uti-
lity per generare il sorgente di queste classi, per Scenario 1 - Prova 1
generare una serie di file batch (.bat) per com- “First Execution, No Gac, No Native Ima-
pilarle, per pubblicare/rimuoverle dalla GAC e ges, No Temporary ASP.NET Files”
per creare/eliminare le relative immagini native Per questa prima prova, ci dovremo as-
con ngen.exe. sicurare che la directory C:\WINDOWS\
La serie di chiamate in cascata alle 100 classi Microsoft.NET\Framework\v1.1.4322\
ci assicurerà, per quanto concerne i nostri test, Temporary ASP.NET Files sia vuota (elimi-
che il JIT “attraversi” tutte le classi e quindi tutti natene tranquillamente tutto il contenuto ma
gli assembly, senza escluderne nemmeno una e NON la cartella stessa e solo dopo aver riav-
ci darà modo di rendere “evidenti”, in termini di viato IIS – iisreset.exe) quindi riavviamo il ser-
tempo trascorso, le differenti strategie adottate vizio IIS, proprio per simulare il nostro ormai
nei diversi scenari. famigerato “primo avvio”. Se ora tentassimo
di accedere tramite browser alla
nostra WebForm1.aspx avrem-
mo un risultato logicamente non
corretto, in quanto il riavvio di
IIS non rimette automatica-
mente in esecuzione il proces-
so w3wp.exe.
Teniamo dunque in considera-
zione questo aspetto e superia-
molo con un piccolo stratagem-
ma. Creiamo un’ulteriore e ba-
nale applicazione ASP.NET che
faccia da “gateway” al solo sco-
po di scopo di assicurare che
i processi w3wp.exe e aspnet_
wp.exe siano già in esecuzio-
ne quando approderemo alla
WebForm1.aspx dell’applica-
zione WebGenerate.
Visto che ci siamo faremo in
Figura 3 Le 101 cartelle generate dal codice modo che questa ulteriore ap-
plicazione ASP.NET, che d’ora in
avanti chiameremo PreWebGe-

12 VBJ N. 64 - Luglio/Agosto 2005


GAC

nerate, “ci porti” lei stessa e attraverso una


Response.Redirect(…), alla WebForm1.aspx Listato 2 Definizione della Class 100

della applicazione WebGenerate.


PreWebGenerate avrà anch’essa un’unica
Web Form, denominata WebForm1.aspx, con using System;
all’interno un controllo link button, (LinkBut- [assembly: System.Reflection.AssemblyKeyFile(“mykeys.snk”)]
ton1) con il codice (vedi Listato 4) in rispo-
namespace Generate
sta all’evento di click. {
Questa funzione scriverà in verità per prima public class Class100
il file times.txt, inserendo come nel caso pre- {
public Class100() { }
cedente i millisecondi intercorsi tra la mezza- public string Method()
notte del 1 gen 1900 e la data ed ora attua- {
le, e facendo una semplice sottrazione con il return new Class99().Method();
valore scritto dall’applicazione WebGenera- }
}
te otterremo i millisecondi impiegati dal .NET }
Framework per la “preparazione” all’esecu-
zione dell’applicazione WebGenerate stessa e
cioè la creazione dei file necessari nella car-
tella Temporary ASP.NET Files, esclusi i tempi di Prima di procedere oltre, verificate che il file
esecuzione dei metodi Method() delle 100 clas- times.txt esista nella root del disco C (ricordate-
si. Apriamo dunque il browser e puntiamo all’in- vi i permessi) e che effettivamente contenga i 3
dirizzo web: http://localhost/PreWebGenerate/ valori di cui sopra. Verificate inoltre che la car-
WebForm1.aspx tella Temporary ASP.NET Files contenga nelle
Ora facendo click sul controllo LinkButton1 in- sue sottodirectory, 101 sotto cartelle.
vocheremo l’applicazione WebGenerate che fi- Per trovare le 101 cartelle dovrete entrare nel-
nalmente compilerà tramite JIT prima l’assem- la directory WebGenerate e quindi in ulterio-
bly di code-behind (WebGenerate.dll) e quindi ri 2 cartelle (nei miei test il percorso comple-
a cascata gli assembly Class100.dll, Class99.dll to è C:\WINDOWS\Microsoft.NET\Framework\
… fino all’ultimo assembly Class1.dll, dando vita v1.1.4322\Temporary ASP.NET Files\
alla comparsa della stringa “something” nel webgenerate\65eab3c7\fad2b067\assembly\
browser; se farete voi stessi la prova tenetevi dl2) come visualizzato in Figura 3; il percor-
dentro la sensazione del tempo trascorso. so esatto differirà da macchina a macchina, in
Nel file c:\times.txt troveremo, al termine dei quanto ASP.NET utilizza un meccanismo di no-
test, 3 valori numerici separati dal caratte- menclatura basato su algoritmo di Hash e sulla
re punto e virgola (;) e nel seguente formato: chiave pubblica dell’assembly stesso.
valore1;valore2;valore3.
Se chiamiamo diff1 e diff2 rispettivamente
valore2-valore1 e valore3-valore2, possiamo Listato 3 Alla fine delle iterazioni viene eseguita la
Class 1
affermare che:

• diff1 conterrà il tempo totale, espresso in using System;


millisecondi, necessario alla creazione del- [assembly: System.Reflection.AssemblyKeyFile(“mykeys.snk”)]
le cartelle e dei file temporanei nella folder namespace Generate
{
Temporary ASP.NET Files più il tempo, sem- public class Class1
pre espresso in millisecondi, necessario al {
JIT per compilare tutti gli assembly coinvol- public Class1()
ti dall’applicazione WebGenerate (101 as- {
}
sembly).
• diff2 conterrà invece i millisecondi ne- public string Method()
cessari alla esecuzione di tutto il codice {
in questione e dunque dei tempi di ese- return “something”;
}
cuzione dei diversi metodi Method() del- }
le 100 classi più il codice presente nel }
code-behind.

N. 64 - Luglio/Agosto 2005 VBJ 13


GAC

Scenario 1 - Prova 2 le ha rigenerate tutte e comunque! Questo


“Second Execution, No Gac, No Native Ima- meccanismo di compilazione può essere al-
ges, Temporary ASP.NET Files (101 Classes)” terato, per modalità e quantità, attraverso il
Questa prova prevede semplicemente la tag <compilation> presente nel file di configu-
chiusura e la riapertura del browser sulla pagi- razione machine.config del .NET framework,
na WebForm1.aspx dell’applicazione PreWeb- riportato nel Riquadro 1.
Generate, quindi facciamo click e attendiamo I valori di default, come si vede, sono
nuovamente la scritta “something”. 3.000 pagine .aspx oppure 3.000KB.
Questo test dovrebbe simulare successive Dato che è raro trovare in un’applicazio-
richieste di utenti differenti (nuove sessioni) ne ASP.NET più di 3.000 pagine ovve-
laddove l’applicazione non abbia subito mo- ro pagine la cui dimensione superi in to-
difiche e laddove IIS non sia stato mai riav- tale i 3.000KB, la nostra applicazione su-
viato (ad esempio reboot della macchina o birà nella maggior parte dei casi una
iisreset.exe). compilazione completa e indiscriminata.
Modificate all’occorrenza questi valori per ot-
Scenario 1 - Prova 3 tenere i due possibili ed opposti comporta-
“First Execution, No Gac, No Native Images, menti; aumentati questi valori se l’applicazio-
Temporary ASP.NET Files (101 Classes)” ne è ad esempio composta da più di 3.000
Questa terza ed ultima prova di questo sce- pagine, assicurandovi dunque una compila-
nario prevede solo il riavvio di IIS (iisreset.exe) zione completa in start-up.
senza alterare il contenuto della directory Perderemo qualche istante in più all’inizio,
Temporary ASP.NET Files, dove sono già beneficiando però in seguito di codice già
presenti i file .xml, .cs, .dll, ecc. contenuti compilato.
nelle varie sotto cartelle temporanee. Oppure riduciamo all’osso questi valori, di-
Questo è una variante della Prova 2 e si ve- stribuendo così nel tempo il carico di com-
rifica ad esempio quando IIS viene riavviato o pilazione.
quando la macchina viene fatta ripartire ma
non si è modificato il contenuto della directory Scenario 2 - “Gac, No Native Images”
Temporary ASP.NET Files e l’applicazione Analogamente allo Scenario 1 condurremo
non ha subito ovviamente modifiche al codi- anche qui le stesse tre prove ma stavolta re-
ce e/o non è stata mai ricompilata. gistrando in GAC tutti e 100 gli assembly
Al termine della prova entrate nella directory (Class100.dll, Class99.dll, … Class1.dll).
Temporary ASP.NET Files e osservate data Questa scenario prende spunto dall’osser-
ed ora di modifica delle .dll. vazione che tutto quello che sta in GAC o
Vi accorgerete che il .NET Framework (ovve- eventualmente in directory condivise tra-
ro ASP.NET), a prescindere dalla loro validità, mite codebase, non viene mai copiato nel-

Tabella 1 I tempi calcolati sul PC dell'autore

c:\times.txt

3324018372781,25;3324018383453,13;3324018383468,75

3324018575078,13;3324018575078,13;3324018575078,13

3324018661859,38;3324018677140,63;3324018677156,25

3324019487906,25;3324019488968,75;3324019489609,38

3324019663046,88;3324019663046,88;3324019663046,88

3324019748125,00;3324019748906,25;3324019749500,00

3324020183125,00;3324020184218,75;3324020185031,25

3324020275984,38;3324020275984,38;3324020275984,38

14 VBJ N. 64 - Luglio/Agosto 2005


GAC

la directory del chiamante.


Ovviamente questo procedi-
mento introduce operazioni
aggiuntive in deployment e
non sono consigliabili duran-
te la normale fase di test.

Scenario 2 - Prova 1
“First Execution, Gac, No
Native Images, No Tem-
porary ASP.NET Files (1
Class)”
Se utilizzate Visual Studio
.NET ricordatevi di registrare
in GAC gli assembly (nel codi-
ce allegato all’articolo c’è un
file batch GacAll.bat che lo fa
per voi) e quindi ricordatevi Figura 4 I risultati del test in forma grafica
di togliere e aggiungere nuo-
vamente il riferimento all’as-
sembly Class100.dll nell’ap-
plicazione ASP.NET WebGe- generando con ngen.exe le rispettive 100 im-
nerate. magini native.
Quindi ricompiliamo, facciamo ripartire IIS Questa scenario prende spunto dalle featu-
tramite iisreset.exe e svuotiamo l’intera di- res che il tool ngen.exe offre, ed ovvero, com-
rectory Temporary ASP.NET Files. pilare il codice nativo senza scomodare il JIT
Riapriamo il browser e puntiamo nuovamente a run-time.
all’indirizzo: http://localhost/PreWebGenerate/
WebForm1.aspx. Ora se andate a controllare Scenario 3 - Prova 1
la directory Temporary ASP.NET Files trovere- “First Execution, Gac, Native Images, No
te con grande stupore solo UNA (1) cartella, Temporary ASP.NET Files (1 Class)”
quella dell’assembly di code-behind. Se utilizzate Visual Studio.NET ricordatevi, la-
Quanto tempo avremo risparmiato? Le consi- sciando tutto in GAC, di generare le immagi-
derazioni sui tempi vediamole alla fine, intanto ni native dei 100 assembly e solo dopo di to-
terminiamo tutte le prove necessarie. gliere e re-inserire il riferimento all’assembly
Il file c:\times.txt viene scritto in append, Class100.dll nell’applicazione ASP.NET Web-
quindi prenderemo tutti i dati alla fine dei Generate.
test. In realtà questo passo è obsoleto in quanto,
per Visual Studio .NET è ininfluente l’esistenza
Scenario 2 - Prova 2 o meno di immagini native in fase di compila-
“Second Execution, Gac, No Native Ima- zione; se ne accorgerà solo il CLR - Common
ges, Temporary ASP.NET Files (1 Class)” Language Runtime - appunto a run-time.
Stesse modalità dello Scenario 1 – Prova 2. Nel codice allegato all’articolo c’è un file ba-
tch NativeAll.bat che lo fa per voi.
Scenario 2 - Prova 3 Quindi ricompiliamo, facciamo ripartire IIS
“First Execution, Gac, No Native Images, (iisreset.exe) e svuotiamo l’intera directory Tem-
Temporary ASP.NET Files (1 Class)” porary ASP.NET Files.
Stesse modalità dello Scenario 1 – Prova 3. Riapriamo il browser, puntiamo nuovamente
all’indirizzo: http://localhost/PreWebGenerate/
Scenario 3 - “Gac, Native Images” WebForm1.aspx e procediamo come già illu-
Analogamente allo Scenario 1 e 2 condurre- strato in precedenza. Ora se andate a control-
mo anche qui le stesse tre prove ma stavolta lare la directory Temporary ASP.NET Files trove-
non solo tenendo in GAC tutti e 100 gli assem- rete sempre e solo una (1) cartella, quella del-
bly (Class100.dll, Class99.dll, … Class1.dll) ma l’assembly di code-behind.

N. 64 - Luglio/Agosto 2005 VBJ 15


GAC

Ancora una volta le considerazioni sui tempi te- Conclusioni


niamocele per la fine … siamo quasi arrivati. Se avete avuto la pazienza di arrivare fin qui,
le conclusioni sono a questo punto estrema-
Scenario 3 - Prova 2 mente semplici ma allo stesso tempo estrema-
“Second Execution, Gac, Native Images,
Temporary ASP.NET Files (1 Class)”
Stesse modalità degli altri Scenari – Prova 2.
Vi siete mai chiesti perché
Scenario 3 - Prova 3
“First Execution, Gac, Native Images, Tem-
il “primo avvio” di una ap-
porary ASP.NET Files (1 Class)” plicazione ASP.NET impiega
Stesse modalità degli altri Scenari – Prova 3.
più tempo dei successivi?
Risultati

Raccogliamo ora il frutto delle nostre prove,


andando a prelevare con molta cura il conte- mente interessanti.
nuto del file c:\times.txt. Dalla Figura 4 e della Tabella 2 si evince che:
Ora potete rimuovere tutte le immagini native
e tutti gli assembly dalla GAC tramite due file • Lo Scenario 2, ovvero tutto in GAC e
batch che trovate nel materiale di corredo al- senza immagini native offre le perfor-
l’articolo: NoNativeAll.bat e NoGacAll.bat. mance migliori: 1,7 sec. circa al “pri-
Portiamo questi numeri sotto un foglio di mo avvio” contro i ben 10,7 sec. cir-
calcolo, facciamo le dovute sottrazioni per ca dello Scenario 1 (un’eternità).
ricavare i valori diff1 e diff2 delle 9 prove ef- Ovviamente il grosso del risparmio lo ab-
fettuate (3 per scenario), e facciamo le nostre biamo ottenuto grazie al fatto che nello
brave considerazioni sui tempi ottenuti. Scenario 2 abbiamo evitato al processo
Chiamiamo inoltre tot la somma tra diff1 e aspnet_wp.exe, in virtù dell’esecuzione
diff2, che sarà per noi il tempo totale tra ge- side-by-side (default), di “copiare” 100
nerazione, compilazione ed esecuzione in mil- assembly in più nella directory Tempo-
lisecondi, della nostra applicazione WebGe- rary ASP.NET Files e di doverne verifica-
nerate. re nuovamente l’integrità, verifica già ef-
In Tabella 1 trovate il contenuto del mio file fettuata in fase di registrazione.
c:\times.txt ed in Tabella 2 i valori diff1, diff2 Nel caso invece di una assembly condiviso tra-
e tot desunti dalle prove effettuate sulla mia mite codebase saremmo noi ad indicare tramite
macchina. il tag <assemblyIdentity name=”MyAssembly.dll”
publicKeyToken=”xxxxxxxxxxxxxx” … />, iden-
tià e posizione dell’assembly in questione.
In totale avremo solo 19 file in 7 car-
Listato 4 Il codice che risponde al click telle, relativi al solo assembly di code-
behind, anziché 220 fi le in 207 car-
telle come nel caso dello Scenario 1.
Confrontando il valore diff2 dello Sce-
private void LinkButton1_Click(object sender,
System.EventArgs e)
nario 2 con lo stesso dello Scena-
{ rio 1, osserviamo però che il tem-
StreamWriter sw=new StreamWriter(“c:\\ po di esecuzione necessario nel-
times.txt”,true); le successive richieste è aumentato.
DateTime startdate=new DateTime(1900,1,1,0,0,0,0);
DateTime enddate=DateTime.Now;
Questo fenomeno potrebbe essere dovu-
TimeSpan diff=(TimeSpan)enddate.Subtract(startdate); to ai meccanismi di individuazione che il
sw.Write(“{0};”,diff.TotalMilliseconds.ToString()); run-time utilizza per “pescare” un assem-
sw.Close(); bly dalla GAC.
Response.Redirect(“http://
localhost/WebGenerate/WebForm1.aspx”); • Lo Scenario 3 si comporta addi-
} rittura peggio dello Scenario 2 (ma …
come … ngen.exe?… si proprio lui).

16 VBJ N. 64 - Luglio/Agosto 2005


GAC

Tabella 1 I risultati del test sul PC dell'autore

Valori diff1, diff2 e tot a confronto

(a) (b) (a+b) Note

diff1 diff2 tot

Scenario 1 No Gac, No Native Images

Prova 1 10.671,88 15,62 10.687,50 First Execution, No Temporary ASP.NET Files


Second Execution, Temporary ASP.NET Files
Prova 2 0,00 0,00 0,00
(101 Classes)
First Execution, Temporary ASP.NET Files
Prova 3 15.281,25 15,62 15.296,87
(101 Classes)
Scenario 2 Gac, No Native Images

First Execution, No Temporary ASP.NET Files


Prova 1 1.062,50 640,63 1.703,13
(1 Class)
Second Execution, Temporary ASP.NET Files
Prova 2 0,00 0,00 0,00
(1 Class)
First Execution, Temporary ASP.NET Files (1
Prova 3 781,25 593,75 1.375,00
Class)
Scenario 3 Gac, Native Images

First Execution, No Temporary ASP.NET Files


Prova 1 1.093,75 812,50 1.906,25
(1 Class)
Second Execution, Temporary ASP.NET Files
Prova 2 0,00 0,00 0,00
(1 Class)
First Execution, Temporary ASP.NET Files (1
Prova 3 750,00 781,25 1.531,25
Class)

Questo dimostra come vere le perples- Ma perché vengono copiati nella directory
sità prima ipotizzate sull’utility ngen.exe. Temporary ASP.NET Files\WebGenerate\
Il JIT compila ed applica delle ottimizzazio- ...? Non potrebbero restare lì dove sono?
ni di compilazione a JIT-compile-time, che Questo comportamento è dovuto al fatto che
ngen.exe, a native-compile-time, non può l’assembly di code-behind della nostra appli-
nemmeno lontanamente immaginare di fare. cazione WebGenerate verrà preso in carico
Inoltre è noto che le immagini nati- dal processo aspnet_wp.exe nella directory
ve restano confinate nel loro Applica- Temporary ASP.NET Files\WebGenerate\...
tion Domain e quindi comunque non po- e non nella directory \bin, meccanismo ne-
trebbero essere utilizzate da ASP.NET cessario alla “fusione” tra codice di scrip-
che è invece Cross Application Domain. ting lato server e code-behind. Questa fu-
Notate che la differenza tra Scenario 3 e Sce- sione non può essere attuata nella cartel-
nario 2, in tutte e tre le prove è di circa 0,2 la \bin (non ci piace che la nostra cartel-
secondi quindi abbastanza trascurabile; ma la venga “sporcata”), occorre invece una
perché perderli? E soprattutto perché dover cartella temporanea: la directory Tempo-
rigenerare a manina le immagini native a fron- rary ASP.NET Files\WebGenerate appunto.
te di ricompilazione degli assembly? Questo è tra l’altro il meccanismo che ci
• Lo Scenario 1 segue in coda con le peggio- consente di ricompilare una applicazio-
ri performance di start-up in assoluto (ov- ne ASP.NET mentre essa è in esecuzio-
vero quello che tutti noi di solito usiamo). ne; l’assembly nella directory \bin è infatti
ASP.NET perde la maggior parte del tempo a sempre libero da ogni vincolo di ownership.
“copiare” i 100 assembly ed a creare tutta la Ora, visto che:
struttura su file system (vedi prova 3 dello sce-
nario 1), nella directory Temporary ASP.NET • quando un assembly a fa richiesta di un
Files\WebGenerate - 220 file in 207 cartelle. assembly b … b deve essere portato nel-

N. 64 - Luglio/Agosto 2005 VBJ 17


GAC

la stessa directory (o sotto directory) del- Tali conclusioni dovrebbero portarci anche a
l’assembly a (a meno di non usare un co- capire quale è stato il motivo per il quale, in
debase o la GAC). fase di installazione del .NET Framework, alcu-
• nel caso della nostra applicazione (Web- ni assembly siano stati registrati anche in for-
Generate), l’assembly è WebGenerate.dll mato nativo. La presenza di un assembly con
che ormai vive in Temporary ASP.NET immagine nativa interrompe il meccanismo di
Files\WebGenerate\.... JITting in cascata sugli assembly da questo re-
• L’assembly richiesto è, nel primo caso, ferenziati. Sarebbe infatti dispendioso per il JIT
Class100.dll, quindi questo viene copiato tutte le volte ricompilare a run-time ad esem-
anch’esso in una sotto directory dell’as- pio, l’assembly System.dll.
sembly richiedente (Temporary ASP.NET In conclusione non voglio impartire istruzio-
Files\WebGenerate\....). ni su cosa fare, ad esempio condividere tut-
• L’assembly Class100.dll fa poi richiesta to con codebase o pubblicare tutto in GAC o
a sua volta di Class99.dll … e in cascata non utilizzare mai ngen.exe. Spero soltanto di
vengono quindi copiati tutti in Temporary aver acceso una piccola luce sui meccanismi
ASP.NET Files\WebGenerate\.... che ASP.NET innesca dietro le quinte, quan-
do un’applicazione web parte la prima volta;
È proprio questa operazione di “copia” ed “in- lascio a voi ogni considerazione sul da farsi,
colla” che rende il nostro famigerato “primo caso per caso.
avvio” lento … lentissimo. Solo a partire dal .NET Framework 2.0, sarà
Successive richieste, fatte alla stessa appli- possibile finalmente e veramente pre-compila-
cazione, a patto di non aver alterato il conte- re le applicazioni ASP.NET e soprattutto creare
nuto della Temporary ASP.NET Files, saran- immagini native attraverso una nuova versione
no estremamente più performanti perché non del tool ngen.exe, che promette stavolta dav-
innescheranno più lo stesso meccanismo, in vero miracoli.
quanto tutto l’occorrente è già nelle directory Vi rimando, in bibliografia, ad un articolo mol-
in questione. to interessante di Reid Wilkes - MSDN Maga-
zine, aprile 2005 – che anticipa le caratteristi-
che salienti e più interessanti della nuova ver-
sione del tool ngen.exe presente nell’SDK del
L’obiettivo principe è .NET Framework 2.0.

capire ed ottimizzare il Bibliografia


“primo avvio” di una
[1] Jeffrey Richter – “JIT Compilation and Per-
applicazione ASP.NET formance - To NGen or Not to NGen ?”,
http://www.codeguru.com/Csharp/.NET/
net_general/toolsand3rdparty/article.php/
c4651
Altre considerazioni possono essere fatte su [2] Dino Esposito – “Programmare Micro-
un altro ipotetico scenario – direi quasi maso- soft Asp.Net”, Mondadori Informatica,
chistico – che mi sono guardato bene dal pre- Chapter 2 – Web Form Internals - http://
sentarvi ed ovvero: niente in GAC … ma tutto www.dotnet2themax.com/ShowContent.as
con immagini native! px?ID=750fdf64-9ff5-460f-85b2-908d7941
Se ci provate, otterrete performance ancora e0fe&Page=0&#RET
peggiori dello Scenario 1, per tutte le consi- [3] G. Di Mauro, F. Balena - “Practical Guide-
derazioni fatte fin ora e che quindi potete ben lines and Best Practices for Microsoft Visual
immaginare. Basic and Visual C# Developers”, Microsoft
Per dovere di cronaca vi dico inoltre che i tem- Press.
pi non subiscono variazioni di rilievo passan- [4] Reid Wilkes - “NGen Revs Up Your Per-
do dalla compilazione in modalità DEBUG, alla formance with Powerful New Features”,
compilazione in modalità RELEASE (i tempi che MSDN Magazine April 2005, http://
ho riportato in tabella si riferiscono alla compi- msdn.microsoft.com/msdnmag/issues/05/
lazione in modalità RELEASE). 04/NGen/default.aspx

18 VBJ N. 64 - Luglio/Agosto 2005


VB.NET

Le novità VB
nel nuovo
Visual Studio
La nuova versione di Visual Studio porta con sé diverse interessanti novità relative al
linguaggio Visual Basic

di Lorenzo Vandoni

N
el momento in cui scrivo è stata resa dispo- Public Class Lista(Of T)
nibile la seconda beta della nuova versione di Private moItem() As T
Visual Studio 2005. Molte, come spesso ca-
pita, sono le novità. In questo articolo cercheremo di Public Sub Add(ByVal oItem As T)
concentrarci sugli aspetti sintattici e semantici relati- ...
vi al linguaggio Visual Basic. End Sub
End Class
L’avvento dei generics
La parola chiave Of serve ad in-
La novità principale è costituita dalla possibilità di uti- dicare un parametro di tipo gene-
lizzare la programmazione generica. In pratica, diventa rico, in questo caso rappresenta-
possibile creare delle classi del tipo “lista di X”, dove to dal marcatore T. Per usare que-
X è un tipo qualsiasi, oppure delle funzioni di ordina- sta classe si potrà scrivere:
mento in grado di ordinare oggetti di classi diverse.
La programmazione generica permette di creare fun- Dim oLista1 As New Lista(Of Integer)
zioni e classi in grado di utilizzare oggetti di un gene- Dim oLista2 As New Lista(Of String)
rico tipo T, offrendo la possibilità di validarne la cor-
rettezza durante la compilazione. I vantaggi principali La dichiarazione di questi due
di questo approccio, già discusso in questa rubrica, oggetti provocherà due diverse
sono quelli di consentire una maggiore efficienza, e di istanziazioni della classe gene-
permettere un maggiore controllo del codice. Non sarà rica, in cui il marcatore T verrà
possibile, infatti, introdurre delle stringhe all’interno di rispettivamente sostituito dai tipi
una classe definita come lista di numeri interi. La de- Integer e String. In questo modo,
finizione di una classe generica in Visual Basic.NET una chiamata del tipo
può essere scritta in questo modo:
oLista1.Add(“abc”)

provocherà un errore di compi-


Lorenzo Vandoni è laureato in Informatica, ed è uno spe- lazione. È possibile inoltre impor-
cialista di progettazione e sviluppo con tecniche e linguaggi
re vincoli sul tipo generico T, spe-
object-oriented. Ha collaborato alla realizzazione di framework,
strumenti di sviluppo e software commerciali in C++, Java e cificando che debba implementare
Visual Basic. Può essere contattato tramite e-mail all’indirizzo una specifica interfaccia, o derivare
vandoni@infomedia.it. da una classe base, scrivendo:

N. 64 - Luglio/Agosto 2005 VBJ 19


VB.NET

Public Class GenericClass(Of T As Base) di più classi, e potenzialmente anche di un’intera


applicazione, all’interno di un unico file vb. Con Vi-
La scrittura di una funzione generica può es- sual Basic 2005 diventa possibile anche fare l’ope-
sere effettuata in questo modo: razione inversa, cioè suddividere l’implementazio-
ne di un’unica classe tra più file vb. Questa pos-
Sub Sort(Of T)(ByVal oArr() As T) sibilità, finora disponibile solo in C++, può essere
... utile solo in casi molto particolari. Uno di questi,
End Sub però, risulta particolarmente importante. Si tratta
del caso delle classi il cui codice viene generato
La funzione potrà essere poi richiamata scrivendo: automaticamente da Visual Studio, come le form.
Nelle versioni precedenti di Visual Studio, il codi-
Sort(Of Integer)(oIntArray) ce generato veniva posto in una particolare regio-
ne all’interno dello stesso file vb dove sarebbe sta-
La gestione degli operatori to poi scritto il codice da parte dello sviluppatore.
Questo codice viene ora posto, invece, all’interno
La possibilità di sovrascrivere e personalizzare di un file separato, chiamato nomefile.designer.vb.
la gestione degli operatori non aggiunge di fatto Al di là di questa particolarità, la possibilità di uti-
un nuovo tipo di funzionalità al linguaggio, ma lizzare i tipi parziali può essere utile anche per per-
permette di progettare in modo più elegante l’in- mettere a più sviluppatori di intervenire contempo-
terfaccia di alcune classi. Per esempio, suppo- raneamente sul codice di una stessa classe, o per
nendo di voler dotare una nostra classe Pun- suddividere in più sezioni fisiche parti logicamente
to di un operatore di somma, non saremo più distinte di una classe di grosse dimensioni.
costretti a creare un metodo Add, ma potremo
sfruttare l’operatore +, permettendo agli utenti Le altre novità
della classe di scrivere, ad esempio:
Oltre ai generics, alla gestione degli operatori e
Dim a As Punto(3,4) ai partial type, la nuova versione di Visual Basic
Dim b As Punto (5,6) porterà con sé altre interessanti novità, elenca-
Dim c As Punto = a + b te brevemente nei punti successivi, seguendo
un personalissimo ordine di preferenza:
invece di
• Finalmente è disponibile la parola chiave Con-
Dim c As Punto = a.Add(b) tinue, che permette di passare direttamen-
te al ciclo successivo di un loop, saltando
Per sovrascrivere un operatore nel contesto tutte le righe di codice comprese tra questa
di una nostra classe è sufficiente implementa- istruzione e la fine del loop. I programmato-
re un metodo nel modo seguente: ri C/C++ conoscono bene questa istruzione,
mentre i programmatori Visual Basic hanno
Public Operator +(ByVal p1 As Punto, ByVal p2 As Punto) da sempre avuto a disposizione solo l’istru-
As Punto zione exit, che permette di uscire dal loop, e
hanno dovuto simulare la continue con qual-
Come si vede, occorre utilizzare la parola chia- che stratagemma, il più comune dei quali è
ve operator immediatamente prima del nome un GoTo ad una label posta immediatamen-
dell’operatore che si intende sovrascrivere. te prima della successiva iterazione.
• I commenti XML, già disponibili in C# nella
I tipi parziali versione precedente di Visual Studio, per-
mettono di aggiungere commenti al codice
Gli sviluppatori Visual Basic, fino alla versione 6, utilizzando una speciale sintassi basata su
erano abituati a far coincidere la nozione di classe XML. La stesura di commenti in questo for-
o form con la nozione di file: ogni file cls o frm cor- mato viene supportata dall’editor, senza la ne-
rispondeva ad una classe o ad una form, e la de- cessità di apprenderne la sintassi, e consente
finizione di ogni classe o form era completamente di ottenere in modo automatico la documen-
inclusa all’interno di un solo file. Con l’avvento di tazione di progetto e informazioni aggiuntive
.NET, è diventato possibile includere la definizione da visualizzare tramite Intellisense.

20 VBJ N. 64 - Luglio/Agosto 2005


VB.NET

• Un’altra interessante innovazione è costi- casi abbiano una loro utilità. Il fatto che siano
tuita dalla parola chiave My, che fornisce stati resi disponibili anche in Visual Basic può
un percorso di accesso alternativo a mol- essere comodo soprattutto nel caso in cui si
te tra le classi più utilizzate del framework. debbano richiamare funzioni C che usino uno di
Per esempio si può scrivere My.User.Identity questi tipi come parametro o valore di ritorno.
per accedere alle informazioni relative al- • Un’altra cosa che non mi è mai piaciuta mol-
l’utente dell’applicazione. In modo analo- to, è la possibilità di utilizzare, fino a Visual
go, My.Computer e My.Application permet- Basic 6, istanze “implicite” di form, scriven-
tono di accedere rapidamente alle informa- do ad esempio
zioni relative al sistema e all’applicazione.
• La parola My può essere anche utilizzata per MyForm.Show
accedere da programma agli elementi del pro-
getto. Per esempio, si può aggiungere un ele- invece di
mento all’insieme dei Settings del progetto,
e accedere a quell’elemento da programma Dim x as New MyForm
scrivendo My.Settings.<NomeElemento> x.Show
• A molti sarà capitato di utilizzare operazio-
ni di cast non sicure, cioè di dovere esegui- Con Visual Basic 2005, questa possibilità è
re alcune operazioni dipendenti dal tipo di stata reintrodotta.
una variabile, senza conoscere esattamente
il tipo di quest’ultima. Per potere verificare L’ambiente di sviluppo
la correttezza del cast era finora necessa-
rio gestire l’eventuale eccezione all’interno Al di là degli aspetti strettamente legati al lin-
di un blocco try, in questo modo: guaggio, la nuova versione di Visual Studio por-
terà con sé diverse nuove funzionalità. Tra queste,
Try vale sicuramente la pena di citare la possibilità,
Dim y as MyType = CType(x,MyType) cara ai programmatori Visual Basic, di modifica-
Catch re il codice durante il debug. Questa funzionali-
End Try tà, non disponibile in Visual Studio 2003, è sta-
ta nuovamente introdotta con la versione 2005.
Viene ora offerta la possibilità di utilizzare una Sono stati inoltre introdotti diversi miglioramenti
scorciatoia, costituita dall’istruzione TryCast, che nell’editor di codice, che ora è in grado, come nel
consente di verificare l’avvenuta conversione sem- caso degli strumenti Office, di suggerire possibili
plicemente controllando che il valore della variabile correzioni per diversi tipi di errore, e di mostrare
di destinazione non sia uguale a Nothing: tramite tooltip i valori di variabili anche complesse
durante le operazioni di debug. Anche la finestra
Dim y as MyType = TryCast(x,MyType) Immediata è stata potenziata, e ora può essere
If y IsNot Nothing Then utilizzata, come con Visual Basic 6, anche quan-
do il programma non è in esecuzione.
In questo frammento di codice si può vede-
re anche un’altra delle novità minori introdotte Conclusioni
con Visual Basic 2005, cioè la possibilità di uti-
lizzare il nuovo operatore IsNot invece di scri- Abbiamo visto una veloce carrellata, non esausti-
vere If Not y Is Nothing. va, delle principali novità introdotte da Visual Ba-
sic 2005. Alcune sono sicuramente interessanti,
• È ora possibile associare regole di visibilità altre sono solo delle curiosità. La maggior parte
diverse alle due parti Get e Set di una stes- di queste novità non sono particolarmente origi-
sa proprietà. Si può fare in modo, cioè, che nali, ma solo adattamenti di strumenti già presen-
la parte Get sia pubblica mentre la parte Set ti in altri linguaggi, o reintroduzioni di funzionalità
sia privata o protetta. già presenti in Visual Basic 6. Con queste nuove
• I tipi unsigned, in C, non mi sono mai piaciu- aggiunte, Visual Basic diventa un linguaggio piut-
ti molto, li ho sempre considerati soprattutto tosto completo, avendo eliminato molte limitazio-
come un modo per compiere errori più facil- ni senza per questo perdere quella semplicità di
mente. Non si può negare però che in alcuni utilizzo che lo ha reso popolare.

N. 64 - Luglio/Agosto 2005 VBJ 21


VB.NET

Un motore di scripting
per l’ambiente .NET
Realizziamo un programma in grado di compilare ed eseguire al volo file di testo
contenenti codice VB.NET

Scripting
di Filippo Bonanni

H
o sempre considerato le tecnologie di scripting estensione che saranno compila-
tra gli strumenti più utili messi a disposizione ti ed eseguiti sul classico doppio
di un programmatore; ormai non conto più le click del mouse.
volte che ho utilizzato un file VBScript o JScript per Il programma completo di esem-
risolvere piccole (ma non solo) esigenze: disporre di pi e file di setup, chiamato Net-
file di testo contenenti codice che viene elaborato a Script, è disponibile sul sito ftp di
run-time da un interprete, facilmente modificabili e Infomedia; i file di script sono ri-
distribuibili, ha sicuramente contribuito a migliorare conoscibili per l’estensione .vbns
la mia vita di sviluppatore. (Visual Basic Net Script).
Purtroppo queste tecnologie presentano anche dei
limiti, primo fra tutti l’utilizzo di un linguaggio non ti-
pizzato, pertanto con l’avvento di .NET confidavo di Struttura del Programma
trovare un epigono del Windows Scripting Host che
mi permettesse di abbinare l’immediatezza dello scrip- Come già discusso in un pre-
ting alla potenza del codice “managed”. cedente articolo (Computer Pro-
Invece, per rimpiazzare i vecchi VBScript mi sono gramming n. 146 “Un DTS con
ritrovato a scrivere, oltre al codice, file batch per la XML e la Reflection di .NET”), la
compilazione, lasciandomi una sensazione di mal- compilazione a run-time in .NET
contento ogni volta che editavo, ricompilavo ed ese- è ottenuta mediante l’ausilio del-
guivo. Il programma descritto in questo articolo è il le classi contenute nei namespa-
frutto di tale insoddisfazione: un motore di scripting ce System.CodeDom.Compiler e
per .NET in grado di compilare al volo ed eseguire System.Reflection (vedi [1]); ciò
file di testo contenenti codice VB.NET. su cui ci focalizzeremo maggior-
Per rendere l’utilizzo assolutamente trasparente, ol- mente è l’utilizzo della Reflection
tre alla realizzazione dell’applicazione vedremo anche per ispezionare l’assembly com-
come creare un semplice setup che ci permetta di pilato a run-time in cerca della
abbinare al programma, dei file con una particolare classe contenente il punto d’in-
gresso (la Sub Main) e la logica
per referenziare in fase di com-
pilazione assembly di terze parti
Filippo Bonanni Si occupa di informatica dal 1998 ed al non registrati nella GAC.
momento i suoi interessi sono rivolti principalmente all’am- Il programma quindi è strut-
biente .NET. Attualmente lavora nel team di sviluppo della
turato secondo questa sempli-
D&T Informatica di Sesto Fiorentino in progetti web-based di
interfacciamento a sistemi AS/400 e di archiviazione ottica ce logica:
in ASP.NET. Può essere contattato tramite e-mail all’indirizzo
fbonanni@infomedia.it. 1. Recupero e lettura del file;

22 VBJ N. 64 - Luglio/Agosto 2005


VB.NET

2. Compilazione con l’aggiunta di eventuali ri-


ferimenti ad assembly esterni; Listato 1 Un file di script d’esempio

3. Recupero del punto d’ingresso ed esecu-


zione del codice compilato.
Option Explicit On
I file di script
Imports System
Imports Microsoft.VisualBasic
Il Listato 1 mostra un modello di file conte-
nente il codice che verrà compilato ed esegui- ‘-- ESEMPIO DI AGGIUNTA REFERENZE --
to dal nostro programma. ‘-- RIMUOVERE SE NON NECESSARIO --
‘///<references>
Il codice VB.NET è composto dalla direttiva ‘/// <prv>C:\NetAssembly\test.dll</prv>
Option Explicit On, dalle importazioni di name- ‘///</references>
space e dalla classe MyApplication contenen-
te il punto d’ingresso Sub Main. I riferimenti Public Class MyApplication
Public Shared Sub Main()
ad assembly “privati” sono dichiarati all’inter- ‘Qui il nostro codice
no del codice mediante una struttura XML in- End Sub
serita come righe di commento: End Class

‘///<references>
‘/// <prv>C:\NetAssembly\test.dll</prv> bly da referenziare. Per farlo utilizziamo la
‘///</references> classe CompilerParameters del namespace
System.CodeDom.Compiler (vedi [1])
Questa struttura sarà estrapolata dal codice,
memorizzata in un oggetto XmlDocument e uti- Dim CompParam as New CompilerParameters()
lizzata per recuperare il percorso completo de-
gli assembly da referenziare. CompParam.GenerateExecutable=true
CompParam.GenerateInMemory = true
Recupero e lettura del file CompParam.IncludeDebugInformation = Debug

Per recuperare ed eseguire il codice si usa la CompParam.ReferencedAssemblies.add(“System.dll”)


classica forma da riga di comando in cui si pas- CompParam.ReferencedAssemblies.add(“System.Data.dll”)
sa al programma il percorso completo del file sor- CompParam.ReferencedAssemblies.add(“System.Xml.dll”)
gente “Netscript.exe c:\testfile.vbns”, prevedendo CompParam.ReferencedAssemblies.add(“System.Drawing.dll”)
anche il passaggio del parametro /d che ci per- CompParam.ReferencedAssemblies.add(“System.Web.dll”)
metterà di specificare se la compilazione debba CompParam.ReferencedAssemblies.add(“System.Windows.Forms.dll”)
avvenire in modalità debug. CompParam.ReferencedAssemblies.add(“System.Management.dll”)
La lettura avviene in due fasi tramite l’utilizzo di CompParam.ReferencedAssemblies.add(“Microsoft.VisualBasic.dll”)
uno StreamReader: la prima fase recupera solo
le righe che iniziano per ‘/// contenenti la struttura Le prime tre righe specificano che creeremo
XML sopra descritta, verificando che siano rac- un file di tipo eseguibile, che sarà eseguito in
chiuse tra i due tag <references> e </references> memoria (e quindi non salvato su disco) e che
e generando un’eccezione in caso contrario (Li- in base al valore della variabile booleana Debug
stato 2). La seconda fase non fa altro che recu- (settata a True se abbiamo passato il parametro
perare nuovamente il file ma stavolta per intero /d) potranno essere incluse le informazioni per il
debug. Le altre righe invece aggiungono le refe-
sr.close() renze agli assembly più comuni contenuti nella
sr=New StreamReader(filename) GAC, così da avere ampia libertà nella scrittura
del codice. Gli eventuali riferimenti ad assem-
Compilazione con l’aggiunta di eventuali bly esterni sono specificati nella variabile strin-
riferimenti ad assembly esterni ga contenente il codice XML: dopo averla letta
tramite un oggetto XmlDocument, utilizziamo un
Prima di compilare occorre fornire al Provider XmlNodeList e una semplice query Xpath (vedi
le informazioni riguardanti il tipo di file che ot- [2]) per recuperare tutti i nodi-figlio dell’elemen-
terremo (se eseguibile o meno) e gli assem- to principale <references>

N. 64 - Luglio/Agosto 2005 VBJ 23


VB.NET

‘Carico in un XmlDocument il codice xml delle referenze dell’assembly che dobbiamo importare è me-
Dim doc as XmlDocument glio specificare una directory alternativa nella
Dim nodeList as XmlNodeList quale copieremo il file specificato nell’elemen-
to <prv> ; per farlo ricorriamo al file .config e
doc=New XmlDocument() all’elemento <probing> (vedi [1]) che permette
doc.LoadXml(xml) di specificare ulteriori percorsi di ricerca

‘recupero tutti i nodi-figlio del nodo principale <configuration>


nodeList=doc.SelectNodes(“/references/*”) <runtime>
<assemblyBinding xmlns=”urn:schemas-microsoft-
Compiendo un ciclo su tutti i nodi-figlio tro- com:asm.v1”>
vati, recuperiamo il testo contenuto negli ele- <probing privatePath=”bin;bin_temp;”/>
menti <prv> che indica il percorso dell’as- </assemblyBinding>
sembly. </runtime>
Per referenziarlo correttamente, non basta </configuration>
aggiungerlo alla lista come abbiamo fatto per
gli assembly della GAC, occorre anche che L’assembly sarà sovrascritto ogni volta che
sia contenuto in una directory che verrà scan- eseguiremo lo script onde evitare problemi di
sionata dal Common Language Runtime du- disallineamento tra la versione già presente
rante il caricamento dei medesimi. nella directory temporanea e quella da refe-
Tipicamente la directory predefinita è la bin renziare; se il file è trovato e copiato corretta-
contenuta all’interno della path principale del mente viene aggiunto alla lista dei riferimenti.
programma. Visto però la natura temporanea La ricerca dell’assembly è effettuata prima di
tutto come se all’in-
terno del tag <prv>
Listato 2 La lettura del codice avviene in due fasi tramite l'utilizzo di uno fosse specificato il
StreamReader percorso comple-
to poi, nel caso in
Dim sr as StreamReader
cui il file non ven-
sr=New StreamReader(filename) ga trovato, ante-
ponendo il percor-
‘Ciclo per recuperare la struttura xml di referenze agli assembly so in cui risiede lo
Do While sr.Peek() >= 0
script che si sta
strTemp=sr.ReadLine() eseguendo (Lista-
to 3). Per la com-
If strTemp.IndexOf(“’///”)>=0 Then pilazione utilizziamo
strTemp=strTemp.SubString(strTemp.IndexOf(“’///”)+4)
la classe VBCode-
‘segnalo che sto leggendo il tag di apertura Provider contenu-
If strTemp.StartsWith(“<references>”) Then ta nel namespace
GetReference=True Microsoft.VisualBasic
End If
(vedi [1]): tale clas-
‘Se ho trovato il tag di apertura recupero le righe successive se, tramite il metodo
If GetReference Then VBCodeProvider.Cre
xml+=strTemp.Replace(“’///”,””) & VbCrLf
ateCompiler() forni-
End If
sce un’istanza del
‘segnalo che sto leggendo il tag di chiusura compilatore di codi-
If strTemp.StartsWith(“</references>”) Then ce Visual Basic che
GetReference=False
End If
utilizzeremo, insieme
End If ai parametri specifi-
Loop cati in CompParam,
per compilare il co-
If GetReference Then Throw New XmlReferenceException(String.Format(“L’elemento
<references> non è stato chiuso con </references>”))
dice sorgente ed ot-
tenere il programma
da eseguire

24 VBJ N. 64 - Luglio/Agosto 2005


VB.NET

Riquadro 1 Una rappresentazione delle chiavi create

HKEY_CLASSES_ROOT
|
|_.vbns (Predefinito=”NetScriptFile”)
|
|_ShellNew (FileName=”C:\Programmi\D&T Informatica\NetScript\Modelli\model.vbns”)

HKEY_CLASSES_ROOT
|
|_NetScriptFile (Predefinito=”File NetScript”)
|
|_DefaultIcon (FileName=”C:\Programmi\D&T Informatica\NetScript\NetScript.exe, 0”)
|
|_Shell (Predefinito=”Open”)
|
|_Debug (Predefinito=”Esegui con NetScript (debug mode)”)
| |
| |_command (Predefinito=””C:\Programmi\D&T Informatica\NetScript\NetScript.exe” %1 /d”)
|
|_Open (Predefinito=”Esegui con NetScript”)
|
|_command (Predefinito=””C:\Programmi\D&T Informatica\NetScript\NetScript.exe” %1”)

Dim Provider as New VBCodeProvider La proprietà restituisce un oggetto MethodInfo


Dim Comp as ICodeCompiler=Provider.CreateCompiler() ed a sua volta dispone di una proprietà Name
Dim CompRes as CompilerResults che ci fornirà il nome del membro contenente il
CompRes=Comp.CompileAssemblyFromSource(CompParam, punto d’ingresso: ora non resta che richiamarlo
sr.ReadToEnd()) per eseguire il nostro script. Per farlo recupe-
riamo il membro tramite un oggetto Type ese-
Dopo la compilazione CompRes contiene l’as- guendo un ciclo all’interno dei tipi contenuti nel
sembly o, nel caso di errori, una lista delle eccezioni nostro assembly
generate che potranno essere mostrate a video
Dim t as Type
If CompRes.Errors.Count > 0 Then
For i=1 to CompRes.Output.Count-1 for i=0 to CompRes.CompiledAssembly.GetTypes.Length - 1
Console.WriteLine(CompRes.Output.Item(i)) If CompRes.CompiledAssembly.GetTypes(i).GetMember(Entry
Next Point).Length > 0 Then
Else t = CompRes.CompiledAssembly.GetTypes(i)
‘Qui la procedura di esecuzione dello script descritta Exit For
più avanti End If
End If Next

A questo punto possiamo eseguire il nostro pro-


Recupero del punto d’ingresso ed esecu- gramma ricorrendo al metodo InvokeMember della
zione del codice compilato classe Type, passandogli il nome dell’EntryPoint e
specificando che si tratta di un metodo
Per recuperare il punto di ingresso è sufficien-
te leggere la proprietà EntryPoint relativa all’as- t.InvokeMember(CompRes.CompiledAssembly.EntryPoint.Name,
sembly appena generato BindingFlags.InvokeMethod, Nothing , Nothing , Nothing)

Dim EntryPoint as String Il Listato 4 riporta tutta la procedura dall’aggiunta


EntryPoint=CompRes.CompiledAssembly.EntryPoint.Name dei riferimenti alla compilazione ed esecuzione

N. 64 - Luglio/Agosto 2005 VBJ 25


VB.NET

Realizzazione del Setup vo” del menu contestuale proprio come per
i file di Testo, Word, Excel ecc.
Per rendere l’applicazione comoda da utiliz-
zare occorrono tre cose: Tutto questo lo si può realizzare intervenendo
sul Registro di Configurazione tramite un sem-
• L’abbinamento dei file .vbns al programma plice file di script che eseguiremo per effettuare
in modo che vengano eseguiti sul doppio l’installazione; senza dilungarsi sulla struttura del
click del mouse Registry, analizziamo le voci che dovranno esse-
• L’aggiunta delle voci di “esecuzione” e di re scritte nella chiave principale HKEY_CLAS-
“esecuzione in modalità debug” al menu SES_ROOT. Creiamo la chiave .vbns che conter-
contestuale che appare cliccando col pul- rà il riferimento ad un’altra chiave NetScriptFile e
sante destro sui file .vbns il percorso completo del file modello da utilizzare
• La creazione di un file .vbns vuoto come quando l’utente selezionerà “File NetScript” dalla
quello del Listato 1 tramite la voce “Nuo- voce “Nuovo” del menu contestuale.

Listato 3 Il codice per la ricerca dell'assembly

‘Ciclo alla ricerca di elementi <prv>


For i=0 to nodeList.Count-1
reference=Nothing

If nodeList.Item(i).Name=”prv” Then

‘recupero il percorso completo dell’assembly e lo copio nella directory temporanea specificata


nel file .config
reference=nodeList.Item(i).InnerText
reference=tempBinDir & reference.Substring(reference.LastindexOf(“\”)+1)

‘Cerco il file come specificato nell’elemento <prv> e se non lo trovo provo a cercarlo nella
cartella in cui risiede lo script
If File.Exists(nodeList.Item(i).InnerText) Then
File.Delete(reference)
File.Copy(nodeList.Item(i).InnerText, reference)
Else

If File.Exists(filepath & nodeList.Item(i).InnerText) Then

File.Delete(reference)
File.Copy(filepath & nodeList.Item(i).InnerText, reference)
Else
Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”,
nodeList.Item(i).InnerText))
End If

End If

End If

‘Se sono riuscito a copiare l’assembly, lo aggiungo alle referenze


If Not(reference is Nothing) Then

If File.Exists(reference) Then
CompParam.ReferencedAssemblies.add(reference)
Else
Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”, reference))
End If

End If
Next

26 VBJ N. 64 - Luglio/Agosto 2005


VB.NET

Listato 4 Tutta la procedura di compilazione ed esecuzione (continua)

sr=New StreamReader(filename)
CompParam.GenerateExecutable=true
CompParam.GenerateInMemory = true
CompParam.IncludeDebugInformation = Debug

CompParam.ReferencedAssemblies.add(“System.dll”)
CompParam.ReferencedAssemblies.add(“System.Data.dll”)
CompParam.ReferencedAssemblies.add(“System.Xml.dll”)
CompParam.ReferencedAssemblies.add(“System.Drawing.dll”)
CompParam.ReferencedAssemblies.add(“System.Web.dll”)
CompParam.ReferencedAssemblies.add(“System.Windows.Forms.dll”)
CompParam.ReferencedAssemblies.add(“System.Management.dll”)
CompParam.ReferencedAssemblies.add(“Microsoft.VisualBasic.dll”)

‘Cerco referenze aggiuntive


If xml.Trim()<>”” Then
If Debug Then
Console.WriteLine(“Referenze aggiutive:”)
Console.WriteLine(xml)
Console.WriteLine()
End If

‘Carico in un XmlDocument il codice xml delle referenze


doc=New XmlDocument()
doc.LoadXml(xml)

‘recupero tutti i nodi-figlio del nmodo principale


nodeList=doc.SelectNodes(“/references/*”)

‘Ciclo alla ricerca di elementi <prv>


For i=0 to nodeList.Count-1
reference=Nothing
If nodeList.Item(i).Name=”prv” Then
‘recupero il percorso completo dell’assembly e lo copio nella directory temporanea specificata nel file .config
reference=nodeList.Item(i).InnerText
reference=tempBinDir & reference.Substring(reference.LastindexOf(“\”)+1)
If File.Exists(nodeList.Item(i).InnerText) Then
File.Delete(reference)
File.Copy(nodeList.Item(i).InnerText, reference)
Else
If File.Exists(filepath & nodeList.Item(i).InnerText) Then
File.Delete(reference)
File.Copy(filepath & nodeList.Item(i).InnerText, reference)
Else
Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”,
nodeList.Item(i).InnerText))
End If
End If
End if
‘Se sono riuscito a copiare l’assembly, lo aggiungo alle referenze
If Not(reference is Nothing) Then
If File.Exists(reference) Then
CompParam.ReferencedAssemblies.add(reference)
Else
Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”, reference))
End If
End If
Next
End If

N. 64 - Luglio/Agosto 2005 VBJ 27


VB.NET

Listato 4 (segue) Tutta la procedura di compilazione ed esecuzione

CompRes=Comp.CompileAssemblyFromSource(CompParam, sr.ReadToEnd())
‘Nel caso di errori li mostro a video
If CompRes.Errors.Count > 0 Then
For i=1 to CompRes.Output.Count-1
Console.WriteLine(CompRes.Output.Item(i))
Next
Else
‘Recupero del punto di ingresso dell’assembly generato
EntryPoint=CompRes.CompiledAssembly.EntryPoint.Name
for i=0 to CompRes.CompiledAssembly.GetTypes.Length - 1
If CompRes.CompiledAssembly.GetTypes(i).GetMember(EntryPoint).Length > 0 Then
t = CompRes.CompiledAssembly.GetTypes(i)
Exit For
End If
Next

‘Esecuzione del programma


t.InvokeMember(CompRes.CompiledAssembly.EntryPoint.Name, BindingFlags.InvokeMethod, Nothing , Nothing , Nothing)
End If

Nel Riquadro 1 è mostrata una rappresenta- RegistryKey con il quale creare i valori all’inter-
zione della chiave creata; tra parentesi indichia- no della chiave selezionata. Di seguito mostria-
mo, a sinistra dell’uguale i valori stringa e a de- mo la scrittura della chiave .vbns
stra i dati in esso contenuti. Il percorso del file
modello è contenuto all’interno del valore strin- Dim rk as RegistryKey
ga FileName della chiave ShellNew. La chiave rk=Registry.ClassesRoot.CreateSubKey(“.vbns”)
NetScriptFile a cui punta il valore predefinito di rk.SetValue(“”,”NetScriptFile”)
.vbns è quella che contiene tutti i riferimenti al rk.close()
programma e precisamente: rk=Registry.ClassesRoot.CreateSubKey(“.vbns\\ShellNew”)
rk.SetValue(“FileName”,ModelDir & FileModello)
• Il nome che appare nella voce “Nuovo” del rk.close()
menu contestuale (“File NetScript”);
• Il riferimento all’icona associata ai file .vbns; Conclusioni
• Le due voci per l’esecuzione in modalità
normale e debug (rispettivamente la chiave Forse qualcuno si sarà accorto che nella tratta-
Open e la chiave Debug). zione abbiamo volutamente evitato di affrontare
l’argomento della referenza di assembly di terze
Il dato del valore stringa Predefinito della chia- parti registrati nella GAC: in questo caso infatti è
ve Shell indica quale, tra le sottochiavi presen- impensabile ricorrere alla soluzione di specificare
ti, è quella che contiene il comando predefinito l’intero percorso data la complessità della struttu-
da eseguire sul doppio click, mentre i dati dei ra di directory e sottodirectory, con nomi tutt’al-
valore stringa delle chiavi Open e Debug mo- tro che facili da ricordare. In un prossimo articolo
strano il testo che appare quando si clicca col affronteremo questa lacuna, realizzando un pro-
pulsante destro su un file .vbns. Il marcatore gramma che ci permetterà di specificare solo il
%1 è utilizzato dal Sistema Operativo per so- nome dell’assembly e interrogare in modo rapido
stituire ad esso il percorso completo del file di la GAC per conoscerne il percorso completo.
script che stiamo eseguendo. La scrittura nel
Registro di Configurazione è effettuata tramite Riferimenti
le classi Registry e RegistryKey contenute nel
namespace Microsoft.Win32 (vedi [1]); la prima [1] .NET Framework SDK - http://msdn.micro-
è una classe statica che fornisce un rapido ac- soft.com/netframework/support/documentation
cesso in lettura/scrittura a ciascuna delle chiavi [2] XPath Tutorial - http://www.w3schools.com/
principali del Registry, restituendo un oggetto xpath

28 VBJ N. 64 - Luglio/Agosto 2005


DATABASE

SQL Server 2005


ed il CLR:
un matrimonio annunciato
SQL Server 2005 rappresenta una grande evoluzione nella gestione di database. Vediamo come
l’integrazione con il framework .NET possa migliorare la vita degli sviluppatori e dei DBA.

di Andrea Benedetti

T
ra le varie novità introdotte con la nuova ver- o dove devono essere fatti calcoli
sione di SQL Server, sicuramente l’integrazione ed aggregazioni complesse.
con il framework .NET è quella che agli occhi Tutte le procedure che conten-
dello sviluppatore risulta più intrigante. gono ed utilizzano cursori diven-
È necessario chiarire subito un punto: T-SQL, ov- tano quindi ottime candidate per
vero il dialetto proprietario di casa Microsoft, non una riscrittura in codice gestito.
è morto. Prima di questa versione l’unico
Anzi le nuove funzionalità ed i nuovi operatori sono modo per scrivere logica lato ser-
talmente tanti e tali che potrebbe essere necessa- ver era quella di costruire stored
rio rimettersi un po’ sui libri: large value data type, procedure estese che non pote-
event notifications, DDL triggers, pivot, row_num- vano fornire nessun controllo del-
ber, ecc… le risorse da esse utilizzate.
Il T-SQL, che si compone di linguaggio per le query SQL Server ci mette oggi a di-
e accesso ai dati e di linguaggio procedurale, re- sposizione un nuovo modello
sta, di fatto, l’unica scelta per recuperare e mani- di programmazione fornito dal-
polare dati (il CLR diventa un’alternativa alla scrit- la completa integrazione con il
tura di codice procedurale) e la scelta migliore con framework (versione 2.0), con il
comandi basati su set di dati. motore del CLR inserito diretta-
Conviene utilizzarlo sempre quando possibile ed mente nel motore di SQL Ser-
impiegare il CLR solo dopo aver verificato l’assen- ver (SQL Server fa da host al
za di istruzioni equivalenti o in presenza di logica CLR, quindi in un unico domi-
che non può essere espressa in maniera dichiarati- nio di applicazione); CLR che di-
va nel linguaggio di interrogazione. venta un’ottima e robusta alter-
Sicuramente possiamo trarne vantaggio, in termini nativa alla scrittura di procedu-
di performance, in tutti quei casi in cui, navigando re estese.
un set di record, dobbiamo “lavorare” ciascuna riga Infatti se un’applicazione client,
scritta in .NET, gira al di fuori di
un database ed accede ad esso
attraverso una connessione, con
Andrea Benedetti si occupa di disegno e manutenzione di
SQL Server 2005 siamo in grado,
basi di dati e di sviluppo di applicazioni web-based finalizzate
all’erogazione di servizi sanitari per conto di Studiofarma Srl invece, di scrivere codice gestito
di Brescia. Può essere contattato tramite e-mail all’indirizzo che verrà eseguito direttamente
abenedetti@infomedia.it. all’interno del database stesso.

N. 64 - Luglio/Agosto 2005 VBJ 29


DATABASE

Chiariamo che il runtime viene/verrà carica- Lo strumento di sviluppo utilizzato, Visual Stu-
to solo alla richiesta di esecuzione di codi- dio 2005, ci permette di utilizzare il medesimo
ce gestito. ambiente sia per scrivere ed effettuare debug
Se questa richiesta non verrà mai fatta, è evi- di applicazioni/oggetti database, sia per rea-
dente che il CLR non verrà mai caricato. lizzare applicazioni e componenti client.
Inoltre viene data la facoltà al DBA di disabi- Inoltre permette lo sviluppo di oggetti (fun-
litare completamente l’integrazione con il fra- zioni, procedure, trigger, tipi e aggregazioni),
mework (di default è già così, quindi per uti- operazioni di debug ed installazione automa-
lizzarlo dovremo abilitarlo configurando appo- tica sul server.
sitamente il server). Di default i database di esempio non vengo-
no installati.
L’integrazione ed i principali benefici È necessario quindi, durante l’installazione,
entrare nelle opzioni avanzate e selezionare i
Per iniziare a prendere dimestichezza con database interessati (database che, eventual-
questo matrimonio annunciato, abbiamo co- mente, possono anche essere installati in un
struito una macchina di test (una macchina vir- secondo momento).
tuale ospitata in Microsoft Virtual PC con si- Come abbiamo appena detto, di default, il
stema operativo Windows Server 2003 Stan- CLR viene disabilitato. Possiamo vedere la
dard Edition) in tre passi: installazione Visual configurazione estesa (Figura 1) del server
Studio 2005 (in beta 1), disinstallazione del con queste semplici istruzioni:
Framework 2.0 (non può essere soprascritto)
e, per finire, installazione di SQL Server 2005 USE master
(in beta 2). GO

Figura 1 La configurazione del server

30 VBJ N. 64 - Luglio/Agosto 2005


DATABASE

La prima procedura .NET


Listato 1 L’equivalente in C# della stored
procedure
Creiamo adesso un piccolo esempio per poter
capire meglio come viene gestita l’integrazio-
using System; ne. Lanciamo Visual Studio 2005 e, dal menu
using System.Data; “File”, “New”, “Project”, “Database”, sceglia-
using System.Data.Sql;
mo il nuovo tipo di progetto, presente da que-
using System.Data.SqlServer;
using System.Data.SqlTypes; sta versione: “SQL Server Projects”. Assegnia-
mo un nome, SqlCrlTest, e premiamo “OK”
public partial class StoredProcedures (Figura 2).
{
[SqlProcedure]
Si aprirà a video una finestra per poter con-
public static void up_SelectProducts(string figurare la connessione verso il server sql: in-
ProductName) dichiamo il nome del server, selezioniamo la
{ modalità di autenticazione (integrated securi-
SqlPipe myPipe = SqlContext.GetPipe();
System.Data.SqlServer.SqlCommand cmd =
ty), selezioniamo il database AdventureWorks
SqlContext.GetCommand(); e premiamo “OK”.
cmd.CommandText = “SELECT ProductID, Name, Visual Studio apre quindi una nuova solution
ProductNumber “ + di progetto, con il nome stabilito, vuota.
“ FROM Production.Product “ +
“ WHERE name like @ProductName + ‘%’”; Facciamo quindi tasto destro sul progetto
cmd.Parameters.Add(“@ProductName”, SqlClrTest, click su “Add” e, tra i vari oggetti
SqlDbType.VarChar); proposti, scegliamo “Stored Procedure”.
cmd.Parameters[0].Value = ProductName; Assegniamo un nome al file e, quindi, al me-
SqlDataReader dr = cmd.ExecuteReader(); todo interno alla classe che andremo a scri-
vere: up_SelectProducts.cs ed infine premia-
myPipe.Send(dr); mo “Add”.
}
Diamo una prima occhiata alla classe che Vi-
};
sual Studio ha costruito con il suo template.
Il metodo up_SelectProducts() è defini-
to static void; tale metodo è decorato dal-
EXEC sp_configure ‘show advanced option’, ‘1’ l’attributo [SqlProcedure]; tra gli assem-
RECONFIGURE bly referenziati ne troviamo due fi no ad
EXEC sp_configure oggi sconosciuti: System.Data.SqlServer e
GO System.Data.SqlTypes.
Il tipo di ritorno della procedura deve essere
Tra i record visualizzati dall’istruzione sp_ definito static (o shared in VB.NET; la classe
configure è sufficiente individuare la voce “clr non verrà infatti istanziata direttamente) e, per
enabled” ed identificare il valore attualmente default, viene definito void (altrimenti potreb-
impostato. Per abilitarlo sarà sufficiente ese- be essere definito come Int in quanto le sto-
guire l’istruzione: red procedure possono ritornare solo interi o
nulla, ed il tipo SQL Server Int è compatibile
EXEC sp_configure ‘clr enabled’, 1 con i tipi CLR SqlInt32, SqlInt16, System.Int32
e System.Int16).
Il CLR delega a SQL Server, ad esclusione L’attributo [SqlProcedure] consentirà, all’at-
del meccanismo di garbage collection, tutte to dell’installazione, di far riconoscere il tipo
quelle attività che, di regola, gestisce diretta- di oggetto definito nel codice e, quindi, di ca-
mente: allocazione di memoria, thread, locking, ricarlo correttamente.
meccanismi di sincronizzazione... Il namespace System.Data.SqlServer contie-
SQL Server è infatti in grado di gestire la me- ne ed espone le classi per accedere e lavora-
moria direttamente, possiede un suo algorit- re nel processo di SQL Server (SqlExecution
mo di gestione dei thread (cooperative thread Content Class).
scheduling) e può decidere autonomamente Si tratta di un in-memory (in-process) provider
di abortirli in caso di eccezioni e anche, se (quindi assai diverso dal provider SqlClient),
necessario, scaricare l’AppDomain, ovvero il studiato ed ottimizzato per questo, che ci met-
runtime caricato. te in condizione di scrivere codice che verrà

N. 64 - Luglio/Agosto 2005 VBJ 31


DATABASE

eseguito direttamente all’interno del database Questa classe espone il metodo GetConnec-
sotto forma di stored procedure, user-defined tion() che ritorna un oggetto SqlConnection che
function, triggers, ecc... implementa l’interfaccia ISqlConnection.
Il codice scritto ed eseguito non dipenderà La SqlConnection ritornata ha alcune diver-
in alcun modo dalle connessioni al database. sità rispetto al medesimo oggetto esposto dal
Infatti nella scrittura di procedure e funzioni namespace SqlClient: non esiste un costrutto-
non sarà necessario creare connessioni (come re per la classe (si può solo utilizzare tramite il
era invece indispensabile nella scrittura di sto- metodo statico GetConnection()), esiste un nuo-
red procedure estese) grazie alla classe Sql- vo metodo CreateExecutionContext, non imple-
Context. menta l’interfaccia IDisposable (quindi non è

Listato 2 UDT che rappresenta il tipo “codice fiscale”

using System; if (s.IsNull || s.Value.ToLower().Equals


using System.Data.Sql; (“null”))
using System.Data.SqlTypes; return null;

[Serializable] string valore = Convert.ToString(s);


[SqlUserDefinedType(Format.UserDefined, MaxByte if (s.ToString().Length < 16)
Size = 512)] {
public class UDT_codiceFiscale : INullable, throw new NotSupportedException
IBinarySerialize (“Lunghezza errata”);
{ }
private Boolean _isNull;
private string cf = “”; UDT_codiceFiscale u = new UDT_codice
Fiscale();
public override string ToString() u.cf = s.ToString();
{ u._isNull = false;
if (this.IsNull) return u;
{ }
return “NULL”;
} public string myCF
else {
{ get
return this.cf; {
} return this.cf;
} }
}
public bool IsNull
{ #region IBinarySerialize Members
get
{ void IBinarySerialize.Read(System.IO.Binary
return this._isNull; Reader r)
} {
} this.cf = r.ReadString();
}
public static UDT_codiceFiscale Null
{ void IBinarySerialize.Write(System.IO.Binary
get Writer w)
{ {
UDT_codiceFiscale h = new UDT_ if (this.cf == null) this.cf =
codiceFiscale(); string.Empty;
h._isNull = true; w.Write(this.cf);
return h; }
}
} #endregion
}
public static UDT_codiceFiscale
Parse(SqlString s)
{

32 VBJ N. 64 - Luglio/Agosto 2005


DATABASE

possibile chiamarne la Dispose()). Il secondo


namespace, System.Data.SqlTypes, fornisce Listato 3 Caricamento dell’assembly e creazio-
ne del tipo con istruzioni T-SQL
le classi dei tipi che corrispondono perfetta-
mente ai tipi definiti in SQL Server.
Se infatti esiste il tipo System.Int32 che non -- Carico l’assembly:
richiede né conversioni, né marshaling, il tipo CREATE ASSEMBLY SqlClrTest
FROM ‘C:\....\SqlClrTest.dll’
System.Decimal non ha un esatto tipo corri-
spondente (ad esempio il valore massimo che -- Creo il tipo
può assumere il tipo SqlDecimal è di molto CREATE TYPE [UDT_codiceFiscale]
superiore al valore che può assumere il tipo EXTERNAL NAME SqlClrTest.UDT_codiceFiscale
decimal del CLR, ed una possibile conver- -- Creo la tabella:
sione di questo valore genererebbe un erro- CREATE TABLE ANA
re di runtime). (
Utilizzando questi tipi, quindi, non è neces- anaID int primary key not null,
cf UDT_codiceFiscale
sario effettuare alcuna conversione, renden- )
do di fatto più veloce l’esecuzione del codi-
ce gestito. -- Dichiaro una variabile di tipo UDT_codiceFiscale
Realizziamo, a titolo di esempio, una sem- DECLARE @CF UDT_codiceFiscale
SET @CF = CONVERT(UDT_codiceFiscale,
plice stored procedure in T-SQL: ‘BNDNDR75E09G702H’)
-- Insert sulla tabella
CREATE PROCEDURE dbo.up_SelectProductsTSQL INSERT ANA (anaID, cf) VALUES (1, @cf)
(
-- Select sulla tabella [nome colonna].[nome pro-
@ProductName varchar(50) prietà]
) SELECT cf.myCF FROM ANA
AS
SET NOCOUNT ON
SELECT ProductID, Name, ProductNumber L’equivalente scritto in C# è visibile nel Li-
FROM Production.Product stato 1. La prima considerazione cade sulla
WHERE name like @ProductName + ‘%’ definizione della classe: partial class Stored-
Procedures.
SET NOCOUNT OFF Il Framework 2.0 introduce questa definizione
GO di classe parziale che consente di suddividere
definizioni, strutture e interfacce su più file.
Risulta quindi evidente che
ogni classe che andremo a
scrivere, appartenente a col-
lezioni di oggetti (User Defi-
ned Functions, Triggers, ecc.),
deve necessariamente essere
definita parziale mentre non lo
sarà, ad esempio, per gli User
Defined Type.
L’oggetto SqlPipe serve per
inviare, invocando il suo me-
todo Send, i risultati al client e
rappresenta, in output, un Ta-
bular Data Stream (TDS, il for-
mato di scambio dati che SQL
Server utilizza da sempre).
SqlPipe è in grado di inviare
qualsiasi oggetto che imple-
Figura 2 La selezione di un nuovo progetto menti l’interfaccia IDataRe-
cord e si ottiene dal contesto
corrente, così come il SqlCom-

N. 64 - Luglio/Agosto 2005 VBJ 33


DATABASE

mand di cui abbiamo impostato il comando da collezione Assemblies sempre all’interno del-
eseguire ed il parametro necessario. la voce “Programmability”.
Scritto il codice non ci resta che effettuare Clicchiamo con il tasto destro sul nostro as-
l’installazione del nostro progetto: dal menu sembly per visualizzarne le proprietà ed ana-
“Build”, “Deploy Solution”. lizzare la voce “Permission set”.
Con questa operazione Visual Studio 2005 Per gli assembly caricati all’interno di SQL Ser-
genera ed esegue le necessarie istruzioni SQL ver, e quindi per il codice gestito, esistono tre
per installare l’assembly all’interno del databa- categorie di sicurezza (Code Access Security):
se e per creare la stored procedure costruita SAFE, EXTERNAL_ACCESS, UNSAFE.
nel codice (si può verificare facilmente apren-
do una traccia di SQL Profiler per visualizza- • SAFE è il set di permessi di default, permet-
re le istruzioni che vengono eseguite diretta- te di eseguire calcoli ed accesso ai dati uti-
mente sul server). lizzando il provider in-process, è l’equiva-
A questo punto non ci resta che aprire SQL lente di una procedura scritta in T-SQL.
Server Management Studio, espandere il nodo • EXTERNAL_ACCESS viene utilizzato in sce-
Databases del nostro server, espandere il db nari in cui si rende necessario accedere a ri-
AdventureWorks, “Programmability” e “Stored sorse esterne al server (ad esempio lettura/
Procedures”. scrittura file).
Possiamo notare che, oltre ad essere pre- • UNSAFE quando un assembly richiede ac-
sente la procedura up_SelectProductsTSQL, cesso a risorse particolari (ad esempio le
creata poco fa, esiste anche la procedura up_ API Win32).
SelectProducts installata direttamente dall’am-
biente di sviluppo insieme all’assembly Sql- Nelle prime due categorie il runtime si preoc-
ClrTest che possiamo trovare all’interno della cupa di verificare che il codice sia type-safe per

Figura 3 Esecuzione della stored procedure

34 VBJ N. 64 - Luglio/Agosto 2005


DATABASE

assicurarsi che non esistano accessi diretti a


locazioni di memoria, insomma fa di tutto per Listato 4 La classe UDA_csv.cs

eliminare possibili buffer overruns, puntatori


a locazioni errate, ecc.
In conclusione, il percorso che abbiamo fat- using System;
to è stato: scrivere il codice sorgente, com- using System;
using System.Data.Sql;
pilare, effettuare il deploy dell’assembly (Vi-
using System.Data.SqlTypes;
sual Studio si preoccupa per noi di registrare using System.Data.SqlServer;
e caricare nel db la funzione scritta), la con-
nessione al db e l’esecuzione della procedu- [Serializable]
[SqlUserDefinedAggregate(System.Data.Sql.Format.Use
ra (Figura 3). rDefined, MaxByteSize = 8000)]
Così come abbiamo effettuato la registra- public class UDA_csv : IBinarySerialize
zione ed il caricamento dell’assembly, con al- {
trettanta semplicità Visual Studio ci permet- System.Text.StringBuilder risultatoCSV;
te il debug diretto del nostro codice. Per pri- public void Init()
ma cosa aggiungiamo al nostro progetto uno {
script di test facendo tasto destro sul proget- risultatoCSV = new System.Text.StringBuil
to e click su “Add Test Script”. der();
}
Visual Studio aggiunge una nuova cartella
chiamata “TestScripts”, all’interno della qua- public void Accumulate(SqlString Value)
le si trova un file “Test.sql”. {
L’esecuzione di questo file di script sarà la if (Value.IsNull)
{
prima operazione che verrà fatta al lancio del- return;
l’applicazione. Non ci resta che inserire nel }
file di script l’istruzione T-SQL da lanciare: else
{
EXEC dbo.up_SelectProducts ‘c’, imposta-
risultatoCSV.Append(Value + “;”);
re un breakpoint sulla prima istruzione della }
nostra procedura e premere F5 per esegui- }
re il codice. Il nostro ambiente di sviluppo ci
public void Merge(UDA_csv Group)
consentirà di eseguire passo passo le istru- {
zioni che abbiamo generato e di controllare Accumulate(Group.ToString());
così ogni parte del flusso di lavoro. }
Così come abbiamo restituito un set di re- public SqlString Terminate()
{
cord, possiamo utilizzare lo stesso oggetto return new SqlString(risultatoCSV.ToStrin
SqlPipe per inviare al client un eventuale mes- g());
saggio di errore. Proviamo quindi a scatena- }
re un’eccezione (una divisione per zero) con
#region IBinarySerialize Members
due righe di codice:
void IBinarySerialize.Read(System.IO.BinaryRea-
[SqlProcedure] der r)
public static void up_GeneraEccezione() {
risultatoCSV = new System.Text.StringBuil-
{ der();
int a = 12; this.risultatoCSV.Append(r.ReadString());
int b = 0; }
try
void IBinarySerialize.Write(System.IO.BinaryWri-
{ ter w)
b = a / b; {
} w.Write(this.risultatoCSV.ToString());
}
catch(Exception ex)
{ #endregion
SqlContext.GetPipe().Send(“Errore!”); }
SqlContext.GetPipe().Send(“Msg: “ + ex.Message);
}
}

N. 64 - Luglio/Agosto 2005 VBJ 35


DATABASE

Tabella 1 Equivalenza dei tipi SQL Server e CLR

SQL Server CLR (SQL Server) CLR (.NET Framework)

Varbinary SQLBytes, SQLBinary Byte[]

Binary SQLBytes, SQLBinary Byte[]

Image

Varchar

Char

Nvarchar(1), Nchar(1) Char

Nvarchar SQLChars, SQLString String, Char[]

Nchar SQLChars, SQLString String, Char[]

Text

Ntext

Uniqueidentifier SQLGuid Guid

Rowversion

Bit SQLBoolean Boolean

Tinyint SQLByte Byte

Smallint SQLInt16 Int16

Int SQLInt32 Int32

Bigint SQLInt64 Int64

Smallmoney SQLMoney Decimal

Money SQLMoney Decimal

Numeric SQLDecimal Decimal

Decimal SQLDecimal Decimal

Real SQLSingle Single

Float SQLDouble Double

Smalldatetime SQLDateTime DateTime

Datetime SQLDateTime DateTime

SQL_variant Object

Table ISQLResultSet

Cursor

Timestamp

Xml SqlXml

Mandandola in esecuzione, la procedura viene • alternare i linguaggi .NET a T-SQL;


eseguita regolarmente, inviando al client il mes- • utilizzare classi ed oggetti del framework (ad
saggio di errore generato dall’eccezione. esempio: crittografia, accesso ai file…).
Da questi esempi risultano chiari, sempre te-
nendo presenti le premesse fatte all’inizio, al- Possibili scenari di utilizzo
cuni vantaggi e benefici che possiamo ottene-
re dall’integrazione del framework: Possibili scenari di utilizzo potrebbero essere in-
dividuati nel rimpiazzare stored procedure che uti-
• scrivere logica di business direttamente nel lizzano cursori o codice che carica intere tabelle o
database; grossi result-set per costruire aggregazioni partico-

36 VBJ N. 64 - Luglio/Agosto 2005


DATABASE

Figura 4 Esecuzione User Defined Aggregate

lari o per accedere a risorse esterne (file, consumo e Write per abilitare il codice a leggere e scri-
di web service, ecc.) Proviamo a scrivere tre pic- vere lo stream di byte.
coli esempi che possano spiegare meglio l’utilizzo Impostiamo la proprietà MaxByteSize, uti-
di codice gestito con SQL Server 2005. lizzata solo con il tipo di formato che abbia-
mo impostato, così da indicare la dimensione
User Defined DataType massima dello stream, tenendo presente che
il valore non può superare gli 8.000 byte.
Vediamo come poter definire un UDT che Questa volta dopo aver scritto e compilato il
rappresenti il tipo “codice fiscale” (Listato 2). codice eseguiamo, a titolo di esempio, il cari-
Ancora una volta tasto destro sul nostro pro- camento dell’assembly e la creazione del tipo
getto, “Add”, “User-Defined Types” ed impostia- con istruzioni T-SQL, direttamente da una fi-
mo il nome come UDT_codiceFiscale.cs. nestra di query del Management Studio (Li-
La classe, decorata con l’attributo [Serializa- stato 3). Sicuramente non è opportuno co-
ble], prevede l’implementazione dell’interfaccia struire tipi personalizzati per entità comples-
INullable ed i metodi: ToString, Parse e Null. se (persona, impiegato).
Nell’esempio, banalmente, andremo ad in-
serire un membro privato di tipo stringa chia- User Defined Aggregate
mato “cf”.
Avendo una proprietà stringa, la classe non La scrittura di un UDA, ovvero una funzione
può avere un SqlUserDefinedTypeAttribute im- che lavora su di un set di righe e ritorna un
postato a Native in quanto soltanto i tipi che valore scalare (come le funzioni di aggrega-
hanno un’identica rappresentazione nel CLR zione sum, max, min, avg), può risultare molto
possono essere impostati come tali. più semplice ed il risultato molto più veloce e
Impostando tale attributo a Format.UserDefined meno pesante in termini di risorse.
è allora necessario implementare anche l’inter- Oggi, l’integrazione con il Framework ci forni-
faccia IBinarySerialize con i suoi membri Read sce questo nuovo modo per aggregare dati.

N. 64 - Luglio/Agosto 2005 VBJ 37


DATABASE

select DepartmentID, dbo.UDA_csv(NationalIDNumber) as


Listato 5 Validazione di un indirizzo e-mail tra-
myCSV
mite un’apposita regular-expression
from HumanResources.Employee
group by DepartmentID
using System;
using System.Text.RegularExpressions; User Defined Function
using System.Data.Sql;
using System.Data.SqlTypes;
L’ultimo esempio che vediamo tratta la defi-
public partial class UserDefinedFunctions nizione di una UDF. Apriamo con Visual Stu-
{ dio una nuova classe, selezionando l’elemen-
[SqlFunction]
public static SqlInt16 UDF_regex(string eMail)
to “User-Defined Function”, che chiameremo
{ “UDF_regex.cs”. Dicevamo poco sopra che
string regExEmail = @”\w+([-+.]\w+)*@\w+([- uno dei principali vantaggi dell’integrazione
.]\w+)*\.\w+([-.]\w+)*”; con il CLR è sicuramente il fatto di poter utiliz-
Regex re = new Regex(regExEmail);
zare classi e funzioni che il framework offre.
if(re.IsMatch(eMail)) Potremmo quindi implementare una soluzio-
{ ne che utilizzi le classi che il namespace Sys
return 1; tem.Security.Cryptography offre, oppure po-
}
else tremmo scrivere una funzione che validi, tra-
{ mite un’apposita regular-expression, un indi-
return 0; rizzo e-mail (Listato 5). Eseguiamo il deploy
} dell’applicazione e proviamola tramite due
}
}; semplici istruzioni T-SQL:

select dbo.UDF_regex(‘aaa’) as isValidEmail


select dbo.UDF_regex(‘pippo@pluto.com’) as isValidEmail
Proviamo a costruire una lista di valori se-
parati da “;”, una sorta di file csv. Conclusioni
Facciamo tasto destro sul nostro progetto,
“Add”, “Aggregate” ed impostiamo il nome Come abbiamo cercato di verificare, il sup-
come “UDA_csv.cs”. porto al Common Language Runtime porta si-
Anche questa volta Visual Studio fornisce curamente vantaggi.
uno scheletro della classe che andremo a co- Detto questo dobbiamo sempre, prima di
struire grazie ai template presenti per ciascun usare codice .NET, accertarci che non esista-
oggetto costruibile. no istruzioni equivalenti in T-SQL, anche veri-
La classe, serializzabile, deve implementa- ficando le nuove estensioni e funzionalità in-
re quattro diverse funzioni: Init, Accumulate trodotte. Per quanto riguarda la definizione di
(che conterrà tutta la logica di aggregazione), tipi nelle tabelle, la comunità degli sviluppatori
Merge, Terminate (Listato 4). e dei DBA ha iniziato da un po’ di tempo una
discussione (assai utile) sul loro uso.
L’utilizzo del Framework potrebbe essere inu-
Prima di questa versione tile quando il tipo da costruire ha un solo va-
lore. Ad oggi il problema maggiore riguarda
l’unico modo per scrivere lo- le indicizzazioni e le ricerche, così come sono
gica lato server era di costrui- sempre da valutare la flessibilità offerta e la
possibilità di ridefinire gli operatori.
re stored procedure estese
Insomma la discussione continua.

Riferimenti
Una volta eseguito il deploy del nostro pro-
getto possiamo eseguire, tramite una nuova [1] http://www.microsoft.com/sql/2005/
query nel SQL Server Management Studio (Fi- default.asp
gura 4), la nostra funzione: [2] newsgroup: microsoft.public.it.sql

38 VBJ N. 64 - Luglio/Agosto 2005


SOFTWARE ENGINEERING

I design pattern più


famosi implementati
in VB.NET
Il pattern Factory Method consente di delegare a una sottoclasse la
DP 3
scelta di quale oggetto istanziare

di Lorenzo Vandoni Terza puntata

I
n questa breve serie di articoli abbiamo finora esa- possibilità alle sottoclassi di A
minato i pattern Iterator e Observer, due tra i più di modificare il tipo dell’ogget-
famosi ed utilizzati tra quelli descritti nel libro “De- to creato. Alcuni esempi pos-
sign Pattern” di Gamma, Helm, Johnson e Vlissides. sono essere quello di una ge-
Questa puntata è dedicata ad un pattern meno noto, nerica collezione con un me-
chiamato Factory Method. todo che restituisca un itera-
tore, oppure quello di un og-
Il pattern Factory Method getto che rappresenti una con-
nessione a un database, con
Anche in questo caso la descrizione del pattern un metodo che restituisca un
verrà suddivisa in tre paragrafi, che evidenziano ri- oggetto command.
spettivamente lo scopo, le motivazioni e l’applicabi-
lità del pattern. • Applicabilità. Il pattern è appli-
cabile in tutti quei casi in cui il
• Intent. Lo scopo del pattern è quello di definire tipo dell’oggetto creato non è
una generica interfaccia che permetta la creazio- prevedibile a priori, oppure lo è
ne di un oggetto, delegando però alle sottoclassi ma si vuole offrire la possibilità
la scelta di quale tipo di oggetto istanziare. di crearne delle sottoclassi.

• Motivation. Questo pattern può essere utile so- Soluzione del problema
prattutto per chi si occupa della realizzazione di
framework o librerie di classi. In questi casi può La soluzione del problema è co-
capitare la necessità di definire, in una classe A, stituita dal diagramma di classi
dei metodi di creazione (metodi, cioè, che resti- mostrato in Figura 1. I paragrafi
tuiscano come risultato della loro esecuzione un seguenti ne forniscono una de-
riferimento ad un nuovo oggetto), offrendo però la scrizione più dettagliata.

• Structure. Le classi Product e


Lorenzo Vandoni è laureato in Informatica, ed è uno spe- Creator sono quelle che fanno
cialista di progettazione e sviluppo con tecniche e linguaggi
parte dell’ipotetico framework.
object-oriented. Ha collaborato alla realizzazione di framework,
strumenti di sviluppo e software commerciali in C++, Java e La classe Creator fornisce un
Visual Basic. Può essere contattato tramite e-mail all’indirizzo metodo FactoryMethod che si
vandoni@infomedia.it. preoccupa di creare un generi-

N. 64 - Luglio/Agosto 2005 VBJ 39


SOFTWARE ENGINEERING

co Product. Le sottoclassi di Creator hanno la di una classe più specifica, in questo caso
facoltà di fornire una versione specializzata di ConcreteProduct. Il FactoryMethod, ovvia-
FactoryMethod, che crea un oggetto più spe- mente, mantiene la stessa signature, e in
cifico, ConcreteProduct, derivato da Product. particolare continua a restituire un generi-
co riferimento ad un oggetto Product.
• Participants. Più in dettaglio, queste sono le re-
sponsabilità delle interfacce e classi coinvolte: • La classe ConcreteProduct deriva da
Product e rappresenta un caso concre-
• Creator è una classe base, che definisce to di oggetto creato dal FactoryMethod
il FactoryMethod. A seconda delle circo- (ad esempio, un iteratore forward only o
stanze, si potrà trattare di una vera clas- un comando per SQL Server).
se base, di una classe astratta o di una
semplice interfaccia. Questo dipende più
che altro dal fatto che la classe Product • Collaborations. La struttura dinamica del
abbia una semantica specifica, o possa pattern è molto semplice, e non vi sono par-
a sua volta essere considerata alla stre- ticolari collaborazioni da evidenziare tra gli
gua di semplice interfaccia. oggetti coinvolti. Semplicemente, un ogget-
to ConcreteCreator si preoccuperà di istan-
• La classe Product è una classe base che ziare un oggetto ConcreteProduct all’inter-
rappresenta un generico oggetto creato dal no della sua implementazione del metodo
FactoryMethod. Anche in questo caso si può FactoryMethod.
trattare di una vera classe, di una classe
astratta o di una interfaccia. Nel caso di col- Conseguenze e benefici
lezioni ed iteratori, ad esempio, può avere
senso creare un generico iteratore concre- Vediamo ora quali possono essere i punti di
to, con funzionalità di base applicabili a tut- forza della soluzione ed alcune sue possibili ap-
ti i tipi di collezioni. Nel caso di connessioni plicazioni pratiche:
e comandi, al contrario, non ha senso im-
plementare un generico comando. In ogni • Consequences. Il principale vantaggio è dato
caso, Product dovrà però definire i metodi dalla possibilità di aggiungere nuove classi
corrispondenti alla semantica dell’oggetto concrete come sottoclassi di Creator e Pro-
implementato (ad esempio, un iteratore o un duct, senza modificare la struttura del fra-
comando). Tali metodi verranno poi even- mework, e soprattutto senza dover modifi-
tualmente sovrascritti dalle classi derivate. care il codice delle applicazioni client.

• La classe ConcreteCreator deriva da • Known uses. Questo pattern è usato in molti


Creator e sovrascrive il metodo Factory- framework commerciali ed open source. Un
Method, in modo da ritornare un’istanza esempio di utilizzo che può risultare molto fa-
miliare ai lettori di questa rivista
è costituito dal metodo Create-
Command definito nell’interfac-
cia IDbConnection. Questo me-
todo permette di creare un ge-
nerico comando IDbCommand
a partire da una generica con-
nessione, e viene implemen-
tato da parte di tutte le classi
che implementano l’interfaccia
IDbConnection, come ad esem-
pio OleDbConnection e Sql-
Connection. Ovviamente, ogni
Figura 1 Architettura del pattern Factory Method connessione creerà un’istanza
della classe comando definita
dal proprio provider.

40 VBJ N. 64 - Luglio/Agosto 2005


SOFTWARE ENGINEERING

sti messaggi creando dei viewer specifici, ovve-


Implementazione del pattern in VB.NET ro due istanze di PersonViewer. A queste istanze
vengono quindi passati due riferimenti a due di-
Come esempio di implementazione consideria- versi TextBox, che verranno utilizzati per eseguire
mo il caso di un generico oggetto i cui dati deb- la visualizzazione. La chiamata al metodo Paint,
bano essere visualizzati all’interno di un generi- infine, permette di eseguire la visualizzazione.
co dispositivo di output. Per semplicità, restrin-
geremo la casistica dei possibili dispositivi di ou- Conclusioni
tput ai soli controlli che possano essere disposti
su una Windows Form. Il fatto di utilizzare due Il codice dell’esempio è mostrato a titolo di
classi diverse per gestire la memorizzazione dei esempio e sicuramente migliorabile, ma nella
dati e la visualizzazione degli stessi è una buona sua semplicità permette comunque di mostrare
norma di progettazione, che tra l’altro abbiamo un esempio di applicazione del pattern Factory
già visto formalizzata all’interno del pattern Ob- Method, e come sia possibile utilizzare contem-
server. Poiché la logica di visualizzazione dipen- poraneamente due o più pattern.
de, oltre che dal tipo di dispositivo, anche dal In questo caso specifico, il pattern Factory
tipo di dati da visualizzare, è corretto assegnare Method viene utilizzato per delegare ad una
all’oggetto visualizzato la responsabilità di creare sottoclasse la scelta di quale oggetto istanziare
il suo visualizzatore. Nell’esempio vengono quin- in risposta all’esecuzione di un metodo specifi-
di definite due classi base, GenericViewer e Ge- co, mentre Observer viene utilizzato per sepa-
nericObject, il cui codice viene mostrato nel Li- rare la logica applicativa dalle modalità di visua-
stato 1. GenericViewer rappresenta un oggetto lizzazione. Come conseguenza dell’applicazione
Product, mentre GenericObject è il Creator. Per del primo pattern, siamo riusciti a creare un’ar-
ognuna di queste due classi base viene definita chitettura di base, costituita delle classi Gene-
una sottoclasse, per rappresentare un caso con- ricObject e GenericViewer, che permette di im-
creto: Person è una sottoclasse di GenericObject, plementare diversi tipi di classi derivate mante-
e viene utilizzata per mantenere dati personali; nendo la stessa interfaccia funzionale.
PersonViewer è uno specifico visualizzatore, che Un’importante conseguenza dell’applicazione
“sa” come mostrare i dati personali su un gene- del pattern Observer, invece, è costituita dal-
rico control. L’esempio è completato da una form la possibilità di avere più viewer separati per lo
con un pulsante alla cui pressione viene creato stesso oggetto. Factory Method è un Creational
un oggetto Person, a cui viene richiesto di crea- Pattern, ovvero un pattern che incapsula una so-
re due viewer. L’oggetto Person risponde a que- luzione di progettazione per un problema relati-
vo all’istanziazione di
uno o più oggetti. I
Listato 1 Implementazione delle interfacce del pattern in VB.NET pattern esaminati ne-
gli articoli precedenti,
Iterator e Observer,
sono invece catego-
‘la classe generic viewer rappresenta un generico Product
Public Class GenericViewer
rizzati come Beha-
Private moDevice As Control vioral pattern. Nel-
Private moSubject As GenericObject la prossima puntata
Public Overridable Sub Paint() mostreremo, come
moDevice.Text = moSubject.ToString
End Sub ultimo esempio di
End Class implementazione,
Adapter, un pattern
‘la classe generic viewer rappresenta un generico Creator di tipo Strutturale,
Public Class GenericObject
Protected moViewers As System.Collections.ArrayList ovvero un esempio
Public Overridable Function CreateViewer() As GenericViewer appartenente alla
Dim oViewer As New GenericViewer terza ed ultima delle
moViewers.Add(oViewer)
categorie di pattern
Return oViewer
End Function presentate all’inter-
End Class no del libro Design
Pattern.

N. 64 - Luglio/Agosto 2005 VBJ 41


TECNICHE

Verificare
la disponibilità
di un control OCX
con VB6
A volte è necessario che un programma si comporti in modo diverso a seconda della
disponibilità di un determinato control OCX

di Lorenzo Vandoni

S
upponiamo di voler scrivere un programma Basterebbe infatti utilizzare la
che utilizzi un controllo OCX, senza sapere se seguente istruzione:
questo sarà effettivamente disponibile su tut-
te le macchine sulle quali il programma dovrà esse- CreateObject(“NomeDll.NomeClasse”)
re installato.
I motivi per cui una situazione del genere si può ve- Questa istruzione, nel caso in
rificare sono molti. cui l’oggetto in questione non
Il control in questione potrebbe costituire una fun- esista o non sia stato registra-
zionalità separata, disponibile a pagamento, oppure to, restituisce un errore del
potrebbe essere un control di terze parti, da acqui- tipo ActiveX component can’t
stare separatamente. create object. Sarà quindi suf-
Indipendentemente dal motivo per cui una situa- ficiente intercettare l’errore con
zione del genere si possa verificare, è interessante un’istruzione del tipo On Error
studiare una soluzione per questo problema perché, GoTo per poter prendere le con-
come vedremo, nonostante sia piuttosto semplice, tromisure desiderate.
non è del tutto ovvia. Con un control OCX que-
sta tecnica però non è appli-
La differenza tra OCX e DLL cabile.
Un control normalmente vie-
Se il componente non fosse implementato come ne aggiunto staticamente alla
controllo OCX, ma come ActiveX DLL, la soluzione form, in fase di design, e in ogni
al problema sarebbe molto semplice. caso non può essere creato con
un’istruzione CreateObject.
Nel caso il control sia aggiun-
Lorenzo Vandoni è laureato in Informatica, ed è uno spe- to staticamente alla form, però,
cialista di progettazione e sviluppo con tecniche e linguaggi
qualora l’applicazione venga in-
object-oriented. Ha collaborato alla realizzazione di framework,
strumenti di sviluppo e software commerciali in C++, Java e stallata su una macchina dove
Visual Basic. Può essere contattato tramite e-mail all’indirizzo quel control non sia disponibile,
vandoni@infomedia.it. si avrà un errore a runtime.

42 VBJ N. 64 - Luglio/Agosto 2005


TECNICHE

La soluzione time senza referenziarli all’interno del progetto.


Per poterlo fare, però, occorre affrontare il pro-
La soluzione al problema consiste nell’ag- blema delle licenze.
giunta del control a runtime, cosa che può es-
sere fatta col metodo Add, applicato alla col- Il problema delle licenze
lection Controls della form. Per aggiungere a
runtime un control di tipo Label, ad esempio, Alcuni control ActiveX sono protetti da una li-
si può scrivere: cenza, che consente il loro utilizzo solo da parte
degli sviluppatori che li hanno acquistati.
myForm.Controls.Add “VB.Label”, “MyLabel” Per uno sviluppatore Visual Basic, la gestione
delle licenze è normalmente del tutto trasparen-
In questa istruzione, MyLabel costituisce il te, essendo sufficiente aggiungere il control alla
nome del nuovo control, che sarà quindi ac- form per poterlo utilizzare.
cessibile scrivendo: In questo caso, infatti, le informazioni di licenza
vengono automaticamente incluse all’interno.
myForm.Controls.Item(“MyLabel”) Nel caso si voglia però creare il control a run-
time, senza referenziarlo all’interno del progetto,
occorre tenere conto di questo aspetto.
Fortunatamente, la procedura da adottare non
Se il componente fosse è troppo complicata.
L’elenco delle licenze disponibili all’interno
implementato come Acti- dell’applicazione, è disponibile nella collezione
veX DLL la soluzione sa- Licenses. Per aggiungere una nuova licenza, è
quindi sufficiente scrivere:
rebbe molto semplice
Call Licenses.Add(“NomeDll.NomeControl”)

Nel caso in cui il control non sia correttamen-


Per poter utilizzare questa tecnica bisogna te installato e “licenziato” sulla macchina sul-
però fare attenzione ad un paio di partico- la quale l’applicazione viene eseguita, questa
larità: istruzione provocherà un errore a runtime, che
potrà essere intercettato in modo da eseguire
• prima di tutto, il ProgId, ovvero il tipo del le azioni opportune.
control, specificato come primo argomento
della chiamata al metodo Add, non sempre
coincide con il nome del control stesso.
Tanto per fare un esempio concreto, esiste un Un control normalmente
control di tipo DNSToolsCtl.DgnVoiceTxt, che
ha come ProgId la stringa Dragon.VTxtCtrl.1.
viene aggiunto
Fortunatamente Visual Basic viene in aiuto staticamente alla form
in questo frangente, segnalando il corret-
to valore del ProgId all’interno della descri-
in fase di design
zione dell’errore che si verifica tentando di
creare il control utilizzando come ProgId il
suo tipo.
Conclusioni
• la creazione del control, in ogni caso, può
fallire se non si aggiunge il control al proget- Il problema presentato è piuttosto partico-
to, e non si deseleziona l’opzione Remove lare, e probabilmente a molti sviluppatori non
information about unused ActiveX controls, capiterà mai di verificarlo. La ricerca di una
visibile tra le proprietà del progetto. soluzione ha permesso però di evidenziare al-
cune caratteristiche interessanti e non mol-
In alternativa a quest’ultima opzione, esiste to conosciute legate alla gestione dei con-
anche la possibilità di creare dei control a run- trol ActiveX.

N. 64 - Luglio/Agosto 2005 VBJ 43


APPLICATIVI

Controllo Remoto in
Visual Basic .NET
Prima puntata
Remote

di Stefano Corti


I
l linguaggio Visual Basic .NET può essere defini- Trasferire file dalla macchina
to a tutti gli effetti un moderno linguaggio di pro- locale alla macchina remota
grammazione orientato agli oggetti. Al pari di C# e e viceversa.
di Visual C++ .NET, costituisce parte integrante della • Ottenere le schermate video del
nuova piattaforma .NET di Microsoft che, insieme al- computer remoto.
l’ambiente di sviluppo Visual Studio, rappresenta un • Ottenere informazioni su alcuni
importante e raffinato riferimento per tutti i program- aspetti dell’hardware del com-
matori che desiderano sviluppare applicazioni per i puter remoto.
sistemi operativi Microsoft Windows. • Remotizzare in real-time la shell
Queste pagine costituiscono il primo di una serie di di DOS.
appuntamenti dedicati alla progettazione e allo svilup- • Operare sul registry del com-
po di un’applicazione completa in Visual Basic .NET. puter remoto.
Più precisamente spiegheremo in dettaglio i processi
per sviluppare un programma che consente di con- Analizzeremo in dettaglio alcu-
trollare, attraverso una rete basata su protocolli TCP/ ne importanti classi raggruppate
IP, una macchina remota. Realizzeremo quindi due di- in specifici namespace e ci de-
stinti applicativi; uno denominato “Controller” desti- dicheremo alla creazione di nuo-
nato a girare sul PC controllante ed un secondo ap- ve classi istanziabili per rendere
plicativo che chiameremo “Target” che dovrà essere operative le funzioni proposte. I
eseguito sulla macchina che ci prefiggiamo di con- programmi presentati non sono
trollare. Con l’applicazione che ci accingiamo ad illu- destinati ovviamente ad ambien-
strare potremo eseguire le seguenti operazioni: ti di produzione, ma ci offriranno
l’occasione di soffermarci attenta-
• Sessioni di chat-line tra Controller e Target in varie mente su alcuni importanti aspet-
modalità di dialogo. ti del linguaggio e della Class Li-
• Esplorare le unità logiche e le cartelle del compu- brary del .NET framework.
ter remoto.
• Creare nuove cartelle, rinominare e cancellare file Introduzione ai Socket TCP
sul computer remoto. e alle classi del namespace
System.Net.Sockets
Stefano Corti si occupa di programmazione PHP e JSP lato La nostra applicazione preve-
server, della piattaforma .NET e di integrazione di sistemi
de un modello progettuale di tipo
legacy con le nuove realtà del web, soprattutto in ambito ge-
stionale, bancario e finanziario. È attualmente alle dipendenze “Client/Server”.
di un primario gruppo bancario italiano. Può essere contattato Il programma Target in esecu-
tramite e-mail all’indirizzo scorti@infomedia.it. zione sulla macchina remota, è

44 VBJ N. 64 - Luglio/Agosto 2005


APPLICATIVI

a tutti gli effetti un server in ascolto su una dura Sub richiesta, che nel nostro caso risulta
determinata porta, pronto ad accettare le con- essere runServerTarget().
nessioni e le richieste di un client che, nel no-
stro caso, è costituito dal programma Controller. rThread = New Thread(AddressOf runServerTarget)
Utilizzeremo TCP come protocollo di traspor- rThread.Start()
to in quanto, a differenza dei socket di data-
gramma basati su UDP, è fortemente orienta- Si noti la presenza dell’operatore AddressOf
to alla connessione tra processi in esecuzione, che consente di creare un delegato di funzio-
laddove vi è un effettivo controllo della corretta ne che fa riferimento alla funzione specifica-
consegna dei pacchetti inoltrati. Dovremo suc- ta nell’argomento. Nel nostro caso si tratta di
cessivamente sviluppare un protocollo applica- una procedura Sub. Quindi invochiamo il me-
tivo basato su opportuni comandi a stringa li- todo Start() che innesca l’avvio del Thread. È
bera che verranno inviati dal Controller e che importante precisare che queste linee di codi-
dovranno essere comprensibili dal Target affin- ce devono essere collocate all’interno del co-
ché possa adeguatamente eseguire le opera- struttore della Form1 Sub New(), subito dopo
zioni richieste, al pari di quanto avviene con i la chiamata a InitializeComponent(), affinchè il
comandi di protocolli standard quali ad esem- Thread contenente la procedura runServerTar-
pio RETR, TOP, NOOP, DELE nel caso del pro- get() venga avviato nel momento in cui la Form
tocollo POP3 oppure GET e POST nel caso di viene caricata e inizializzata. In seguito tornere-
HTTP. Sceglieremo quindi uno o più numeri di mo maggiormente in dettaglio su questi aspetti
porta non assegnati da IANA (0 - 1023 well- delle procedure multi-threading. Ora focalizzia-
known port numbers) per mettere in ascolto i mo la nostra attenzione sulla progettazione det-
vari servizi esposti dall’applicativo Target. Os- tagliata del server di base, insieme al relativo
serviamo il Listato 1. client. Per prima cosa dobbiamo fare in modo
Per prima cosa dobbiamo importare nel pro- che l’indirizzo IP della macchina Target venga
getto alcuni namespace che contengono le clas- reso noto all’utente, affinchè possa essere co-
si richieste. Il namespace System.Net.Sockets municato al Controller, laddove il provider di
espone un certo numero di classi per la fun- connettività fornisca una soluzione basata sul-
zionalità di networking, mentre il namespace l’assegnazione di IP dinamici. Per ottenere l’in-
System.Threading contiene classi specifiche per dirizzo IP del computer Target, definiamo una
la definizione di procedure multi-thread. Abbia- particolare funzione che chiameremo getLoca-
mo scelto la porta TCP 13000 per porre in stato lIp() che riportiamo integralmente.
di ascolto il server di base, la cui procedura di
definizione è localizzata nella Sub runServerTar- Private Function getLocalIp() As Strin
get(). Il server di base ci consentirà di interpre- Dim HostName As String
tare ed eseguire la maggior parte dei comandi Dim indiIp As String
impartiti dal controller, per lasciare a procedure HostName = System.Net.Dns.GetHostName()
specifiche le funzionalità di trasferimento file e indiIp = System.Net.Dns.GetHostByName(HostName).
remotizzazione desktop Target. Per questi par- AddressList(0).ToString()
ticolari aspetti utilizzeremo altri numeri di porta. Return indiIp
Poiché è importante che i server in ascolto sul- End Function
le diverse porte siano in qualche modo sincro-
nizzati e contemporaneamente attivi, dovremo La funzione ritorna un valore stringa indiIp
inserire le relative procedure Sub in altrettanti contenente l’indirizzo IP della macchina sulla
Thread separati, il cui ciclo di vita deve essere quale è in esecuzione. La classe Dns, appar-
inizializzato nel momento opportuno. Per ora tenente al namespace System.Net, espone il
occupiamoci del Thread principale, al cui in- metodo GetHostName() che consente di otte-
terno è in funzione il server sulla porta 13000. nere il nome host del computer locale, passa-
Prima di tutto dobbiamo istanziare un nuovo to alla variabile stringa HostName. Tale stringa
oggetto Thread che chiameremo rThread. De- viene poi passata come parametro del meto-
finiamo rThread privato in quanto non vogliamo do GetHostByName(); tale metodo esegue una
che sia visibile all’esterno della classe Form1 e query nel server Dns per ottenere informazio-
poi creiamo un oggetto rThread passando come ni sull’Host specificato. Il valore ritornato è di
parametro del costruttore il nome della proce- tipo IPHostEntry ed è relativo all’omonima clas-

N. 64 - Luglio/Agosto 2005 VBJ 45


APPLICATIVI

se; tale classe presenta il metodo AddressList() Premendo un apposito pulsante il nostro indiriz-
che recupera un array di tipo IPAddress con- zo IP verrà anche visualizzato in un TextBox.
tenente una lista di indirizzi IP risolti a partire
dal nome Host (nel nostro caso utilizziamo l’in- Private Sub showIpButton_Click(ByVal sender As
dice 0 dell’array in quanto dobbiamo ottenere System.Object, ByVal e As System.EventArgs)
un solo indirizzo di base). Il metodo ToString(), Handles showIpButton.Click
infine, converte il tipo ritornato in una stringa e ipText.Text = “Indirizzo IP da comunicare al
lo assegna alla variabile (string) indiIp. Ovvia- Controller: “ & getLocalIp()
mente l’intera funzione verrà dichiarata privata End Sub
in quanto non vi è ragione per renderla visibi-
le all’esterno della nostra classe base Form1. La funzione getLocalIP() ci servirà anche per
Provvediamo quindi a visualizzare l’informazio- passare il valore dell’indirizzo IP alle relative
ne ottenuta nella barra del titolo del nostro ap- linee di codice che implementano un sempli-
plicativo Target. ce sistema di autenticazione essenzialmente
basato su controlli di assegnazione di indiriz-
Me.Text = “Remote Administrator - Target - Indirizzo IP: zi IP, nome utente e relativa password. Questi
“ & getLocalIp() aspetti li vedremo dettagliatamente in seguito.

Listato 1 Struttura base del server

Imports System.IO
Imports System.Threading
Imports System.Net.Sockets

Private rThread as Thread


Private writer As BinaryWriter
Private reader As BinaryReader
Private socketStream As NetworkStream
Private connection As Socket

Public Sub New()


MyBase.New()
InitializeComponent()
rThread = New Thread(AddressOf runServerTarget)
rThread.Start()
End Sub

Public Sub runServerTarget()


Dim ip As String = getLocalIp()
Me.Text = “Remote Administrator - Target - Indirizzo IP: “ & getLocalIp()
Dim listener As TcpListener
Try
listener = New TcpListener(13000)
listener.Start()
While True
inCommands.Clear()
inMessages.Clear()
outMessages.Clear()
inCommands.Text &= “PC pronto per accettare connessioni da Controller” & vbCrLf
connection = listener.AcceptSocket()
socketStream = New NetworkStream(connection)
writer = New BinaryWriter(socketStream)
reader = New BinaryReader(socketStream)
inCommands.Text &= “Connessione accettata...” & vbCrLf
...
End While
...
End Try

46 VBJ N. 64 - Luglio/Agosto 2005


APPLICATIVI

Proseguiamo ora nel-


la spiegazione del no- Listato 2 I blocchi che intercettano le eccezioni nello scambio dei dati

stro server. Procedia-


mo quindi alla crea-
zione di un oggetto Try
Listener di tipo TcpLi- ...
While True
stener e inizializzia-
...
mo la nuova istanza Try
della classe passando ...
al costruttore il nume- Catch inputOutputException As IOException
sentText.Text &= “Interruzione nel trasferimento dati.”
ro di porta sulla quale sentText.Text &= vbCrLf & “In attesa di riconnessione da Controller.”
desideriamo eseguire Finally
la sessione di ascolto inCommands.Text &= vbCrLf & _
del server. “SESSIONE CHIUSA DA CONTROLLER.” & vbCrLf
writer.Close()
Nel nostro caso reader.Close()
si tratta della por- socketStream.Close()
ta 13000. La classe connection.Close()
TcpListener fornisce End Try
End While
metodi che attendono Catch inputOutputException As IOException
e accettano richieste MessageBox.Show(“Eccezione Intercettata: “ & _
di connessioni in in- inputOutputException.Message, “Eccezione Sollevata”, _
gresso in modalità di MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
blocco sincrona.
Più precisamente il
metodo Start() inizia-
lizza il socket sottostante, lo associa al punto (writer e reader) istanziati da due classi che for-
finale locale e attende tentativi di connessione niscono metodi specifici per consentire la scrit-
in ingresso. tura e la lettura dei flussi binari.
Quindi, all’interno di un ciclo While infinito, in- Non appena il server riceve una richiesta di
vochiamo il metodo AcceptSocket() dell’oggetto connessione da parte del client, rappresentato
Listener e ne passiamo il valore restituito alla dal programma Controller, è possibile scrivere
variabile privata connection di tipo Socket. Ac- tutto il codice che ci serve per elaborare i dati
ceptSocket() è un metodo di blocco che resti- ricevuti. Abbiamo detto che il protocollo applica-
tuisce un socket che è possibile utilizzare per tivo che andremo ad implementare è basato su
inviare e/o ricevere dati. comandi stringa formattati secondo precisi pa-
Ora che abbiamo ottenuto un canale di comu- rametri, ragione per cui è necessario estrarre le
nicazione, dobbiamo preparare il socket per vei- stringhe veicolate dal flusso binario che abbia-
colare i dati richiesti sotto forma di stream bina- mo a disposizione per mezzo dei metodi appli-
ri di byte. Dunque creiamo un oggetto Network- cati agli oggetti writer e reader rispettivamente
Stream – che chiameremo socketStream – e lo di tipo BinaryWriter e BinaryReader.
inizializzeremo passando al relativo costruttore A tal fine, utilizziamo il metodo ReadString(),
il valore di connection. Sono necessari infine al- che legge e restituisce dal flusso corrente una
tri due oggetti; uno di tipo BinaryWriter e uno di stringa avente un prefisso di lunghezza e codi-
tipo BinaryReader, che eseguono rispettivamen- ficata come numero intero, 7 bit alla volta.
te la scrittura e la lettura di tipi primitivi in codi- La stringa ottenuta può così essere analizzata
ce binario in un particolare flusso. Il costruttore per mezzo di semplici meccanismi di manipo-
di queste due particolari classi è un costrutto- lazione di stringhe e i comandi inviati possono
re di overload che accetta anche valori di tipo essere così ricostruiti ed eseguiti.
NetworkStream.
La classe NetworkStream, lo ricordiamo, for- Dim reply As String
nisce il flusso di dati sottostante per l’accesso reply = reader.ReadString()
alla rete e per garantire il transito di dati all’in-
terno del socket. In questo modo abbiamo otte- È importante osservare che tutto il codice di
nuto l’apertura di un socket TCP e due oggetti implementazione del server viene inserito in due

N. 64 - Luglio/Agosto 2005 VBJ 47


APPLICATIVI

cicli While End While e Do Loop While nidifica- ta la connessione fra gli end-point. Il secondo
ti. Il ciclo while più esterno costituisce in realtà blocco invece si occupa di intercettare eccezio-
un loop senza fine (While True) che viene in- ni generate da errori durante la lettura dei dati.
terrotto nel caso in cui venga generata ed inter- In questo specifico caso, dal momento che la
cettata un’eccezione. Il ciclo Do Loop While più connessione è già stabilita, includiamo nel bloc-
interno invece consente di stabilizzare il server co Finally tutto il codice che ci serve per rila-
affinchè sia in grado di continuare a ricevere i sciare le risorse occupate, invocando il meto-
comandi dal Controller. do close() per gli oggetti writer, reader, socket-
Tale ciclo si ripete a meno che venga ricevu- Stream e connection (Listato 2).
to il comando TERMINATE e finchè la proprietà Un’analoga struttura di intercettazione di pos-
connected di connection (che abbiamo visto es- sibili eccezioni di tipo IOException dovrà esse-
sere un oggetto istanziato dalla classe Socket ) re implementata a livello client sull’applicativo
restituisce True. Notiamo che l’intera struttura Controller.
viene inserita in un blocco Try Catch End Try
all’interno del quale vi è un blocco Try Catch Realizzazione dell’architettura Client-Server
Finally EndTry. e implementazione del programma Controller
Abbiamo previsto il primo blocco per intercet-
tare possibili eccezioni sollevate nel caso si do- Vediamo ora come realizzare una vera e pro-
vesse verificare un errore mentre viene stabili- pria struttura di codice in grado di svolgere sem-
plici transazioni basa-
te sul modello Client-
Listato 3 Struttura del client
Server. Abbiamo vi-
sto, di massima, come
realizzare il program-
ma server. Ora ci con-
Private output As NetworkStream centreremo sulla pro-
Public Shared reader As BinaryReader
gettazione del client e
Public Shared writer As BinaryWriter
vedremo come i due
Public Sub runClientController() sistemi possono dia-
Dim client As TcpClient logare. È importan-
Try
Dim job As String() = Nothing
te capire che ciò che
Dim ip As String = inIP.Text dovremo essere in
Dim port As Int32 = CInt(inPort.Text) grado di estrarre dal-
client = New TcpClient() la sequenza binaria
client.Connect(ip, port)
output = client.GetStream()
in transito sul socket
writer = New BinaryWriter(output) aperto è sempre una
reader = New BinaryReader(output) sequenza di stringhe
inMessages.Text &= vbCrLf & “Ricevuto flusso I/O” & vbCrLf alfanumeriche, che
Try
Do contengono i coman-
messaggio = reader.ReadString() di del nostro protocol-
job = messaggio.Split(delimiter, 2) lo. Di fatto, per sem-
txtMessages.Clear() plificare, dovrà esse-
If (job(0) < > “REPLYDRIVESSEQUENCE” And _
job(0) < > “REPLYREMOTEFIL” And _ re possibile eseguire
job(0) < > “READYTORECEIVEFILE” And _ una sessione telnet
job(0) < > “REPLYREMOTEDIR” And _ sulla porta 13000 del
job(0) < > “REPLYBASESUBKEYS” And _
server in esecuzione
...
e quindi potere esse-
inMessages.Text &= vbCrLf & “Messaggio ricevuto da Macchina Target: “ & _ re in grado di esegui-
vbCrLf & messaggio re alcune transazioni
End If
semplicemente scri-
If job(0) = “ESTABILISHED” Then vendo i comandi di-
If job(1) = “AUTHREQUIRED” Then rettamente nel prompt
.... cmd.exe Osserviamo
il Listato 3.

48 VBJ N. 64 - Luglio/Agosto 2005


APPLICATIVI

Tabella 1 Alcuni comandi estraibili dallo stream binario

IPCOMINGFROM>192.168.201.10 Fornisce al server l’indirizzo IP del Controller

IDCOMINGFROM>CONTROLLER Fornisce al server l’identità alfanumerica del Client

ESTABILISHED>NOAUTH Avvisa di un errore nella fase di autenticazione

ESTABILISHED>OKAUTH Comunica esito positivo durante la fase di autenticazione


Trasmette a Target un messaggio di testo con risposta
TEXTWITHREPLY>ARGOMENTO
obbligatoria
GETREMOTEMACHINEDATAS>NOARGS Richede a Target dati Hardware/Software macchina remota
Richiede a Target elenco unità logiche e partizioni su mac-
GETDRIVES>NOARGS
china remota
Avvia il processo associato al file selezionato il cui percorso
STARTSELECTEDFILE>ARGOMENTO
assoluto è rappresentato dalla stringa argomento
Imposta la chiave Hive specificata nella stringa argomento nel
SETREGISTRYBASEKEY>ARGOMENTO
registro remoto per una successiva esplorazione ed editazione
DOWNTRAY>NOARGS Minimizza l’applicativo Target nella Systray remota

Questa volta la procedura che contiene il objAu.TargetIP = “0.0.0.0”) Then


codice verrà chiamata runClientController() e MessageBox.Show(“Attenzione: Dovete inserire
sarà anch’essa inserita in un Thread. In que- un indirizzo IP valido”, _
sto caso il Thread non verrà inizializzato diret- “Messaggio di errore”, MessageBoxButtons.OK, _
tamente nel costruttore Sub New() della Form MessageBoxIcon.Error)
del programma Controller, altrimenti ad ogni av- Else
vio dell’applicativo la procedura verrà invoca- inIP.Text = objAu.TargetIP
ta e tenterà di connettersi ad un server, senza cThread = New Thread(AddressOf runClient
nemmeno dare la possibilità all’utente di forni- Controller)
re le proprie credenziali e l’indirizzo IP del ser- cThread.Start()
ver medesimo. ButtonIP.Enabled = False
Abbiamo pensato quindi di eseguire la chia- End If
mata del metodo Start() dell’oggetto cThread End Sub
all’interno della procedura Sub ButtonIP_Cli-
ck attivata in seguito allo scatenarsi dell’even- Vediamo in dettaglio la progettazione del client.
to collegato alla pressione del pulsante “Apri Creiamo subito un’istanza client della classe
Connessione”, contenuto nella scheda “Con- TcpClient. Questa importante classe espone
nessioni” della GUI. metodi per stabilire connessioni di tipo client
Come possiamo facilmente notare, il Thread per i servizi di rete Tcp.
viene inizializzato solo a condizione che sia sta- Il costruttore di questa classe è di tipo over-
to effettivamente inserito un indirizzo IP valido, load; in questo caso utilizziamo il costruttore
oppure che sia stata compilata la scheda di au- di default, omettendo l’inserimento di parame-
tenticazione. In pratica il campo di testo inIP tri nell’interno del costruttore stesso.
deve contenere l’indirizzo IP in formato stringa Uno dei metodi di base che possiamo utiliz-
e la proprietà TargetIP dell’oggetto objAu istan- zare è Connect.
ziato dalla classe AuthenticationMechanism, di Si tratta di un metodo di overload che con-
cui parleremo in seguito, non deve contenere nette un client a un host Tcp remoto utilizzan-
la default network 0.0.0.0. do il nome host e il numero di porta specifica-
ti. Il nome host (eventualmente il corrispettivo
Private Sub ButtonIP_Click(ByVal sender As System.Object, indirizzo IP della macchina remota) può essere
ByVal e As System.EventArgs) Handles passato come argomento di tipo String, men-
ButtonIP.Click tre il numero di porta deve essere necessaria-
If ((inIP.Text = “” Or inIP.Text Is Nothing) And mente espresso come Integer32.

N. 64 - Luglio/Agosto 2005 VBJ 49


APPLICATIVI

Con questi valori specificati, il programma re residenti su due diverse macchine devono
Controller sarà in grado di connettersi al ser- scambiarsi per potere comunicare ed esegui-
ver che abbiamo precedentemente sviluppato. re le operazioni richieste. Come abbiamo visto,
Istanziamo quindi un oggetto di tipo Network- tali comandi testuali vengono iniettati sotto for-
Stream e due oggetti (writer e reader) di tipo ma di stream binari nei flussi socket Tcp aperti.
BinaryWriter e BinaryReader, analogamente a Adesso vediamo come ricostruire le sequenze
quanto abbiamo eseguito durante la progetta- e determinare i vari parametri passati come ar-
zione del server Target. gomento dei vari comandi.
In questo modo siamo in grado di estrarre dal- Ricordiamo che molti protocolli in uso sulle reti
lo stream binario i comandi stringa che ci ser- TCP/IP quali FTP, HTTP, POP3, SMTP, funzio-
vono. È bene notare che, in questo contesto, nano in questo modo. Di fatto, senza utilizzare
gli oggetti reader e writer sono dichiarati pub- alcun client basato su GUI, è possibile esegui-
blici e statici. re un Telnet sulla porta 110 del server remoto
Ciò significa che potranno essere direttamente ed eseguire una sessione di consultazione dei
invocati da altre classi, senza bisogno di crea- messaggi eventualmente presenti, utilizzando il
re nuove istanze. comando RETR oppure vedere solo il campo
Di fatto questi oggetti dovranno essere ac- header del messaggio seguito da un numero
cessibili ed utilizzabili da una seconda specifi- di linee specificate (comando TOP); potremo
ca classe che si occupa della remotizzazione infine cancellare determinati messaggi trami-
dell’editor del registro, di cui parleremo diffu- te il comando DELE.
samente in seguito. La stessa cosa può essere eseguita sulla
porta 21 dove si trova in ascolto il server FTP
Definizione del protocollo di comunica- (se previsto dall’amministratore del server re-
zione e dei comandi a stringa libera moto). Abbiamo chiarito questo concetto, dal
momento che il nostro server target si com-
Ora che abbiamo visto come stabilire una con- porta nel medesimo modo.
nettività di tipo client/server tra i due nostri ap- In pratica Controller e Target si scambiano
plicativi, vediamo subito come definire un sem- continuamente informazioni tramite determinati
plice protocollo applicativo basato su opportuni comandi che ora andremo a definire.
comandi a stringa libera che le due procedu- La nostra procedura è basata appunto su
questa tipologia di architettura, ad eccezione
delle routine di trasferimento file e remotizza-
Listato 4 Assegnamento alla variabile zione schermate desktop remoto, operanti su
oFileDirName differenti porte e con differenti modalità di tra-
sferimento dati.
L’estrazione dei comandi dallo stream bina-
Imports System
Imports System.IO rio è alquanto semplice utilizzando, come ab-
biamo detto, il metodo ReadString().
Public Class RemoteStructure Ogni comando è separato dal relativo para-
metro da uno o più caratteri speciali (abbiamo
Private oFileDirName As String
Private oInformation As String utilizzato il carattere > e il carattere | ). Vedia-
Private oReturned As String mo nella Tabella 1, a puro scopo esemplifica-
Private oDrive As String tivo, alcuni comandi implementati.
Private oSequenzaDrives As String
Private oSequenzaDirFil As String
Rammentiamo che è possibile visualizzare in
Private oUnits As String() uno specifico TextBox tutti i comandi che, a
Private oDirectoryList As String() basso livello, transitano tra le due procedu-
Private oDirectoryFileList As String() re, al fine di poter comprendere agevolmente
Private oStream As StreamReader
il meccanismo di comunicazione sottostante.
Sub New() Adesso ci dobbiamo occupare di scrivere il
Me.oFileDirName = “C:\” codice che svolge le funzioni di interpretazio-
End Sub ne di questi comandi.
Sub New(ByVal FileName As String)
Me.oFileDirName = FileName Abbiamo visto che nella stringa reply si trova
End Sub la sequenza estratta contenente il comando e
gli eventuali parametri operativi.

50 VBJ N. 64 - Luglio/Agosto 2005


APPLICATIVI

Dobbiamo innanzitutto separare, a livello di ...


stringa, il comando propriamente detto dal va- End Select
lore specificato nell’argomento.
Per ottenere ciò, è sufficiente chiamare il me- Progettazione della classe RemoteStructure
todo split() della classe String. ed implementazione delle funzioni di base
Questo metodo consente di individuare le sot-
tostringhe dell’istanza interessata, che sono Ora che abbiamo visto in dettaglio come rea-
delimitate da uno o più caratteri ASCII speci- lizzare una semplice architettura basata sul mo-
ficati in una matrice, iniettando quindi le sot- dello client/server operante sui socket TCP, oc-
tostringhe restituite in una matrice di ogget- cupiamoci dell’implementazione del codice di
ti String. una delle classi base del nostro progetto. Chia-
meremo questa classe RemoteStructure e im-
Dim reply As String = “” plementeremo metodi e proprietà per le fun-
Dim delimStr As String = “>” zioni di base di file manager remoto.
Dim delimiter As Char() = delimStr.ToCharArray() Scriveremo quindi funzioni pubbliche e pri-
Dim job As String() = Nothing vate per l’esplorazione delle risorse su Target,
per ottenere una lista completa delle unità logi-
Try che (unità dischi fisici e logici) presenti sul PC
Do remoto, per eliminare file e cartelle e per otte-
reply = reader.ReadString() nere le proprietà dei file residenti sulla mac-
job = reply.Split(delimiter, 2) china da controllare.
Prima di tutto, in questa fase, è necessario
Dichiariamo job come matrice di tipo String importare nel progetto il namespace System.IO
e, dopo la chiamata del metodo Split(), otte- che contiene al suo interno un elevato numero
niamo la matrice a una dimensione dove job(0) di classi che consentono la lettura e la scrittu-
contiene la stringa del comando, mentre job(1) ra in file e flussi di dati e tipi che forniscono il
contiene la stringa del parametro passato. supporto per i file e le directory di base.
A questo punto non ci resta che analizzare Dichiariamo quindi alcune variabili di classe
il contenuto della matrice con indice 0 all’in- private. Una delle più importanti è sicuramen-
terno di una struttura Select Case End Select te oFileDirName.
per determinare le azioni da intraprendere in Si tratta di una variabile privata di tipo String
seguito alla ricezione dei comandi inviati dal che contiene il persorso assoluto di una di-
Controller ed utilizzando i valori stringa con- rectory o di un file residente su PC remoto.
tenuti in job(1) come parametri da associare Questa variabile ci servirà spesso in quasi
al comando in fase di esecuzione. tutti i metodi pubblici della classe.
Realizziamo quindi un semplice costruttore
Select Case job(0) sovraccarico che ci permette di inizializzare
Case Is = “SHOWALLRUNNINGPROCESSES” ogni oggetto di tipo RemoteStructure.
inMessages.Clear() In caso di assenza di parametro, alla variabi-
inMessages.Text = “Attivata Richiesta Processi” le oFileDirName verrà associato il valore strin-
Try ga “C:\” corrispondente alla radice dell’unità
writer.Write(objProc.displayAllProcesses()) logica C.
Catch exception As SocketException Altrimenti il costruttore potrà accettare an-
... che un parametro specificato – sempre di tipo
End Try String – consentendo, in fase di istanziazione
Case Is = “SHOWPROCESSDETAILS” dell’oggetto, di inizializzare l’oggetto stesso
inMessages.Clear() con un determinato path (Listato 4).
inMessages.Text = “Attivata Richiesta Dettagli Processi” Nella prossima puntata vedremo come defi-
Try nire i medoti e le proprietà più importanti del-
writer.Write(objProc.showProcessDetails(job(1))) la nostra classe RemoteStructure, che costi-
Catch exception As SocketException tuirà anche una sorta di classe di base da cui
... deriveranno, con il meccanismo dell’eredita-
Catch e As Exception rietà, altre importanti classi funzionali al no-
End Try stro progetto.

N. 64 - Luglio/Agosto 2005 VBJ 51


I MITI

XML Class Generator


Come utilizzare il potente motore di scripting di Windows per pro-
durre ed istanziare classi COM completamente costruite a runtime,
per usufruire di tante interessanti potenzialità

Generator
di Vito Vessia

L
a metafora dell’XML come linguaggio dei lin- zare un generatore automatico di
guaggi e come mezzo universale di interscambio classi VBScript che rimappino (e
delle informazioni è stata ironicamente descritta ne facciano da proxy) la struttu-
nella premessa di [1] come “XML has replaced Java, ra di un file XML qualunque, pur-
Design Patterns, and Object Technology as the sof- chè dotato di schema (interno o
tware’s industry solution to world hunger”… Al di là esterno). In realtà la struttura del
delle battute è indubbia l’importanza che ha assun- generatore di classi è semplice,
to ormai questo semplice e quasi banale formato di ma alquanto complessa da spie-
definizione dei dati nel mondo della comunicazione gare in modo astratto e sintetico
moderno. XML, nato come linguaggio di formattazio- in questa premessa.
ne dei contenuti, direttamente discendente da SGML, Quindi non vi resta che leggere
si è poi imposto come esperanto per qualsiasi infor- il seguito…
mazione si volesse produrre e distribuire.
Al punto che, da completo sistema di definizione dei Gli schemi XML
dati e quindi del contenuto, è diventato anche il lin-
guaggio di definizione dei contenitori dei dati e quindi Una interessante caratteristica
come vero e proprio protocollo applicativo. dell’XML è la possibilità di veri-
Un esempio per tutti è certamente SOAP, che usa ficare la correttezza sintattica di
proprio XML per definire un completo protocollo ap- un certo file rispetto ad un tem-
plicativo per realizzare un RPC (Remote Procedu- plate standard di riferimento det-
re Call). to schema.
Altrettanto straordinario è stato l’impatto che han- Infatti l’XML è di per sé un for-
no avuto i vari linguaggi di scripting nel mondo della mato piuttosto rigido, nel sen-
programmazione: hanno unito la potenza dei linguag- so che deve essere garantita la
gi di sviluppo tradizionali alla semplicità di scrittura correttezza sintattica, che preve-
e di deployment, in modo che qualsiasi ambito ap- de una lunga serie di regole da
plicativo potesse sfruttarne al massimo le peculiarità rispettare, perché il file XML si
senza che fosse dotato di una complessa infrastrut- possa definire well formed.
tura di compilazione e di runtime. Queste vanno dal chiudere ogni
Scopo di questo articolo sarà presentare uno stu- tag che viene aperto, all’uso de-
dio su un possibile utilizzo dello scripting per realiz- gli apici per i valori degli attribu-
ti, alla presenza di un solo nodo
padre per ciascun file e tanto al-
Vito Vessia È co-fondatore della codeBehind S.r.l. (http: tro ancora.
//www.codeBehind.it), una software factory di applicazioni Non sarà argomento di questo
enterprise, web e mobile, dove progetta e sviluppa applica-
articolo spiegare queste regole;
zioni e framework in .NET, COM(+) e Delphi di cui si occupa
degli aspetti architetturali. È autore del libro “Programmare la Rete e le librerie sono ormai
il cellulare”, Hoepli, 2002, sulla programmazione dei telefoni sature di materiale sull’XML. Un
cellulari connessi al PC con protocollo standard AT+.Può essere buon consiglio per la lettura è
contattato tramite e-mail all’indirizzo vvessia@infomedia.it. certamente [1].

52 VBJ N. 64 - Luglio/Agosto 2005


I MITI

La verifica sintattica però non è tutto, ma si ment), con la struttura di ogni attributo che
pone la necessità di definire un formato che contiene (xs:attribute) compresi i tipi e le even-
descrivesse le regole semantiche a cui il file tuali constraint (es. use=”required”).
deve adeguarsi perché possa essere compre- L’elenco dei nodi è flat, cioè non presenta
so anche da chi non ne è l’autore. la tipica struttura gerarchica dei file XML che
Proviamo ad immaginare di voler definire lega i nodi padre ai figli: questa gerarchia è
un formato standard di fatture da condivide- invece definita attraverso la presenza dei nodi
re con i nostri clienti e fornitori, e per far ciò xs:sequence definiti nei nodi xs:element.
vogliamo basarci su XML; dobbiamo altresì Il principio di funzionamento è molto sempli-
specificarne la logica (es. un nodo di testata ce: per verificare che il file XML sia semanti-
che si chiama HEADER, che contiene alcu- camente corretto rispetto allo schema di ri-
ni attributi come Number, Year e altri anco- ferimento deve soddisfare tutte le regole di
ra, poi devono esserci uno o più nodi di tipo quest’ultimo.
ROW e così via). Questa verifica di correttezza sarebbe un po’
troppo onerosa, anche se non impossibile, se
realizzata a mano affidandosi solo al nostro
colpo d’occhio, ma in realtà il compito è rea-
XML è di per sé lizzato egregiamente da un buon parser XML
di ultima generazione.
un formato Si intende cioè un parser in grado di ma-
piuttosto rigido nipolare e di comprendere gli schema XML
di ultima generazione definiti dal World Wide
Web Consortium [2].
Nel nostro esperimento utilizzeremo il nuo-
Lo schema XML serve proprio a questo. In vo e potente parser Microsoft XML Core Ser-
realtà gli schemi sono un’invenzione recen- vices (MSXML) 4.0.
te per l’XML: inizialmente il loro compito era Questo componente racchiude al suo interno
svolta dai DTD (Document Type Definition) un classico DOM (Document Object Model)
dalla logica molto simile. cioè un oggetto in grado di produrre un com-
Gli schemi hanno numerosi vantaggi: sono pleto object model a partire da un file XML,
XML essi stessi, supportano l’ereditarietà e un SAX (Simple API for XML) cioè un compo-
sono molto più potenti e completi. Osservia- nente che non conserva in memoria l’intera
mo ad esempio il Listato 1. struttura del file XML ma scatena degli eventi
Lo schema di riferimento del file XML ap- durante il parsing dello stesso.
pena mostrato è visibile nel
Listato 2.
In realtà l’esempio è stato
privato di numerosi attribu-
ti e di alcuni nodi per ridur-
ne l’occupazione tipografica,
ma rende l’idea.
Per consultare il codi-
ce Visual Basic si riman-
da al sorgente che accom-
pagna l’articolo, scaricabi-
le liberamente dal sito ftp:
//ftp.infomedia.it.
Possiamo notare come il file
XML presenti un riferimento
allo schema a cui soggiace
(Untitled2.xsd) e che lo sche-
ma non sia altro che un con- Figura 1 La gerarchia di oggetti dello Schema
tenitore di tutti i tipi di nodi Object Model
presenti nel file (nodi xs:ele-

N. 64 - Luglio/Agosto 2005 VBJ 53


I MITI

È certamente più performante del DOM an- Cosa c’entra lo scripting?


che se richiede una maggiore elaborazione e
offre meno servizi. Giunti a questo punto della lettura vi chiede-
La vera novità della versione 4.0 è invece rete cosa c’entra lo scripting in tutto questo.
la presenza del SOM (Schema Object Mo- In effetti non c’entra apparentemente nulla, se
del): un componente in grado di produrre un non fosse che si possono realizzare interes-
object model a partire da uno schema XML santi applicazioni dall’abbinamento della tec-
(Riquadro 1). nologia di scripting ActiveScript di Microsoft
Data la natura completamente plain text del e l’XML con schema.
formato XML, sarebbe possibile editare, crea-
re e modificare questi file e gli schemi asso-
ciati direttamente col Notepad, ma ci si ren-
de conto ben presto che questo compito ha Realizzare un’intera
del disumano. applicazione in VBScript
Ed è soprattutto vero quando si usano pe-
santemente gli schemi. è probabilmente
poco pratico

Riquadro 1 Schema Object Model (SOM)


Infatti il disaccoppiamento tra schema XML
e dati XML ricorda da vicino la metafora OOP
Il SOM è basato sull’abstract W3C riferito in [3]. Esso della classe e dell’oggetto: la classe è la de-
prevede sostanzialmente l’accesso agli elementi conte- finizione formale delle regole e dei comporta-
nuti in uno schema XML (proprietà, dichiarazioni, relazio-
menti di un particolare aggregato strutturale di
ni tra elementi). È quindi un set navigabile di classi che
riflettono direttamente le specifiche W3C XSD (Schema dati, l’oggetto è invece la stessa classe che
Definition Language). In Figura 1 è riportata la gerarchia prende vita e va in esecuzione su un partico-
delle interfaccia implementate in questo componente. Si lare set di dati che corrisponde al prototipo
può notare come appunto la gran parte delle interfacce
dell’aggregato detto in precedenza.
esposte sia discendenti da altre interfacce.
Vediamo subito un esempio di come si carica uno Così se la definizione dei dati e dei compor-
schema XML nel SOM. L’esempio è tratto dalla docu- tamenti sugli stessi (la classe) è unica per una
mentazione a corredo del MSXML 4 [5]: particolare tipologia applicativa, le sue “istan-
Dim oSchemaCache as New IXMLDOMSchemaCollection2
ze” (gli oggetti) specifiche basate su set di dati
Dim oSchema as ISchema differenti possono essere invece numerose e
Dim nsTarget as String senza relazioni tra loro.
nsTarget = “http://www.sample.com/sampletarget” Così, nel caso specifico, possiamo immagina-
oSchemaCache.add nsTarget, “PO.xsd”
Set oSchema = oSchemaCache.getSchema(nsTarget)
re di produrre un set di classi, un intero object
model, a partire da uno schema XML.
Viene dunque istanziato un oggetto IXMLDOMSche- Fatto questo saremo in grado di produr-
maCollection2 e di questo viene invocato il metodo re istanze di queste classi che si basano su
Add per aggiungere un nuovo schema (PO.xsd) con
namespace http://www.sample.com/sampletarget alla file XML differenti, purché soggiacciano allo
collezione degli schema contenuta nell’oggetto appena schema comune da cui sono state generate
istanziato. A questo punto è possibile farsi ritornare lo le classi. Vediamo un esempio di classi VB-
schema appena inserito grazie al metodo getSchema,
Script che permettono di navigare il file XML
ma questa volta nella forma di interfaccia ISchema che
è l’interfaccia più importante del SOM perché è l’unica dati.xml presente nella directory dei sorgenti
che permette di esplorare le caratteristiche dello sche- che accompagnano l’articolo:
ma. Esistono comunque decine di interfacce diverse,
alcune delle quali verranno adoperate nel proseguo di
‘codice VBScript
questo articolo. In generale si rimanda alla consultazione
della ricca documentazione allegata al componente di Class vCRM
Microsoft per una completa comprensione perché non Private m_Dictionary
è scopo di questa esposizione esplorare pienamente le Private m_OnStartup
caratteristiche del componente, cosa che richiederebbe
Private m_VERSION
da solo uno o più articoli interi.
Private m_AUTHOR

54 VBJ N. 64 - Luglio/Agosto 2005


I MITI

Listato 1 Un file XML

<?xml version=”1.0”?>
<vCRM AUTHOR=”Vito Vessia” mlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”F:
\ScriptClasses\XMLClasses\disk\Untitled2.xsd”>
<ORDER DONE_NODE=”FALSE” CODE_SITE=”JRHOLD” CODE_CORRESPONDENT=”ARDILLO”
CODE_INVOICE_CUST=”029900” CODE_SHIP_CUST=”029900” CODE_ORDER_TYPE=”%” CODE_FAIR=”COD_FIERA”
CODE_PL_SOFA=”AJ” CODE_PL_ACCESS=”EF” CODE_CURRENCY=”ITL[~]” COD_CAUS_COMM=”G03”
COD_CAUS_PROD=”MAG” CODE_DOC_TYPE=”003”>
<HEADER_REMARK HRMRK_CODE_SITE=”JRHOLD” HRMRK_PROGRES=”1” HRMRK_CODE=”OCMC”
HRMRK_REMARK=”Maximum[~]Care” HRMRK_ON_CREDITS=”0” HRMRK_ON_DDT=”1” HRMRK_ON_LABEL=”0”
HRMRK_ON_INVOICE=”1” HRMRK_ON_OC=”1”/>
<ROW CODE_DBALT=”031615” CODE_PL=”AJ” ROW=”1” NUM_REVIS_ROW=”0” ORDER_SET=”2” PO=””
CODE_PART=”073504224”>
<ROW_REMARK RRMRK_CODE_SITE=”JRHOLD” RRMRK_PROGRES=”2” RRMRK_CODE=”OCMC”
RRMRK_REMARK=”Maximum[~]Care”/>
<COVER CODE_COVER=”15001011” CODE_QUEST=”Z0110001” FLG_ROW_MODIFIED=”” PROGR_RIV=”1”
NUM_REVIS_COVER=”0” CVR_CODE_SITE=”JRHOLD”/>
</ROW>
</ORDER>
</vCRM>

Public CUSTOMER Quella appena mostrata è la classe di navi-


Public ORDER gazione del nodo vCRM del file XML in que-
stione (file già parzialmente riportato in pre-
Private Sub Class_Initialize() cedenza in questo articolo).
set m_Dictionary = CreateObject Osservando le funzioni in essa contenute,
(“Scripting.Dictionary”) possiamo notare la presenza di property let/
m_OnStartup = True get per accedere alla proprietà AUTHOR che,
set CUSTOMER = CreateObject nel file XML, è l’attributo AUTHOR.
(“Scripting.Dictionary”) Esisterà quindi una coppia di questi meto-
set ORDER = CreateObject di nella classe per ogni attributo nel file XML
(“Scripting.Dictionary”) d’origine.
End Sub Possiamo inoltre notare la presenza della
property get Attributes che rappresenta la
Private Sub Class_Terminate() collezione di tutti gli attributi all’interno della
set m_Dictionary = Nothing classe, in modo che vi si possa accedere per
set CUSTOMER = Nothing indice e per chiave e, soprattutto, si possa
set ORDER = Nothing applicare l’enumerazione COM (la for each).
End Sub Così, per accedere all’elemento AUTHOR po-
tremmo invocare:
Public Property Get Attributes()
Set Attributes = m_Dictionary ‘.Items ‘codice VBScript
End Property Dim oCRM

Public Property Get AUTHOR() Set oCRM = New vCRM


AUTHOR = m_AUTHOR oCRM.AUTHOR = “Vito Vessia”
End Property Msgbox oCRM.AUTHOR

Public Property Let AUTHOR(valueField) oppure accedere in modo simbolico alla pro-
m_AUTHOR = valueField prietà:
End Property
End Class MsgBox oCRM.Attributes(“AUTHOR”)

N. 64 - Luglio/Agosto 2005 VBJ 55


I MITI

Listato 2 Lo schema relativo al file del Listato 1

<?xml version=”1.0” encoding=”UTF-8”?>


<xs:schema xmlns:xs=“http://www.w3.org/2001/XMLSchema“ elementFormDefault=“qualified“>
<xs:element name=»vCRM»>
<xs:complexType>
<xs:sequence>
<xs:element ref=»ORDER»/>
</xs:sequence>
<xs:attribute name=”AUTHOR” type=”xs:string” use=”required”/>
<xs:attribute name=”DATE” type=”xs:string” use=”required”/>
</xs:complexType>
</xs:element>
<xs:element name=»ORDER»>
<xs:complexType>
<xs:sequence>
<xs:element ref=»HEADER_REMARK» maxOccurs=»unbounded»/>
<xs:element ref=”ROW” maxOccurs=”unbounded”/>
</xs:sequence>
<xs:attribute name=”DONE_NODE” type=”xs:string” use=”required”/>
<xs:attribute name=”CODE_SITE” type=”xs:string” use=”required”/>
</xs:complexType>
</xs:element>
</xs:schema>

e scorrerle tutte semplicemente con: Public Property Get TOT_SIT()


TOT_SIT = m_TOT_SIT
‘codice VBScript End Property
Dim lAttribute
Public Property Let TOT_SIT(valueField)
For Each lAttribute in oCRM.Attributes If Not IsNumeric(0 & valueField) Then
Msgbox lAttribute Err.Raise 1970, , “Invalid number!”
Next End If
m_TOT_SIT = valueField
La collezione degli attributi è realizzata usan- End Property
do un Dictionary a livello di classe che viene End Class
istanziato nella Class_Initialize.
Osservando però il nodo XML vCRM, possia- Possiamo notare che la classe vCRM presen-
mo notare che esso possiede alcuni nodi figli. tava una proprietà ORDER.
Uno di questi è ORDER, la cui classe, secon- Questa rappresenta proprio la collezione dei
do il prototipo appena visto, dovrebbe essere nodi figli che, a tutti gli effetti, sono classi di tipo
come quella che segue (per ragioni di praticità Order come quella appena mostrata.
in realtà se ne riporta solo un estratto): Questa collezione è realizzata, ancora una vol-
ta, col solito Dictionary.
‘codice VBScript Così, se vogliamo accedere e stampare a vi-
Class Order deo la proprietà TOT_SIT del secondo nodo
Private m_Dictionary ORDER di una classe vCRM che fa da pro-
Private m_OnStartup xy al nostro XML vCRM, dobbiamo sempli-
Private m_CODE_FAIR cemente scrivere:
Private m_COD_CAUS_PROD
Private m_TOT_SIT Msgbox vCRM.ORDER(2).TOT_SIT

Public HEADER_REMARK A sua volta, il nodo Order conterrà altri nodi


Public ROW figli ripercorrendo la stessa gerarchia del file
XML originario, così da realizzare un mo-

56 VBJ N. 64 - Luglio/Agosto 2005


I MITI

dello ad oggetti. Nel caso specifico i nodi fi- IDispatch di COM come mostrato in [4]. Bre-
gli di Order sono HEADER_REMARK e ROW vemente vediamo come utilizzare direttamen-
e così via fino a completare la gerarchia (es. te da Visual Basic tali classi grazie al potente
vCRM.ORDER(2).ROW(3). COVER(1).CODE_ Microsoft Script Control [6] che è liberamente
QUEST). Si può vedere quindi come è possi- scaricabile dal sito di Microsoft e che sempli-
bile realizzare classi VBScript (o di scripting in fica drasticamente l’uso della tecnologia Acti-
generale in tecnologia Microsoft ActiveScrip- veScripting (il linguaggio usato sarà VBScript,
ting) che ripercorra, in forma più naturale ed alternativamente si sarebbe potuto usare qual-
immediata, la struttura di un file XML qualsiasi siasi altro linguaggio di scripting installato):
basandosi sullo schema a cui soggiace il file
stesso. Sarà così possibile istanziare queste ‘codice VBA
classi con i dati presenti nel file XML, infat- Dim oSC As Object ‘lo Script Control
ti ogni attributo del nodo XML è esposto nel- Set oSC = CreateObject(“MSScriptControl.ScriptControl”)
la classe in forma di property get/let che per- With oSC
metterà quindi anche di assegnare i valori di .AllowUI = True‘è permesso l’uso di interfaccia
queste proprietà: utente negli script
.Language = “VBScript”
vCRM.ORDER(2).TOT_SIT = 3 .UseSafeSubset = False ‘non impone nessuna
restrizione per la sicurezza
Realizzare un’intera applicazione in VBScript End With
è probabilmente poco pratico e ancor meno in- ‘a questo punto aggiungiamo il codice delle classi VBScript
teressante dal punto di vista dell’implementa- che effettuano il proxy dei nodi XML come visto in precedenza
zione in una situazione reale. oSC.AddCode “<codice della classe>”
Si da il caso però che sia possibile utilizzare
proficuamente tali classi in applicazioni Visual Fatto questo possiamo procedere all’istanzia-
Basic (VBA) o in generale in tutti quei linguag- zione di queste classi VBScript e al loro uso
gi ad alto livello capaci di gestire le interfacce all’interno di Visual Basic.

Listato 3 Assegnazione di valori agli attributi in base alle caratteristiche

For Each lAttribute In lComplex.Attributes


CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _
“ Public Property Get “ & lAttribute.Name & “()” & vbCrLf & _
“ “ & lAttribute.Name & “ = m_” & lAttribute.Name & vbCrLf & _
“ End Property” & vbCrLf & _
“ “ & vbCrLf & _
“ Public Property Let “ & lAttribute.Name & “(valueField)” & vbCrLf ‘& vbCrLf

Set lProp = lAttribute


Set lType = lProp.Type
Select Case lType.itemType
Case SOMITEM_DATATYPE_BYTE, SOMITEM_DATATYPE_DECIMAL, SOMITEM_DATATYPE_DOUBLE, _
SOMITEM_DATATYPE_FLOAT, SOMITEM_DATATYPE_INTEGER, SOMITEM_DATATYPE_LONG,
SOMITEM_DATATYPE_SHORT:
CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _
“ If Not IsNumeric(0 & valueField) Then “ & vbCrLf & _
“ Err.Raise 1970, , “”Invalid number!””” & vbCrLf & _
“ End If” & vbCrLf
Case SOMITEM_DATATYPE_DATE, SOMITEM_DATATYPE_DATETIME:
CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _
“ If Not IsDate(“””” & valueField) Then “ & vbCrLf & _
“ Err.Raise 2000, , “”Invalid date!””” & vbCrLf & _
“ End If” & vbCrLf
End Select

N. 64 - Luglio/Agosto 2005 VBJ 57


I MITI

Infatti, trattandosi di normali istanze di inter- su nodi, sequence, ecc… Perché non affidar-
facce IDispatch per utilizzarle in Visual Basic è si quindi ad un generatore automatico di clas-
sufficiente ottenere il puntatore all’istanza del- si a partire da schema e di istanze delle classi
la classe stessa e assegnarlo ad un normale create in precedenza a partire da file XML che
Object o Variant. Questo è possibile sfruttan- soggiacciono allo stesso schema? Ebbene è
do il potente metodo Eval dello ScriptControl quello che andremo a realizzare, seppur in una
(e dello scripting più in generale) che permet- forma non esaustiva.
ta di valutare, facendosi ritornare il risultato in Il sorgente che accompagna l’articolo presenta
forma di variant, una qualsiasi espressione: il progetto Visual Basic di un ActiveX Dll (CPXML
Proxies.vbp) e di un progetto di esempio che
‘codice VBA utilizza tale engine (CPXMLProxyDemo.vbp)
Dim oCRM As Object sul file XML mostrato nell’articolo. Il motore
Set oCRM = oSC.Eval (“New vCRM”) è però generico e funziona su ogni XML con
schema standard [3] e utilizza il nuovo SOM di
A questo punto possiamo accedere a oMyOr- MSXML 4 per produrre le classi a partire dagli
der direttamente da codice Visual Basic esat- schema e il DOM per produrre le istanze delle
tamente come abbiamo fatto all’interno di VB- classi a partire dai nodi XML. Questo presen-
Script: ta solo due metodi pubblici. Il primo, Create-
ClassesFromXMLSchema, permette di produr-
‘codice VBA re una gerarchia di classi VBScript a partire da
oCRM.AUTHOR = “Vito Vessia” uno schema XML:
Msgbox oCRM.AUTHOR
‘codice VBA
Il Class Generator Public Function CreateClassesFromXMLSchema
(XMLSchemaObject As Variant, _
Il meccanismo appena mostrato è molto po- Optional XMLSchemaRef As Variant) As String
tente ma ha, nella forma in cui l’abbiamo mo-
strato, un’utilità quasi nulla perché ci permette Il primo parametro rappresenta proprio lo
sì di scrivere le nostre classi di proxy intorno ad schema da cui produrre le classi.
un qualsiasi XML con schema, ma ci costrin- È di tipo variant perché potrebbe essere sia il
ge comunque a scriverci manualmente, e per nome del file o la url dello schema e sia diret-
ogni differente schema, il codice di tali clas- tamente l’oggetto ISchema, ed infatti vengo-
si. Ci costringe inoltre, cosa ancora più inutile, no fatti i controlli del caso (una IsObject e una
a istanziare tali classi e a popolarle secondo i QueryInterface) per comprenderne la natura.
dati presenti nel file XML in questione. Il secondo parametro è passato invece per
referenza e viene usato come parametro di tipo
out, cioè viene valorizzato in uscita; in pratica
a questo viene assegnata l’istanza dello sche-
Le classi che andremo a pro- ma appena prodotto.
durre non perderanno nulla Il valore di ritorno della funzione è invece pro-
prio il codice sorgente delle classi.
delle caratteristiche dello A questo punto si procede scorrendosi la col-
lection elements dello ISchema, ciascun ele-
schema XML di cui vanno a mento rappresenta un diverso nodo dei file
realizzare il wrapper XML sottoposti allo schema dato.

Vediamo un estratto della classe

A ben pensarci si tratterebbe di scrivere ogni Dim lSchemaItem As MSXML2.ISchemaItem


volta sempre lo stesso codice perché gli sche- Dim lAttribute As MSXML2.ISchemaItem
ma XML si descrivono sempre nelle stesse mo- Dim lSchema As MSXML2.ISchema
dalità viste in precedenza e il popolamento del- Dim lElem As MSXML2.ISchemaElement
le istanze è ancora più banale perché non è al- Dim lComplex As MSXML2.ISchemaComplexType
tro che un susseguirsi interminabile di for each Dim lProp As MSXML2.ISchemaAttribute

58 VBJ N. 64 - Luglio/Agosto 2005


I MITI

Dim lType As MSXML2.ISchemaType L’altro metodo è CreateInstanceFromXML.


For Each lSchemaItem In lSchema.elements Esso banalmente scorre il file XML passato
in input (XMLIssue)
CreateClassesFromXMLSchema = Public Function CreateInstancesFromXML
CreateClassesFromXMLSchema & _ (XMLIssue As Variant, Optional ParentArray As Variant, Op-
„Class „ & StrConv(lSchemaItem.Name, tional XMLSchemaRef As Variant, Optional SourceCode As String
vbProperCase) & vbCrLf = “”) As Object

Set lElem = lSchemaItem Il parametro XMLIssue rappresenta il nome


If lElem.itemType = SOMITEM_ELEMENT Then o il nodo XML che si vuole trasformare in
Set lComplex = lElem.Type un’istanza di una classe VBScript generata
CreateClassesFromXMLSchema = dallo schema a cui soggiace il nodo stesso.
CreateClassesFromXMLSchema & vbCrLf & _ Vale infatti lo stesso discorso di ambivalen-
za visto per il parametro del metodo prece-
A questo punto, per ogni classe che si va a dente.
creare, si procede con l’enumerazione della col-
lection Attributes del Complex ottenuto effet-
tuando il casting dell’oggetto nodo (come si può
osservare nel codice appena mostrato).
Questo meccanismo
Nel codice specifico vengono prodotti i mem- si presta alla realizzazione
bri privati della classe che conterranno i valori
delle proprietà/attributi di ogni nodo. di generatori automatici
For Each lAttribute In lComplex.Attributes
di codice
CreateClassesFromXMLSchema =
CreateClassesFromXMLSchema & vbCrLf & _
“ Private m_” & lAttribute.Name Gli altri parametri, invece, sono apparente-
Next mente poco significativi ed infatti non verran-
no mai adoperati (per questo sono opzionali)
Stessa cosa, però sulla collection particles di in modo diretto; servono invece all’interno del
contentModel del Complex. metodo stesso perché si tratta di un meto-
Essa conterrà l’elenco eventuale dei nodi fi- do ricorsivo.
gli del nodo corrente (ad es. il nodo vCRM ha Infatti la logica del metodo è che, dal nodo
come nodi figlia Order e Customer). XML appena passato (o dal nodo root del file
XML se il parametro XMLIssue è di tipo nodo)
For Each lAttribute In lComplex.contentModel.particles viene popolato un’istanza.
CreateClassesFromXMLSchema = Per ogni nodo figlio del nodo corrente viene
CreateClassesFromXMLSchema & vbCrLf & _ invocato il metodo nuovamente per popolare
“ Public “ & lAttribute.Name le istanze di questi nodi figli e così via.
Next I tre altri parametri servono proprio per co-
municare informazioni alle altre istanze di ri-
Un’altro interessante pezzo di codice da os- corsione del metodo in modo da velocizza-
servare è quello che permette di gestire le con- re alcune operazione e donare uno stato al
straint a livello di property let di ogni attributo, metodo.
cioè di gestire correttamente le assegnazioni
di valori agli attributi in base alle caratteristi- Dim lObject As Object
che peculiari (tipo, lunghezza, ecc.) diretta- Dim lElement As MSXML2.IXMLDOMElement
mente ereditate dalle informazioni di schema. Dim lDOM As MSXML2.DOMDocument40
Si tratta di un’implementazione molto parziale, Dim lXSDFile As String
ma rende l’idea della potenza del meccanismo Dim lAttribute As MSXML2.IXMLDOMAttribute
che si va ad implementare: le classi che an- Dim lSchema As MSXML2.ISchema
dremo a produrre non perderanno nulla delle Dim lSchemaItem As MSXML2.ISchemaItem
caratteristiche dello schema XML di cui van- Dim lSchemaElement As MSXML2.ISchemaElement
no a realizzare il wrapper (Listato 3). Dim lSchemaComplex As MSXML2.ISchemaComplexType

N. 64 - Luglio/Agosto 2005 VBJ 59


I MITI

Dim lSchemaAttribute As MSXML2.ISchemaItem Vediamo dunque il motore appena creato in azio-


Dim lNodeList As MSXML2.IXMLDOMNodeList ne in un esempio molto semplice in Visual Basic:
Dim lChildElement As MSXML2.IXMLDOMElement
‘codice VBA
If SourceCode = “” Then Dim oProxy As Object
‘ci troviamo al livello 0 della ricorsione Dim oObject As Object
e il sorgente delle classi non è stato
‘ancora prodotto e quindi viene prodotto in Set oProxy = CreateObject(“CPXMLProxies.XMLProxy”)
questa fase usando il metodo precedente Set oObject = oProxy.CreateInstancesFromXML(“dati.xml”)
‘(codice omesso, per lo studio del codice di MsgBox oObject.Order(1).Row(2).Cover(1)
questo metodo si rimanda ai sorgenti) .CodQuest
Else ‘è un livello di ricorsione >0 e quindi
il sorgente delle classi è stato passato Semplice e potente…
‘per referenza
Set lSchema = XMLSchemaRef Conclusioni
End If
Abbiamo visto come realizzare un sistema
‘nuova istanza della classe wrapper del nodo corrente di classi VBScript di wrapper a partire da un
Set lObject = g_SC.Eval file XML con schema, riciclando il concet-
(“new “ & lElement.nodeName) to di classe/istanza sull’equivalente XML di
schema/file. Le istanze di queste classi pos-
Per ogni proprietà/attributo dell’istanza vie- sono poi essere utilizzate a runtime dalle no-
ne invocata la property let per assegnare il va- stre applicazioni Visual Basic che quindi ac-
lore preso dal nodo XML originale per quel- quistano la capacità di gestire i complicati
l’attributo. file XML con schema senza conoscere nulla
La valorizzazione avviene per nome simboli- di questo formato, senza ricorrere al potente
co della proprietà, grazie alla potente CallBy- ma un po’ complicato object model di DOM
Name di Visual Basic: e senza neppure mantenere una referenza a
MSXML 4. Il tutto è stato generato facendo
For Each lSchemaAttribute In lSchemaComplex.Attributes uso di un motore molto semplice scritto in Vi-
CallByName lObject, lSchemaAttribute.Name,VbLet, _ sual Basic, che quindi ci mette in salvo dalla
lElement.Attributes.getNamedItem riscrittura di queste classi VBScript ogni vol-
(lSchemaAttribute.Name).nodeValue ta che cambia il formato dello schema e ci
lObject.Attributes.Add lSchemaAttribute.Name, _ risparmia anche dal codice di popolamento
lElement.Attributes.getNamedItem delle istanze. Al di là della possibilità di map-
(lSchemaAttribute.Name).nodeValue pare classi IDispatch su XML, questo mec-
Next canismo si presta alla realizzazione di gene-
ratori automatici di codice, magari a partire
In questo brano di codice si può invece os- da template XML che li descrivono o ad al-
servare l’uso della ricorsione nel metodo: per tre applicazioni simili o diverse che possono
ogni nodo figlio di quello corrente viene invo- trarre giovamento da questo singolare utiliz-
cato infatti lo stesso metodo. zo. Si accettano consigli a riguardo…

For Each lSchemaAttribute In lSchemaComplex. Bibliografia


contentModel.particles
Set lNodeList = lElement.getElementsByTagName [1] Don Box, A. Skonnard, J. Lam, “Essential
(lSchemaAttribute.Name) XML – Beyond markup”, Addison Wesley
For Each lChildElement In lNodeList (2000)
CreateInstancesFromXML lChildElement, [2] I tipi di dati di SOAP, http://www.w3.org/
CallByName(lObject, lChildElement. 2001/XMLSchema
baseName, VbGet), lSchema, SourceCode [3] XML Schema Part 0: Primer, http://
Next www.w3.org/ TR/xmlschema-0/
Next [4] Vito Vessia, “Tecniche di « osmosi » nello
scripting”, DEV n. 95

60 VBJ N. 64 - Luglio/Agosto 2005


dmauri@infomedia.it
.NET TOOLS a cura di Davide Mauri

Dopo più di due anni di esistenza la rubrica .NET Tools 2. Una volta giunto alla versione finale, il costo di ogni
si concede un “strappo” alla regola, ospitando in questo singolo prodotto sarà di ben (!) 49$!
numero una suite di prodotti che avrà un sicuro posto di
rilievo nel prossimo futuro, e che, a differenza dei software Da questo ovviamente va escluso Sql Server Express
recensiti normalmente, è a pagamento. che invece è – e rimarrà – completamente gratuito!
Ohibò! Perché questa decisione? Non c’è più software A questo punto nessuno ha più scuse; dal punto di
“free” disponibile? vista professionale, chi ancora non ha provato .NET lo
Assolutamente no, anzi…ma, come ormai saprete, i tempi potrà fare senza spendere nulla, mentre dal punto di
del rilascio del .NET Framework 2.0 e di Visual Studio vista didattico, chi vorrà, potrà essere sicuro di imparare
2005 (e di Sql Server 2005) sono sempre più vicini e, data (od insegnare) ad utilizzare un prodotto che è in tutto
l’importanza dell’evento, noi di VBJ non possiamo certo e per tutto simile a quello che, magari, un giorno, si
far finta di nulla! userà per lavorare.
È quindi importante farsi trovare pronti, magari andando Tenendo conto che nell’età scolastica (ed includo an-
a vedere cosa bolla in pentola sopratutto per quello che che il periodo universitario) chi sviluppa lo fa solamente
riguarda la programmazione free (o a bassissimo costo) per passione e per divertimento, cosa ci può essere di
di .NET 2.0. meglio che prepararsi al mondo professionale unendo
La suite di prodotti “Express” è stata pensata proprio l’utile al dilettevole?
con questo target in mente: far avvicinare tutti (ma pro-
prio tutti) gli sviluppatori a .NET, soprattutto quelli che Visual Studio Express Edition Products
non vogliono o non possono investire nell’intero Visual
Studio, ma che non vogliono comunque rinunciare a fare A differenza dei fratelli maggiori (da Visual Studio stan-
il salto di qualità verso la piattaforma .NET 2.0. dard in su) che permettono di scrivere codice indifferen-
Tralasciando però per un momento il discorso pura- temente in C#, VB.NET, J# e C++, e che permettono di
mente professionale, la possibilità di avere piattaforme sviluppare applicazioni Windows e Web, la suite Express
di sviluppo ottime ad un prezzo davvero basso, permette è strutturata in modo da diversificare gli ambienti di
di far iniziare la formazione informatica sin dalle scuole sviluppo a seconda del target e del linguaggio.
medie, dando la possibilità ai ragazzi interessati di potersi Per lo sviluppo di applicazioni Windows, a seconda
avvicinare subito ad uno strumento reale di sviluppo, del linguaggio che desideriamo utilizzare, possiamo
lasciando il buon GWBasic al suo posto, ovverosia… scegliere tra:
nell’armadio. A tal proposito ho recentemente avuto
un’ottima esperienza a Milano, dove, insieme ad un  Visual Basic 2005 Express
collega ed a volenterosi professori e genitori abbiamo  Visual C# 2005 Express
organizzato alcune giornate di formazione su Visual  Visual J# 2005 Express
Basic 2005 per ragazzi delle scuole medie.  Visual C++ 2005 Express
Il riscontro è stato molto positivo, soprattutto per i
genitori degli appassionati fanciulli che possono così Se invece vogliamo sviluppare siti web, è sufficiente
utilizzare Visual Studio senza doversi svenare. utilizzare un solo prodotto, che permette di scegliere se
Per due motivi: sviluppare in VB.NET, C# o J#:

1. Attualmente le versioni Express sono ancora in Beta  Visual Web Developer Express
(benché piuttosto stabile) e l’utilizzo ed il download
sono completamente gratuiti. Tutti gli ambienti di sviluppo mettono a disposizione
praticamente lo stesso set di funzionalità; set davvero
molto ampio, tanto da farne prodotti quasi professionali.
Davide Mauri è un consulente freelance che si occupa di Per quanto riguarda la scrittura del codice troviamo le
SQL Server 2000 e di sviluppo di soluzioni Web basate sul ormai indispensabili funzionalità di syntax highlighting e
.NET Framework. All’attività di consulenza affianca una co- di Intellisense (anche conosciuto come auto-completion),
spicua attività di docenza e di formazione presso Mondadori che permettono di scrivere velocemente e possibilmente
Informatica. Il suo sito personale è www.davidemauri.it. senza errori il codice.

N. 64 - Luglio/Agosto 2005 VBJ 61


.NET TOOLS

Figura 1 Visual Web Developer

Tra le novità si trova l’implementazione del refactoring anche gli errori più semplici. Dall’ambiente di sviluppo
“Rename Method” (esteso di fatto ad ogni oggetto) che è possibile anche gestire database (non necessariamente
semplifica di molto la vita quando si vuole rinominare Sql Server) e nel caso della versione Web Developer
un oggetto. navigare nel sito che si sta sviluppando usufruendo del
Come si vede dalla Figura 1 (che mostra l’ambiente in web server integrato.
tutta la sua bellezza), quando si rinomina un elemento, A differenza infatti del predecessore Web Matrix, questo
Visual Studio Express se ne accorge e ci chiede se ambiente di sviluppo è completamente autosufficiente
vogliamo aggiornare tutti i riferimenti all’oggetto con il e non necessita dell’installazione di Internet Information
nuovo nome. Molto comodo! Server o di Cassini sul proprio computer.
Un altro nuovo strumento che aiuta a semplificare L’installazione del prodotto è molto semplice: una
ulteriormente la scrittura del codice è rappresentato volta scaricato l’eseguibile dal sito lo si lancia e lo si
dai Code Snippets. lascia lavorare. Penserà lui a scaricare tutto il necessario
Premendo la combinazione di tasti Ctrl+K e di seguito (.NET Framework 2.0 compreso) per far funzionare tutto
Ctrl+X appare un menù a tendina che ci chiede che correttamente.
cosa vogliamo fare. Prima di concludere, una nota riguardante la docu-
Dobbiamo aggiungere un proprietà alla nostra classe? mentazione e gli esempi forniti.
Nessun problema, scelta la voce adatta il codice verrà La documentazione è – come da standard Microsoft
generato in automatico e noi dovremo solo preoccuparci – piuttosto ricca; se questo però non bastasse le ricerche
di personalizzarlo! fatte all’interno della stessa possono essere automati-
Per quanto concerne l’esecuzione del codice, tutti gli camente estese al web, andando a cercare documenti
ambienti permettono di fare del debugging offrendo ed esempi di codice nelle community registrate su Co-
praticamente tutte le funzionalità delle versioni più co- deZone e su MSDN online, per essere sicuri di avere
stose, fondamentali per poter sviluppare un prodotto sempre informazioni recenti. Per quanto riguarda esem-
di buona qualità, senza dover impazzire nel cercare pi e tutorial, in ogni versione Express vengono forniti

62 VBJ N. 64 - Luglio/Agosto 2005


.NET TOOLS

Figura 2 Il sito fornito come tutorial

Starter Kit a tema: per quanto riguarda la versione Web Prodotto


Developer lo starter kit fornito è quello per la creazione Visual Studio Express Edition Products
di un sito personale (Figura 2), mentre invece per la
versione Visual Basic 2005 ne vengono forniti due: uno Url di riferimento
per la gestione della propria videoteca (Figura 3) ed uno http://lab.msdn.microsoft.com/express/
per creazione di uno screen-saver. Stato Release
Anche in questo caso è comunque possibile scaricarne BETA 2
di nuovi, accedendo alla voce “Download Additional
Starter Kit” direttamente dalla schermata iniziale che Semplicità d’uso 
viene presentata ad ogni avvio dell’ambiente di svi- Semplicissimo, soprattutto grazie agli esempi ed ai tu-
torial forniti.
luppo.
È da sottolineare che gli starter kit sono comunque Utilità 
rivolti ad utenti non proprio alle prime armi (per questi Estrema! È come dare pennelli, colori e tela bianca ad un
ultimi sono presenti dei Walkthrough nell’help) e, proprio pittore… Cosa volere di più?
per questo, c’è da fare un piccolo appunto alla qualità
del codice fornito, che in diversi casi (ad esempio per la
Qualità prodotto 
Ottima.
gestione della connessione ad un database che – come
ormai sanno anche i sassi – deve essere aperta il più Qualità documentazione −
tardi possibile e chiusa il prima possibile) è ben lontano
Il meno è dovuto al fatto che i tutorial in rari casi (ma
dalla sufficienza.
purtroppo ci sono) non mostrano un esempio di buona
Occhio quindi a non prendere come oro colato quello programmazione… Per iniziare possono andare bene,
che viene mostrato negli Starter Kit, cercando di utilizzarli ma se fossero stati fatti in modo migliore ne avremmo
come trampolino di lancio per entrare nel mondo .NET, guadagnato tutti.
non come punto di arrivo e di riferimento.

N. 64 - Luglio/Agosto 2005 VBJ 63


.NET TOOLS

SQL Server 2005 Express cui l’applicazione deve funzionare senza la presenza
di una connettività di rete, garantendo comunque
La versione Express di Sql 2005 prenderà il posto un’autonomia di funzionamento.
dell’attuale MSDN, senza però soffrire delle limitazioni Il tutto, come detto in precedenza, è supportato dalla
circa il numero di connessioni contemporanee suppor- presenza del Sql Server Express Manager, che mira
tate, dando quindi molto
più spazio ad un uso non
solamente personale ma
anche per piccole/medie
esigenze.
Oltre a questa grossa
ed attesa novità, un’al-
tra importante aggiunta
è quella – finalmente
– di uno strumento di
amministrazione e di
sviluppo, che prende
il nome di Sql Server
Express Manager.
La versione Express
supporta tutte le novità
introdotte da Sql Server
2005 senza escluderne
alcuna.
È pertanto possibile
sfruttare le potenzialità
fornite da XML, Web
Services, Service Broker,
.NET, le nuove estensio-
Figura 3 L’applicazione “videoteca” fornita come tutorial
ni al linguaggio T-SQL
e, interessantissimo, lo
sviluppo di report trami-
te i Reporting Services.
Tutto ciò “limitato” ad un
configurazione massima
che supporta:

 1 Processore
 1 GB di RAM
 4 GB per Database

che è una limitazione


in senso lato in quan-
to, tenendo presente
il target del prodotto,
ossia l’uso personale
o per piccole/medio-
piccole soluzioni, non
dovrebbe mai andare
stretta a nessuno.
Un’altra tipologia di
utilizzo della versione
Express è quella a sup-
porto di una versione
maggiore, in modo
poter disporre di un Figura 4 Sql Server 2005 Express Manager
supporto locale per
tutte quelle esigenze in

64 VBJ N. 64 - Luglio/Agosto 2005


.NET TOOLS

a fornire una console evoluta per l’accesso ai dati e


l’amministrazione degli stessi. Attualmente le funzioni
Prodotto
Sql Server 2005 Express
fornite dell’interfaccia grafica sono molto limitate, e
quindi ci si troverà a scriversi da soli il codice T-SQL Url di riferimento
per fare qualsiasi cosa: dal backup al restore, dalla http://lab.msdn.microsoft.com/express/
creazione di login e user, all’amministrazione dei per-
messi degli stessi.
Stato Release
CTP June (IDW 159
Questo sicuramente comporta un buon esercizio per
aumentare la propria familiarità con T-SQL (cosa ap- Semplicità d’uso 
prezzabile e consigliabile sempre a tutti), ma dal punto Il Sql Server Express Manager è un po’ carente nelle
di vista puramente “operativo” un po’ di funzionalità funzioni, in quanto attraverso l’interfaccia sono disponili
in più per aumentare la produttività ci sarebbero state solo quelle più comuni (e neanche tutte!)
sicuramente bene. Pazienza, vorrà dire che sopperi-
Utilità 
remo a tale mancanza creandoci e salvandoci script È uno dei migliori database in circolazione. C’è qualcosa
e funzioni personalizzate ☺. di più utile di un database, visto che al suo interno ci
La documentazione non è ancora fornita con la versio- andranno tutti i dati che vogliamo memorizzare?
ne in circolazione (essendo praticamente una Beta), ma è
possibile scaricarla successivamente a questo indirizzo: Qualità prodotto 
http://go.microsoft.com/fwlink/?LinkId=31046. Sql Server Express 2005 è funzionalmente identico alla
Se siete alle prime armi con Sql Server, prima di versione commerciale del prodotto, che, come detto sopra
lanciarvi a provare il prodotto ricordatevi che la ver- è un dei migliori in circolazione.
sione Express viene installata come Named Instance e Qualità documentazione 
pertanto per potersi collegare è necessario specificare Più che buona.
il seguente indirizzo del server: <nomemacchina>\
SQLEXPRESS.

N. 64 - Luglio/Agosto 2005 VBJ 65