SlideShare a Scribd company logo
1 of 155
Ing. Felice Pescatore
felice.pescatore@poste.it
Microsoft .Net Framework
DotNet FRAMEWORK
Architettura e Dettagli
Quale versione del Framework.NET?
Questo corso sarà incentrato sulla versione 2 del dotNet
Framework,nonostante sia stata rilasciata in versione definitiva la 3 e
sia quasi pronta la 3.5.
Ciò perché la versione 3 non modifica, sostanzialmente, l’architettura
della versione 2, ma aggiunge uno strato superiore con funzionalità
specifiche. Inoltre la maggior parte del software attualmente sviluppato,
la maggior parte del materiale didattico, così come le Certificazioni
Microsoft (MCTS e MCPD) si basano proprio sulla versione 2.
Anche se non esplicitamente evidenziato tutto quanto presentato di
seguito sarà comunque riferito alla versione 2.
A conclusione del corso verrà dedicato uno spazio proprio per presentare
le novità del dotNet 3.5, la cui uscita è prevista per gli inizi del 2008.
Certificazioni Microsoft: i percorsi
Application
Development
Foundation
Esame 70-536
Windows Based
Client Development
Esame 70-526
Design e Developing
Windows Based
Application
Esame 70-548
Web Based Client
Development
Esame 70-528
Design e Developing
Web Based
Application
Esame 70-547
Distribuited
Applicatin
Development
Esame 70-529
Design e Developing
Enterprise
Application
Esame 70-549
Certificazioni Microsoft: testi ufficiali
Certificazioni Microsoft: SQL Server
Esame 70-431
Certificazioni Microsoft: Compendi Consigliati
Serie Passo-Passo, di base
Serie Programming
• msdn2.microsoft.com/it-it/library/default.aspx
• www.ugidotnet.org
• www.aspitalia.com
• www.dotnet2themax.it
Autori:
•Francesco Balena
•Dino Esposito
•Marco Bellinaso
•David Sheppa
Architettura del dotNet Framework
CONCETTI INTRODUTTIVI
Cos’ è il dotNet Framework
Il Framework .NET e' la piattaforma di sviluppo di riferimento per il
mondo Windows, grazie alla quale e' possibile realizzare soluzioni
software a 360° in grado di rispondere alle esigenze più complesse.
I Punti di forza sono da
ricercarsi nella sconfinata
libreria di base, in un
architettura a strati che
permette una evoluzione
natura (.Net 3/3.5),
nell’indipendenza dal
linguaggio di
programmazione e nella
disponibilità del più evoluto
ambiente di sviluppo
attualmente esistente
(Visual Studio)
dotNet presenta un’architettura a strati. Al suo interno possiamo
distinguere gli elementi portanti dell’intero framework:
Il dotNET Framework
 CLR - Common Language Runtime
 BCL - Base Class Library
 ADO.NET & XML
 Windows Forms
 ASP.Net, Web Forms & Web
Services
 CLS – Common Language
Specification
 CTS – Common Type System
Il dotNET Framework: il CLR (1)
Il COMMON LANGUAGE RUNTIME (CLR) è uno strato posto al di sopra dei
servizi del sistema operativo. Esso è responsabile dell'esecuzione vera e propria
delle applicazioni: assicura che vengano rispettate tutte le dipendenze, gestisce la
memoria, la sicurezza, l'integrazione del linguaggio e così via.
Il runtime fornisce numerosi servizi che consentono di semplificare la stesura del
codice, la distribuzione dell'applicazione e di migliorare l'affidabilità della stessa.
Il .NET Framework: il CLR (2)
•Semplificare lo sviluppo
• Definire standard per il riuso del codice
• Fornire servizi come gestione della memoria e garbage collection
• Semplificare deployment delle applicazioni
•I componenti usano i metadati anzichè la registrazione
•Supportare più versioni
• Deployement da riga di comando XCOPY e disinstallazione DEL
• Eliminare l’uso del registro per i componenti e la necessità di codice extra
per la loro gestione.
• Supportare i linguaggi di sviluppo
•Fornendo classi di base per gli strumenti e i linguaggi degli sviluppatori
• Supportare più linguaggi di programmazione
•Definire i CTS utilizzati da tutti i linguaggi .NET
Con il CLR, Microsoft ha posto alcuni obiettivi cardine della propria tecnologia:
Il dotNET Framework: il CLR (3)
Volendo fare un paragone con il mondo JAVA, il CLR è l’equivalente .NET della
Java Virtual Machine (JVM): attiva gli oggetti, li sottopone a controlli di
sicurezza, li dispone in memoria.
Gli eseguibili .NET sono composti da Codice e Metadati. Questi ultimi contengono
la definizione di tipo, informazione sulla versione e riferimenti ad Assembly esterni.
Il Class Loader utilizza i metadati per il caricamento
delle classi .NET, mentre i compilatori Just in Time (JIT)
li utilizzano per compilare l’ Intermediate Language (IL)
Tutti i linguaggi .NET, attraverso il CLR, accedono al medesimo sistema di tipi e
alle stesse classi base, ottenendo cosi una convergenza tra Linguaggi e Modelli di
Programmazione
Il codice eseguito dal CLR viene detto CODICE GESTITO (MANAGED CODE) ed
è memorizzato come Codice+Metadati in un formato standard Windows PE
(portable executable) che consente di:
• Leggere i metadati
• Fornire lo stack del codice
• Gestire le eccezioni
• Recuperare informazioni di sicurezza
• Esistono “versioni del CLR” anche sotto
Linux e Mac Os X (vedi MONO) che
consentono di eseguire il codice in formato
PE anche in macchine non Windows
rendendo .NET un framework
multipiattaforma
Il dotNET Framework: il CLR (4)
Il dotNET Framework: la BCL
Difatti, prima del dotNet Framework, gli
Ingegneri del Software dovevano
pianificare la realizzazione di una
applicazione scegliendo preventivamente
il linguaggio e di conseguenza si
limitavano le librerie in base a quelle
supportate dallo stesso.
La BCL permette invece di scegliere,
teoricamente anche in fase di sviluppo, il
linguaggio più opportuno per il task da
eseguire, permettendone una sostanziale
interoperabilità, al patto di rispettare le
specifiche minime necessarie.
La Base Class Library (BCL) è l’insieme delle librerie funzionali comuni ad ogni
linguaggio basato sulla piattaforma dotNet, e comprende:
Tipi dato complessi (collezioni), Networking, Accesso al file system,
Interfaccia utente, Sicurezza,Programmazione concorrente, XML e molto
altro
Il dotNET Framework: ADO.NET
Praticamente tutte le applicazioni hanno la necessità di interrogare o aggiornare
dati persistenti memorizzati in file piatti, database relazionali o altri tipi di supporto
di memorizzazione.
Per ovviare a tale necessità, il .NET Framework include ADO.NET, un
sottosistema per l'accesso ai dati ottimizzato per ambienti N-tier ed interoperabile
con l'XML ed i documenti XML.
ADO.NET è stato progettato per gli ambienti debolmente accoppiati e per fornire
servizi per l'accesso ai dati ad applicazioni scalabili e servizi basati sul Web.
ADO.NET mette a disposizione API ad elevate
Prestazioni per modelli di dati sia connessi sia
Disconnessi particolarmente adatti alla
restituzione di dati alle applicazioni Web.
Il dotNET Framework: XML
XML svolge un ruolo fondamentale in nel .NET framework, sin dalla sua prima
versione, essendo utilizzato per descrivere la applicazioni (metadati) in modo
analogo ai deployment descriptor di Java. Inoltre è spesso la base di
comunicazione per lo scambio dei dati tra due applicazioni (marshaling e
serializzazione)
ASP.NET fornisce un modello applicativo sotto forma di CONTROLLI che
semplifica la creazione di applicazioni Web.
L’infrastruttura include un insieme di Controlli Lato Server che ricalcano gli
oggetti Widget delle tipiche interfacce utente HTML (tra cui, caselle di riepilogo,
caselle di testo e pulsanti), ed un insieme aggiuntivo di Controlli Web Evoluti
(come calendari e rotator). Essi sono posizionati attraverso drag&drop sulle
pagine, indicate in gergo Web Form.
I controlli vengono eseguiti in realtà sul server Web ed inviano la propria
interfaccia utente ad un browser sotto forma di HTML. Sul server, i controlli
espongono un modello di programmazione orientato all'oggetto che mette a
disposizione degli sviluppatori Web tutta la ricchezza di questo tipo di
programmazione.
Il dotNET Framework: ASP.NET
Il dotNET Framework: WindowsForms
Le WindowsForms sono praticamente le classiche finestre di
un’applicazione windows che contengono i classici elementi come
bottoni,combobox, aree di testo ecc.
Tutti i controlli posizionati su una WindowsForm (e la WindowsForm
stessa, che è ugualmente un controllo) risponde ad una serie di eventi
che lo sviluppatore può gestire attraverso l’implementazione di appositi
Handler.
Il dotNET Framework: ASP.NET - WebForms
Le WebForms sono l’equivalente delle WindowsForms,
eccetto che il loro ambiente di esecuzione è il Browser
Web. Esse vengono elaborate sul server e
successivamente passate al browser nel classico formato
HTML
La fase di implementazione di una WebForm non si
discosta molta da quella di una classica WindowsForm.
Il dotNET Framework: ASP.NET – XML Web Services
I Web Services sono una sorta di piccoli
servizi disponibili in rete, che forniscono
funzionalità specializzate. Essi vengono
interrogati attraverso il protocollo SOAP
basato su XML.
Il dotNET Framework: CTS
Il COMMON TYPE SYSTEM (CTS) definisce un insieme standard di tipi di dato e di regole
necessarie per la realizzazione di nuovi tipi, consentendo ai vari linguaggi di interoperare
correttamente tra loro
CTS fornisce due tipi principali entrambi ereditati da System.Object:
•Tipi a Valore, utili per rappresentare tipi di dati semplici e quelli definiti dall’utente
(ovvero le strutture);
•Tipi a Riferimento, ovvero: tipi di oggetti, tipi di interfacce e tipi di puntatori
Da notare che in .NET tutti i tipi sono orientati agli oggetti (ogni elemento è un oggetto) e i
tipi a valore possono essere convertiti in tipi a riferimento mediante il boxing
CTS definisce, inoltre, un modello uniforme per la gestione delle eccezioni,
indipendentemente dal linguaggio utilizzato (ovviamente con sintassi diverse).
Si tratta in praticamente di definire i seguenti passi:
 Lanciare un’eccezione
 Catturare un’eccezione
 Codice di uscita da un blocco controllato (finally)
Il dotNET Framework: CTS, Tipi a Valore
I Tipi a Valore non possono essere null (tranne i NULLABLE che sono un
discorso a parte) e devono sempre contenere dei dati. Possono essere: :
primitivi, strutture e enumerazioni;
Per creare un tipo a valore personalizzato anche derivandolo da una classe
System.ValueType, proprio come fa dotNet per i tipi primitivi. Prendendo ad
esempio il tipo int, si vede che esso non è nient’altro che un alias di
System.Int32 derivato appunto da Sysem.ValueType.
E’ utile sottolineare che il passaggio di un Tipo a Valore in una funzione
avviene per copia
Il dotNET Framework: CTS, Tipi a Riferimento
I Tipi a Riferimento rappresentano il riferimento ad oggetti allocati nell’Heap (a
differenza dei tipi a valore allocati sullo Stack), ed ammettono il valore Null.
Il passaggio a funzioni avviene per riferimento, cioè viene passato un indirizzo o
un puntatore ad un oggetto.
Questi tipi vengono gestiti dal CLR e sottoposti a GARBAGE COLLECTION.
CTS promuove la sicurezza di tipi in modo da
migliorare la stabilità del codice, attraverso
definizioni dei tipi completamente note e
che non possono essere alterate.
Ciò si traduce nel fatto che i riferimenti agli
oggetti siano strongly-typed,ovvero durante
la fase di assegnazione di un oggetto ad un
reference viene verificato se sono di tipo
compatibile.
Il dotNET Framework: CLS
Le COMMON LANGUAGE
SPECIFICATION, definiscono le
regole che un linguaggio deve
avere per essere gestito dal
framework, permettendo
l’interoperabilità tra i vari linguaggi
supportati dal dotNet Framework.
In pratica descrivo come il
compilatore deve trasformare il
codice sorgente nel codice
intermedio (Intermediate Language
– IL), per essere compatibile con le
specifiche del framework.
Il CLS, inoltre, definisce le API dei
componenti .NET
Esecuzione di applicazioni dotNET
Compilazione ed Esecuzione di codice dotNET
Source code
C++, C#, Visual
Basic o Altria
language .NET
es: Csc.exe o Vbc.exe
Compilatore
Assembly
DLL o EXE
L’applicativo, scritto in uno dei linguaggi supportati, viene compilato, generando così
un ASSEMBLY, che nonostante l’estensione,è un packaging composto da più elementi.
Compilazione e Generazione dell’Assembly
Un ASSEMBLY rappresenta il costituente fondamentale di un'applicazione .NET.
Si tratta dell’unità elementare di rilascio del codice e di gestione delle versioni, ed
è composto da:
•Un manifesto (Manifest);
•Un insieme di uno o più moduli (dll o exe);
•Un insieme opzionale di risorse
Tutti i tipi e le risorse gestiti sono contrassegnati in uno dei seguenti modi:
• accessibili solo all'interno della propria unità implementativa
• esportabili per essere utilizzati dal codice al di fuori di tale unità.
Assembly dotNet
Type Descriptions
Classes
Base classes
Implemented interfaces
Data members
Methods
Name
Version
Culture
Assembly Manifest
Other assemblies
Security permissions
Exported types
Assembly Manifest: Descrizione
• Stabilisce l'identità dell'assembly in
termini di nome, versione, livello di
condivisione tra applicazioni diverse,
firma digitale.
•Definisce quali file (nome e file hash)
costituiscono l'implementazione
dell'assembly.
•Specifica le dipendenze in fase di
compilazione da altri assembly.
•Specifica i tipi e le risorse che
costituiscono l'assembly, inclusi quelli
che vengono esportati dall'assembly.
•Specifica l'insieme dei permessi
necessari al corretto funzionamento
dell'assembly.
Gli assembly si autodescrivono tramite il proprio MANIFEST (manifesto), che
costituisce una parte integrante di ogni assembly stesso:
Assembly Manifest: Uso dei Metadati
I METADATI sono indipendenti dal linguaggio, consentendo a quelli che vi accedono
di interpretarli in modo univoco.
Questo permette di creare progetti (assistiti da IDE come Visual Studio) che
contengono al loro interno codice sorgente di linguaggi diversi, proprio grazie alle
informazioni contenute nei metadati dei componenti;
Il compilatore genera i metadati di un componente dal codice sorgente che
vengono memorizzati nel codice compilato in un formato PE (Portable Executable);
Un tools per la visualizzazione dei metadati è ildasm.exe
Assembly: Execution Model, Intermediate Language
Come detto il Codice Sorgente viene compilato nel linguaggio intermedio IL,
spesso indicato anche come MSIL (Microsoft IL) e CIL (Common IL).
Vediamo un esempio di IL:
.method private hidebysig static void Main()
cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "Hello, world!"
IL_0005: call void
[mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method HelloWorld::Main
Assembly: Execution Model
Gli assembly sono caricati in memoria dal CLR solo all’occorrenza:
• prima viene determinata la versione
• poi viene cercato l’assembly nella Global Assembly Cache (GAC) oppure nel
percorso locale indicato dal codice base;
Prima dell’esecuzione il CLR deve procedere alla compilazione dell’ IL (non eseguibile
direttamente dal processore) per la generazione di codice nativo.
Ci sono due possibilità:
• Compilazione del Metodo a Tempo di Esecuzione, Just In Time (JIT);
• Compilazione di tutto l’Assembly prima dell’esecuzione
Come già accennato in precedenza, il CLR permette di limitare le funzionalità del codice
eseguito.
Assembly: Execution Model Schema
Compilazione
Esecuzione
JIT
Compiler
Native
Code
MSIL
Code
Metadata
Source
Code
Language
Compiler
Assembly
(.exe o .dll)
Compilazione
Just in Time
Assembly: Deployment semplificato
Sicuramente il Deployment rappresenta uno dei grossi vantaggi di dotNet,
permettendo:
• Un’installazione senza effetti collaterali (dll Hell), le applicazioni e i
componenti possono sempre essere condivisi o privati;
• L’esecuzione Side-by-Side, diverse versioni dello stesso componente
possono coesistere, anche nello stesso processo
Il Deployment può avvenire in modi
diversi:
• XCOPY, per le applicazioni
ASP.NET
• .CAB, per le applicazioni
• Windows Forms - Code Download
• .MSI (Windows Installer), per le Applicazioni Windows Forms
l’ installazione in GAC di assembly condivisi, la
Configurazione di shortcut
Assembly Location
Avendo parlato di deployment è utile specificare le location che gli assembly
possono assumere in base alla loro tipologia:
 Assembly privati, directory applicazione (e sub-directory)
 Assembly condivisi, Global Assembly Cache (GAC)
(c:windowsassembly)
 Assembly scaricati da URL, download cache
(c:Documents and Settings%USERNAME%
Local SettingsApplication Dataassemblydl2)
Il Tool per esaminare GAC e download cache è GACUTIL.EXE
Oggetti “vivi” Oggetti non raggiungibili Spazio libero
Garbage Collection(1)
Fase 1: Mark
Durante l’esecuzione del codice gli oggetti non più utilizzati come vengono
deallocati dalla memoria?
dotNet distrugge automaticamente gli oggetti quando non sono più referenziati,
utilizzando un sofisticato algoritmo di Mark-and-Compact
NextObjPtr
Root set
www.devleap.it
Oggetti “vivi”
Spazio libero
NextObjPtr
Root set
Spazio recuperato
Garbage Collection (2)
Fase 2: Compact
Non sempre ci si può affidare in modo “cieco” al Garbage Collector, ma in alcuni
casi serve un comportamento di finalizzazione deterministica. In particolare
quando si hanno:
 Riferimenti a oggetti non gestiti
 Utilizzo di risorse che devono essere rilasciate appena termina il loro
utilizzo
Non si possono usare i finalizzatori (come si fa in C++), che sono richiamabili
direttamente, ma bisogna utilizzare l’interfaccia IDisposable, implementando
il metodo Dispose.
Garbage Collection(3)
Linguaggi del dotNet Framework
Come più volte ribadito fin ora , il dotNet Framework è LANGUAGE-
INDEPENDED, ovvero non dipende dal linguaggio scelto. Microsoft fornisce
direttamente il supporto (ed il compilatore) per:
•C++, C#, J#, VB 2005, Jscript
Esistono, comunque, implementazione di terze parti che spesso permettono di
portare sulla piattaforma dotNet linguaggi molto utilizzati:
•Perl, Ruby, Python, Pascal, APL, COBOL, Eiffel, Haskell, ML, Oberon,
Scheme, Smalltalk…
Chiunque, rispettando le CLS può realizzare (in teoria) il proprio linguaggio
dotNet Ready
Base Class ed Interfacce
Le Classi e le Interfacce della BCL
Base Class ed Interfacce: Namespaces
Base Class Library
Data Xml
Web Services User Interface
Base Class ed Interfacce: Namespaces
System
System.Data System.Xml
System.Web
Globalization
Diagnostics
Configuration
Collections
Resources
Reflection
IO
Threading
Text
Security
SqlClient
OleDb
SQLTypes
Common
Runtime
InteropServices
Remoting
Serialization
Configuration SessionState
Caching Security
UI
HtmlControls
WebControls
System.Drawing
Imaging
Drawing2D
Text
Printing
System.Windows.Forms
Design ComponentModel
XPath
Xsl
Serialization
Schema
Hosting
Handlers
Compilation
Base Class ed Interfacce: Namespaces
System
Threading
Text
Security
Resources
Reflection
IO
Globalization
Diagnostics
Configuration
Collections
Runtime
Serialization
Remoting
InteropServices
Base Class ed Interfacce: Namespaces
System.Data
System.Xml
OracleClient
OleDb
Sql/SqlClient
Common
Serialization
Schema
XPath
Xsl
Odbc Messaging
System.Web
Caching
Configuration
UI
SessionState
HtmlControls
WebControls
Security
Handlers
Hosting
Compilation
Management
Profile Services
dotNet Framework: Namespaces
System.Drawing
Drawing
Design
Printing
Text
System.Windows.Forms
Design ComponentModel
dotNet Framework: Namespaces
VisualStyles
Input / Output
Input e Output con dotNet
dotNet Framework: Input / Output
Una delle principali attività che ci si trova a realizzare è l’interazione con il (i) file
system.
dotNet mette a disposizione un insieme di classi raccolte nel NameSpace
System.IO, che permettono di effettuare operazioni di vario genere su file,
directory,ecc:
 FILEINFO and DIRECTORYINFO (base class: FileSystemInfo),
permettono di interrogare ogni elemento del file system per ottenerne
informazioni di vario genere
 DRIVEINFO, permette di ottenere informazione sulle periferiche di I/O
 FILE, DIRECTORY, PATH sono le “utility class” che permettono,
attraverso metodi statici, di effettuare varie operazioni sui corrispettivi
elementi
dotNet Framework: I/O, ottenere informazioni dal File System
FileInfo ourFile = new FileInfo(@"c:boot.ini ");
if (ourFile.Exists)
{
Console.WriteLine("Filename : {0}", ourFile.Name);
Console.WriteLine("Path : {0}", ourFile.FullName);
}
Ottenere informazione su un file
FileInfo ourFile = new
FileInfo(@"c:boot.ini");
ourFile.CopyTo(@"c:boot.bak");
Copiare un file Enumerare I file in una directory
DirectoryInfo ourDir = new
DirectoryInfo(@"c:windows");
Console.WriteLine("Directory: {0}",
ourDir.FullName);
foreach (FileInfo file in ourDir.GetFiles())
{
Console.WriteLine("File: {0}",
file.Name);
}
Per effettuare le operazioni di lettura e scrittura, il.Net Framework fornisce gli
STREAM (letteralmente flussi), che permettono un accesso sequenziale e
random ai dati.
La classe base (di tipo astratta) è Stream,che fornisce una serie di funzionalità
comuni a tutti gli stream specializzati.
Tra gli Stream Personalizzati:
•FileStream, specializzato nelle operazioni di lettura/scrittura dei file
•Memory Stream, crea un flusso in memoria (utili per operazioni
temporanee)
•Buffered Stream, crea un wrapper per lo stream specifico allo scopo di
migliorarne le performance;
•StreamReader (base class: TextReader), consente operazioni di lettura su
stream generici
•StreamWriter (base class: TextWriter), consente operazioni di scrittura su
stream generici
dotNet Framework: Gli Stream
dotNet Framework: aprire uno Stream su file
L’apertura degli Stream, relativi alla lettura/scrittura file, avviene attraverso una
serie di classi statiche ed enumeratori presenti nel Namespace System.IO:
•File, fornisce le funzionalità di base per leggere e scrivere da file;
•Directory, per effettuare operazioni sulle directory
•FileAccess Enumeration, che specifica i diritti (Read, Write, ReadWrite) da
applicare all’apertura di un file;
•FileMode Enumeration specifica i diritti sui file che si andranno ad aprire.
File.Open(@"C:boot.ini", FileMode.Open, FileAccess.Read);
File.Create(@"c:somefile.txt");
Aprire un file per la lettura
Creare un file
dotNet Framework: Leggere/Scrivere da un file
FileStream theFile = File.Open(@"C:boot.ini", FileMode.Open, FileAccess.Read);
StreamReader rdr = new StreamReader(theFile);
Console.Write(rdr.ReadToEnd());
rdr.Close();
theFile.Close();
StreamReader rdr = File.OpenText(@"C:boot.ini");
Console.Write(rdr.ReadToEnd());
rdr.Close();
FileStream theFile = File.Create(@"c:somefile.txt");
StreamWriter writer = new StreamWriter(theFile);
writer.WriteLine("Hello");
writer.Close();
theFile.Close();
File.WriteAllText(@"c:somefile.txt", "Hello");
Leggere da file
Scrivere su file
dotNet Framework: Usare il MemoryStream
MemoryStream memStrm = new MemoryStream();
StreamWriter writer = new StreamWriter(memStrm);
writer.WriteLine("Hello");
writer.WriteLine("Goodbye");
writer.Flush();
FileStream theFile = File.Create(@"c:inmemory.txt");
memStrm.WriteTo(theFile);
writer.Close();
theFile.Close();
memStrm.Close();
Scrittura su uno Stream in Memoria e successivo riversamento su file
dotNet Framework: Stream Speciali
Oltre agli stream che permetto una normale lettura/scrittura da file o memoria,
esistono una speciale categoria di stream definiti Compression Stream
Attualmente sono disponibili due Compression Stream:
•GZipStream, che permette la compressione compatibile con lo standard de-
facto zip;
•DeflateStream, che crea una compressione proprietaria.
In realtà entrambi gli stream usano lo stesso algoritmo per la compressione. La
differenza sta nel fatto che la compatibilità con lo standard zip richiede un header
apposito e quindi un ulteriore (anche se lieve) aggravio di risorse.
Una sostanziale differenza con gli stream classici, come vedremo dagli esempi, è che
i compression stream scrivono i dati su stream di appoggio (file, memoria).
Infine, è da precisare, che entrambi i compression stream possono gestire al
massimo 4Gb di dati.
L’esempio che segue mostra la compressione/decompressione con la classe GZipStream. Per
utilizzare il DelfateStream basta sostituire l’istruzione di creazione dell‘oggetto
FileStream sourceFile = File.OpenRead(inFilename);
FileStream destFile = File.Create(outFilename);
GZipStream compStream = new GZipStream(destFile, CompressionMode.Compress);
int theByte = sourceFile.ReadByte();
while (theByte != -1)
{
compStream.WriteByte((byte)theByte);
theByte = sourceFile.ReadByte();
}
FileStream sourceFile = File.OpenRead(inFilename);
FileStream destFile = File.Create(outFilename);
GZipStream compStream = new GZipStream(sourceFile, CompressionMode.Decompress);
int theByte = compStream.ReadByte();
while (theByte != -1)
{
destFile.WriteByte((byte)theByte);
theByte = compStream.ReadByte();
}
dotNet Framework: Comprimere/Decomprimere con gli stream
Compressione
Decompressione
dotNet Framework: Working with Text
Lavorare con le Stringhe ed il Testo
dotNet Framework: Working with Text
 Lavorare con le stringhe di testo è un task comune per tutti
gli sviluppatori
 .Net mette a disposizione un insieme di classi raccolte nel
NameSpace System.Text, che permettono di effettuare
operazioni di vario genere sulle stringhe:
 StringBuilder, permette una manipolazione efficiente delle stringhe;
 Regex, Match, Group, permettono l’elaborazione delle stringhe attraverso le
Espressioni Regolari;
 Encode/Decode, sono specializzate nella codifica/decofica delle stringhe nei
vari formati internazionali.
dotNet Framework: La classe StringBuilder e la classe String
• Normalmente si è portati ad utilizzare la classe String per le operazioni sulle
stringhe.
• In realtà un oggetto di tipo String è un oggetto atipico, perché ogni operazione
su di esso (tipo l’aggiunta di nuovi caratteri alla stringa), non modifica l’oggetto
esistente, bensì ne crea uno nuovo aggiornando il reference.
• Per migliorare le performance (ed evitare inutili sprechi di memoria) il .Net
framework prevede la classe StringBuilder, che crea un oggetto stringa dinamico
System.Text.StringBuilder sb = new System.Text.StringBuilder(30);
sb.Append("wombat");
sb.Append(" kangaroo");
sb.Append(" wallaby");
sb.Append(" koala");
string s = sb.ToString();
Console.WriteLine(s);
dotNet Framework: Regular Expression
• Le Espressioni Regolari (Regular Expression) sono una eredità del mondo Unix
e del linguaggio PERL.
• Rappresentano un modo efficiente per effettuare elaborazioni complesse sulle
stringhe di testo. Come esempio, la seguente Regular Expression, permette di
validare un indirizzo email:
• Nella pratica l’utilizzo di Espressioni Regolari può risultare anche molto
complesso e non approfondiremo uteriormente l’argomento.
•Comunque il modo più semplice per verificare se una stringa supera la verifica
rispetto ad una Espressione Regolare (MATCH) è quello di usare la classe Regex
e il metodo statico IsMatch:
^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$
Regex.IsMatch("pattern", @"ApatternZ")
dotNet Framework: Codifica del Testo
• Vista l’eterogeneità dei sistemi informatici esistenti, non meraviglia che esistano
diversi modi di codificare (o meglio, rappresentare) le stringhe.
• La codifica più utilizzata nello scorso decennio è stata quella ASCII (7bit), ormai
insufficiente per codificare adeguatamente anche caratteri speciali (come le
lettere degli alfabeti orientali).
•Oggi la codifica più utilizzata è la Unicode UTF-16 (16 bit), che è anche la
codifica base utilizzata dal framework .Net.
•Nel caso si renda necessario lavorare con codifiche differenti, è possibile
utilizzare le classi Encode/Decode come mostrato dall’esempio seguente:
// Get Korean encoding
Encoding e = Encoding.GetEncoding("Korean");
// Convert ASCII bytes to Korean encoding
byte[] encoded;
encoded = e.GetBytes("Hello, world!");
// Display the byte codes
for (int i = 0; i < encoded.Length; i++)
Console.WriteLine("Byte {0}: {1}", i, encoded[i]);
dotNet Framework: Collection & Generics
Lavorare su insiemi di oggetti
dotNet Framework: Collection & Generics
Ogni framework che si rispetti deve avere un’ampia serie di collezioni (collection)
che permettono di gestire un insieme di oggetti, più o meno omogeneei,
attraverso i classici costrutti dei moderni linguaggi.
dotNet contempla al suo interno l’implementazione delle COLLECTION
CLASSICHE e dei GENERICS
La differenza sostanziale tra le due è che le Collection Classiche memorizzano al
loro interno gli oggetti attraverso il casting, mentre nel caso dei Generics la
categoria di oggetti contenuti nella collection è tipizzata.
Le Generics portano in dote vantaggi di performance (molto limitati in realtà), ma
soprattutto una programmazione type-safe, in cui è il compilatore a verificare il
corretto utilizzo della collezzione.
dotNet Framework: Collection Classiche
Le Collection Classiche sono contenute nel NameSpace System.Collections e
annoverano:
•ArrayList, una semplice collezione ridimensionabile ed indicizzata;
•SortedList, una collezione ordinata composta da coppie nome/valore;
•Queue, la classica coda; struttura FIFO (first in first out);
•Stack, ovvero last in last out, LIFO;
•Hashtable, una collezione nome/valore;
•BitArray, una collezione compatta di valorie Boolean;
•StringCollection, collezione specializzata per le stringhe;
•StringDictionay, ovvero una hashtable specializzata per le stringhe;
•ListDictionary, una collezione nome/valore ottimizzata per piccole quantità
di dati;
•HybridDictionary, una collezione nome/valore ibrida. Per piccole quantità di
dati si comporta come una ListDictionary, altrimenti come una Hashtable
La System.Collctions prevede tre interfacce di base:
•IEnumerable (rende enumerabile la lista),
•ICollection (rende omogenee le funzionalità di base),
•IList (estende le funzionalità di base comuni).
La System.Collctions prevede tre interfacce di base:
•IEnumerable (rende enumerabile la lista),
•ICollection (rende omogenee le funzionalità di base),
•IList (estende le funzionalità di base comuni).
Grazie a queste tre interfacce è possibile operare uniformemente sulle varie
Collection Classiche, attraverso i classici metodi: insert, remove, add, e così via.
dotNet Framework: Collection Classiche
Nota a parte merita la possibilità di effettuare l’ordinamento (collection.Sort) di una
Collezione. Infatti l’ordinamento varia in base al tipo di oggetto inserito nella
collezione Per questo è possibile definire un propria classe di ordinamento (che
deve implementare l‘interfaccia IComparer) e della quale un’istanza può essere
passata al metodo Sort:
coll.Sort(new CaseInsensitiveComparer());
 I generics sono stati introdotti
dalla versione 2 del framework
e sono formati da classi e
metodi che lavorano in modo
uniforme su tipi differenti
 Benefici
 Le variabili sono di un tipo
ben preciso e non Object
 Non è necessario un cast
(errori in fase di
compilazione)
 Riutilizzo reale del codice
class Stack<T>
{
private T[] store;
private int size;
public Stack() {
store = new T[10];
size = 0;
}
public void Push(T x) {
// push code goes here
}
public T Pop() {
return store[--size];
}
}
void Add(Stack<int> s) {
int x = s.Pop();
int y = s.Pop();
s.Push(x+y);
}
dotNet Framework: Generics
dotNet Framework: Generics
Tra le implementazioni si annoverano:
• List<>, ovvero una semplice lista di elementi;
• Queue<>, la coda (FIFO);
• Stack<>, lo stack (LIFO);
• Dictionary<>, collezione nome/valore;
• SortedList<>, lista di elementi ordinati;
• SortedDictionay <>, collezione nome/valore ordinata;
• NameValuePair<>, coppia nome/valore (in pratica è ritornato dall’iterazione sul
dictionary);
• LinkedList<>, lista linkata;
Tra le interfacce troviamo:
•IList<ItemType>
•IDictionary<K, V>
•IEnumerable<ItemType>
•IComparable<OperandType>
ArrayList myList = new ArrayList();
myList.Add(1); // Viene effettuato il boxing (value -> refernce)
myList.Add(2); // Viene effettuato il boxing (value -> refernce)
myList.Add(“3"); // Provocherà un errore a runtime
int i = (int)myList[0]; // Necessario effettuare il cast
List<int> myList = new List<int>();
myList.Add(1); // Nessun boxing
myList.Add(2); // Nessun boxing
// myList.Add(“3"); // Errore a tempo di compilazione
int i = myList[0]; // Nessun casting richiesto
Non-generic
Generic
dotNet Framework: Generics vs. Collection Classiche
Rappresentazione senza Generics Rappresentazione con Generics
intobject
intobject
intobject
intobject intobject
int int
int
int
int
int int
Push Pop
Box Unbox
Push Pop
dotNet Framework: Generics vs. Collection Classiche
dotNet Framework: Serialization
Persistere lo Stato di un Oggetto
dotNet Framework: Serialization
La Serializzazione permette di salvare un oggetto (e, quindi, il suo attuale stato)
in modo persistente su un supporto di memorizzazione, ma anche di inviare lo
stesso oggetto attraverso le reti per un utilizzo remoto.
Il dotNet Framework permette la serializzazione attraverso opportune classi
contenute nel namespace System.Runtime.Serialization:
•BinayFormatter, permette di serializzare un oggetto in formato binario e
trasferirlo su uno stream;
•SoapFormatter , permette di serializzare un oggetto in formato XML
aderente allo standard SOAP
e nel namespace System.Xml.Serialization
•XmlSerializer, permette di serializzare un oggetto in formato XML
dotNet Framework: Serialization
Il dotNet framework utilizza un modo semplice ed immediato per rendere
serializzabile un oggetto:
[Serializable]
class ShoppingCartItem
{
public int productId;
public decimal price;
…
…
<Serializable()> Class ShoppingCartItem
Public productId As Integer
Public price As Decimal
…
… VBC#
basta, in pratica, inserire l’attributo [serializable] per rendere la classe
serializzabile.
La serializzazione comunque può essere parziale, escludendo alcuni elementi,
come di seguito mostrato:
' VB
<NonSerialized()> Public total As Decimal
// C#
[NonSerialized] public decimal total;
dotNet Framework: Serialization, IDeserializationCallback
Nel caso in cui si abbiano dei membri (variabili di istanza) calcolati, si può decidere di non
serializzarne il valore (per risparmiare spazio), facendolo ricalcolare durante la
serializzazione attraverso l’interfaccia IDeserializationCallback ed il relativo metodo
IDeserializationCallback.OnDeserialization
[Serializable]
class ShoppingCartItem : IDeserializationCallback {
public int productId;
public decimal price;
public int quantity;
[NonSerialized] public decimal total;
public ShoppingCartItem(int _productID, decimal _price, int _quantity)
{
productId = _productID;
price = _price;
quantity = _quantity;
total = price * quantity;
}
void IDeserializationCallback.OnDeserialization(Object sender)
{
/ / After deserialization, calculate the total
total = price * quantity;
}
}
dotNet Framework: Binary Serialization
Come detto, la serializzazione avviene mediante le classi prima descritte e
riversando i dati ottenuti su uno stream. Vediamo alcuni esempi:
string data = "This must be stored in a file.";
FileStream fs = new FileStream("SerializedString.Data", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
// Use the BinaryFormatter object to serialize the data to the file
bf.Serialize(fs, data);
fs.Close();
FileStream fs = new FileStream("SerializedString.Data", FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
// Create the object to store the deserialized data
string data = "";
data = (string) bf.Deserialize(fs);
fs.Close();
Serializzazione
Deserializzazione
Se si volesse utilizzare la serializzazione compatibile con lo standard SOAP, basta
sostiture BinaryFormatter con SoapFormatter
dotNet Framework: XML Serialization
Nel caso in cui si voglia rendere il risultato della serializzazione il più portabile
possibile, si può ricorrere alla serializzazione XML.
L’oggetto da serializzare, in questo caso, deve seguire regole più stringenti:
• la classe deve essere pubblica
• tutti i membri da serializzare devono essere pubblici
• deve esistere un costruttore senza parametri espressamente dichiarato
// Create file to save the data to
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Create);
// Create an XmlSerializer object to perform the serialization
XmlSerializer xs = new XmlSerializer(typeof(DateTime));
xs.Serialize(fs, System.DateTime.Now);
fs.close();
// Open file to read the data from
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Open);
// Create an XmlSerializer object to perform the deserialization
XmlSerializer xs = new XmlSerializer(typeof(DateTime));
DateTime previousTime = (DateTime)xs.Deserialize(fs);
fs.close();
Serializzazione
Deserializzazione
dotNet Framework: Custom Serialization
Se le precedenti forme di serializzazione non sono adeguate ai nostri scopi (caso
molto raro) possiamo procedere a realizzare una serializzazione personalizzata.
Ciò si può ottenere implementando l’Interfaccia Iserializable (conseguentemente il
metodo GetObjectData) ed applicando l’attributo [serializable] alla classe.
Tuttavia esiste un metodo più immediato, ma ovviamente meno flessibile, per
controllare la serializzazione e quindi personalizzarla: i Serialization Events.
Si tratta di eventi che si scatenano utilizzando il BinaryFormatter e sono:
•Serializing, scatenato prima di inizializzare la serializzazione;
•Serialized, scatenato dopo la serializzazione;
•Deserializing, scatenato prima della deserializzazione;
•Deserialized, scatenato dopo la deserializzazione;
è possibile catturare questi eventi, e gestirli nel modo più opportuno, creando degli
appositi metodi all’interno della classe da serializzare e applicandovi l’attributo
corrispondente all’evento da catturare: [OnDeserialized]
void CheckTotal(StreamingContext sc)
{
if (total == 0) { CalculateTotal(sc); }
}
dotNet Framework: Graphics
Grafica elementare
dotNet Framework: Graphics
dotNet offre gli strumenti base per la creazione di semplici elementi grafici (linee,
cerchi, ecc.), tutti contenuti nel namespace System.Drawing.
Attraverso gli strumenti offerti è possibile:
•Aggiungere forme all’UI in modo dinamico;
•Creare diagrammi;
•Editare e ridimensionare immagini;
•Cambiare il grado di compressione di una immagine;
•Zoommare immagini;
•Aggiungere un logo di copyright o del testo ad una immagine.
Tra le classi primarie di questo namespace troviamo: Bitmap, Brush, Font,
Graphics, Icon, Image e Pen.
Un ruolo importante rivestono inoltre le strutture di questo namespace (ad es.
Color o Point), che permettono di impostare o modificare gli elementi del disegno.
dotNet Framework: Graphics
Per disegnare una linea o una figura base, bisogna eseguire tre passi
fondamentali:
•Creare un oggetto Graphics, partendo dalla form o dal controllo attuale,
attraverso il metodo System.Windows.Forms.Control.CreateGraphics;
•Creare un oggetto Pen;
•Chiamare un metodo dell’oggetto Graphics per disegnare sul controllo usando
l’oggetto Pen
// Create a graphics object from the form
Graphics g = this.CreateGraphics();
// Create a pen object with which to draw
Pen p = new Pen(Color.Red, 7);
// Draw the line
g.DrawLine(p, 1, 1, 100, 100);
Graphics g = this.CreateGraphics();
Pen p = new Pen(Color.Blue, 3);
g.DrawPie(p, 1, 1, 100, 100, -30, 60);
dotNet Framework: Graphics
Invece di utilizzare l’oggetto Pen, in abbinamento con il metodo graphics.drawXXX,
è possibile utilizzare un oggetto Brush (e il metodo graphics.fillXXX) per poter
creare figure con riempimento.
Graphics g = this.CreateGraphics();
Brush b = new SolidBrush(Color.Maroon);
Point[] points = new Point[]
{new Point(10, 10),
new Point(10, 100),
new Point(50, 65),
new Point(100, 100),
new Point(85, 40)};
g.FillPolygon(b, points);
dotNet Framework: Graphics
Grazie alle implementazioni della classe astratta System.Drawing.Image, è
possibile elaborare immagini esistenti o crearne di nuove. Questa classe astratta
trova nel framework due implementazione:
•System.Drawing.Bitmp, per le immagini;
•System.Drwaing.Imaging.Metafile, per le immagini animate;
Nonostante Image sia una abastract class è possibile utilizzare i metodi
implementati per ottenerne un’istanza:
Image i = Image.FromFile(@"C:windowsgone fishing.bmp");
pictureBox1.BackgroundImage = i;
Se si vuole creare e salvare una
nuova immagine si lavoro pressappoco
come negli esempi precedenti,
eccetto che la creazione
dell’oggetto graphics avviene
attraverso un metodo apposito:
Bitmap bm = new Bitmap(600, 600);
Graphics g = Graphics.FromImage(bm);
Brush b = new LinearGradientBrush(new Point(1, 1),
new Point(600, 600), Color.White,Color.Red);
Point[] points = new Point[]
{new Point(10, 10),
new Point(77, 500),
new Point(590, 100),
new Point(250, 590),
new Point(300, 410)};
g.FillPolygon(b, points);
bm.Save("bm.jpg", ImageFormat.Jpeg);
dotNet Framework: Graphics
Attraverso le funzionalità di System.Drawing è possibile creare anche testo sotto
forma di immagine, utile, ad esempio, se si desidera “marchiare” una foto con un
proprio identificativo.
Graphics g = this.CreateGraphics();
Font f = new Font("Arial", 40, FontStyle.Bold);
g.DrawString("Hello, World!", f, Brushes.Blue, 10, 10);
Infine il namespace in esame prevede una classe apposita per
disegnare/visualizzare le icone di sistema:
Graphics g = this.CreateGraphics();
g.DrawIcon(SystemIcons.Question, 40, 40);
dotNet Framework: Threading
Esecuzione Concorrente di Attività
dotNet Framework: Threading
Grazie all’utilizzo dei Thread è possibile dotare l’applicazione di più flussi paralleli
che possono portare ad un sostanziale miglioramento prestazionale della stessa ed
a una migliore interazione utente-applicazione.
Il namespace di riferimento è il System.Threading, che annovera la classe Thread
e l’enumerazione ThreadState.
La creazione di un nuovo thread, effettuabile in qualsiasi parte del codice, avviene
attraverso i seguenti passi:
1. Creare un metodo senza parametri e senza argomenti di ritorno:
2. Creare un delegato ThredStart, specificando il metodo creato nel passo 1
3. Creare un nuovo oggetto Thread, specificando il treadstart creato al passo
2
4. Chiamare il metodo Thred.Start per avviare il thread
static void SimpleWork()
{
Console.WriteLine("Thread: {0}",
Thread.CurrentThread.ManagedThreadId);
}
dotNet Framework: Threading
In sintesi ecco il codice per avviare un thread:
Se è necessario passare informazioni al thread è possibile utilizzare il delegate
ParameterizedThreadStart:
ThreadStart operation = new ThreadStart(SimpleWork);
// Creates, but does not start, a new thread
Thread theThread = new Thread(operation);
// Starts the work on a new thread
theThread.Start();
ThreadStart operation = new ThreadStart(SimpleWork);
for (int x = 1; x <= 5; ++x)
{
Thread theThread = new Thread(operation);
theThread.Start();
}
static void WorkWithParameter(object o)
{
string info = (string) o;
for (int x = 0; x < 10; ++x)
{
Console.WriteLine("{0}: {1}", info,
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
}
}
ParameterizedThreadStart operation =
new ParameterizedThreadStart(WorkWithParameter);
Thread theThread = new Thread(operation);
theThread.Start("Hello");
hread newThread = new Thread(operation);
newThread.Start("Goodbye");
Il parametro passato deve essere sempre un oggetto.
dotNet Framework: richiamo
I DELEGATI (Delegate) sono una sorta di CallBack (per chi conosce C++) o più
semplicemente puntatori a funzione (C).
Essi vengono ampiamente usati in .Net per la gestione degli eventi, quindi per la
definizione dell’Handler.
La grande differenza rispetto ai linguaggi non gestiti è che i Delegati sono type-
safe, ovvero vengono gestiti esattamente come un oggetto la cui signature è
ben definita.
dotNet Framework: Threading
La classe Thread fornisce i metodi necessari per gestire in modo corretto il flusso di
eleborazione:
• Thread.Start, avvia il thread
• Thread.Join, ferma l’esecuzione del chiamante finchè il nuovo thread non
termina;
• Thread.Abort, abortisce l’esecuzione del thread e solleva una
ThreadAbortException;
• Thread.IsAlive, verifica se il thread corrente è in esecuzione;
• Thread.Priority, ritorna/setta la priorità del thread;
• Thread.IsBackground, ritorna/setta lo stato di background del thread;
dotNet Framework: Threading
Per consentire la condivisione di memoria (quindi dei parametri) tra i vari thread
evitando collisioni, il dotNet framework fornisce opportuni strumenti che ne
regolano l’accesso.
Se stiamo lavorando con variabili condivise ed abbiamo necessità di fare
operazioni di in(de)cremento possiamo avvalerci delle funzionlità della classe
Interlocked:
la classe interlocked “blinda” il parametro su cui opera, rendendo le operazioni
atomiche
static void UpdateCount()
{
for (int x = 1; x <= 10000; ++x)
{
Interlocked.Increment(ref Counter.Count);
}
}
dotNet Framework: Threading
Se abbiamo bisogno di “blindare” più istruzioni in modo da renderle atomiche,
possiamo ricorre al lock:
in realtà il lock è basato sulla classe Monitor che permette un più fine controllo sul
codice da blindare.
Il dotNet framework supporta inoltre i seguenti metodi di sincronizzazione:
• Mutex class
• Semaphore class
• AutoResetEvent class
• ManualResetEvent class
public void UpdateCount()
{
lock (this)
{
count = _count + 1;
if (Count % 2 == 0)
evenCount = evenCount + 1;
}
}
dotNet Framework: Threading
Non è sempre necessario creare un nuovo thread per avviare attività in parallelo.
Infatti il framework dotNet implementa l’ Asynchronous Programming Model (APM), che
permette l’esecuzione di porzioni di codice in thread separati.
Molte classi supportano APM fornendo le versioni BeginXXX e EndXXX di metodi classici.
Come esempio immaginiamo la lettura di un file: potremmo voler realizzare la lettura (lenta)
attraverso un flusso parallelo per non bloccare la fruibilità dell’applicativo. Invece di creare un
thread separato, possiamo procedere come di seguito:
byte[] buffer = new byte[100];
string filename = string.Concat(Environment.SystemDirectory, "mfc71.pdb");
FileStream strm = new FileStream(filename, FileMode.Open, FileAccess.Read,
FileShare.Read, 1024, FileOptions.Asynchronous);
// Make the asynchronous call
IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null);
// Do some work here while you wait
// Calling EndRead will block until the Async work is complete
int numBytes = strm.EndRead(result);
// Don't forget to close the stream
strm.Close();
Console.WriteLine("Read {0} Bytes", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
dotNet Framework: Threading
L’utilizzo del modello APM pone una domanda fondamentale: quando verificare la
fine del flusso parallelo avviato?
Possiamo individuare tre stili:
•Wait-until-done, consiste nell’avviare la richiesta, eseguire (eventualmente)
altre operazioni ed attendere il termine della richiesta;
•Polling, simile al precedente solo che c’è un controllo ciclico del termine della
richiesta;
•Callback, si crea un delegato che viene passato alla BeginXXX e viene
invocato dal framework al termine della richiesta.
L’ultimo aspetto da rilevare è che eventuali exception vengono sollevate solo in
seguito alla chiamata della EndXXX e non nel momento in cui si verificano
dotNet Framework: Threading
…..
// Make the asynchronous call
strm.Read(buffer, 0, buffer.Length);
IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null);
// Do some work here while you wait
// Calling EndRead will block until the Async work is complete
int numBytes = strm.EndRead(result);
…..
// Make the asynchronous call
IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null);
// Poll testing to see if complete
while (!result.IsCompleted)
{
// Do more work here if the call isn't complete
Thread.Sleep(100);
}
// Finished, so we can call EndRead and it will return without blocking
int numBytes = strm.EndRead(result);
….
….
// Make the asynchronous call
IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, new
AsyncCallback(CompleteRead), strm);
}
…
//Callback Metod
static void CompleteRead(IAsyncResult result)
{
Console.WriteLine("Read Completed");
FileStream strm = (FileStream) result.AsyncState;
// Finished, so we can call EndRead and it will return without blocking
int numBytes = strm.EndRead(result);
// Don't forget to close the stream
strm.Close();
Console.WriteLine("Read {0} Bytes", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
}
dotNet Framework: Threading
dotNet Framework: Application Domain & Services
Isolare il Codice Sconosciuto
Spesso è necessario lavorare con assembly esterni, che possono impattare sia
sulle performace/stabilità del nostro applicativo, sia sulla sicurezza.
Il modo migliore per utilizzare un assembly esterno è quello di utilizzare un
Application Domain.
dotNet Framework: Application Domain & Services
Processo
AppDomain
Assembly
Shared Assembly
AppDomain
Assembly
Shared Assembly
Grazie agli Application Domain è possibile caricare, all’interno dello stesso
processo, più assembly separatamente tra di loro, in modo che eventuali anomalie
non vadano ad impattare l’intero processo o gli altri assembly
La classe che permette la gestione degli Application Domain è la
System.AppDomain. Vediamo ora alcuni esempi concreti di utilizzo degli
Application Domain:
dotNet Framework: Application Domain & Services
AppDomain d = AppDomain.CreateDomain("NewDomain");
AppDomain d = AppDomain.CreateDomain("NewDomain");
d.ExecuteAssemblyByName("Assembly");
AppDomain d = AppDomain.CreateDomain("NewDomain");
…..
AppDomain.Unload(d)
Creazione
Caricamento di un Assembly
Unload di un Assembly
E’ bene notare che se sa in anticipo di voler effettuare l’unload dell’assembly che si
va a caricare, l’unico modo per farlo è attraverso un Application Domain
Come ci si aspetterebbe, per poter controllare il livello di isolamento di un
Application Domain (quindi utilizzarlo come una sandbox) il dotNet framework
mette a disposizione una serie di strumenti per configurarlo, attraverso gli
Evidence, di cui discuteremo in seguito. Basti sapere che essi permettono di
classificare l’AppDomain ed assegnargli delle restrizioni e dei permessi sulle attività
da svolgere.
Possiamo applicare le restrizioni sia per un singolo assembly e sia per l’intero
Application Domain:
dotNet Framework: Application Domain & Services
object [] hostEvidence = {new Zone(SecurityZone.Internet)};
Evidence internetEvidence = new Evidence(hostEvidence, null);
AppDomain myDomain = AppDomain.CreateDomain("MyDomain");
myDomain.ExecuteAssembly("SecondAssembly.exe", internetEvidence);
object [] hostEvidence = {new Zone(SecurityZone.Internet)};
Evidence appDomainEvidence = new Evidence(hostEvidence, null);
AppDomain d = AppDomain.CreateDomain("MyDomain", appDomainEvidence);
d.ExecuteAssembly("SecondAssembly.exe");
Attraverso la classe System. AppDomainSetup è infine possibile controllare ogni
ulteriore aspetto di configurazione (dir di base, configuration file, ecc.)
I Windows Services (servizi di windows) sono una gamma di applicativi privi di UI
che, funzionando in background, svolgono attività cicliche monitorando lo stato del
sistema.
La creazione di un servizio avviene utilizzando il template di VS ServiceBase, ed
implementando il metodi OnStart e OnStop come minimo.
Successivamente bisogna creare un’Installer per il servizio che non può essere
avviato come semplici applicativo.
Una volta installato, il servizio può essere gestito sia attraverso il classici Strumenti
di Amministrazione di Windows che attraverso codice (quindi da un assembly
esterno) attraverso la classe System.ServiceProcess.ServiceController:
dotNet Framework: Application Domain & Services
// Connect to the Server service
ServiceController sc = new ServiceController("Server");
sc.Stop(); // Stop the service
// Wait two seconds before starting the service
Thread.Sleep(2000);
sc.Start(); // Start the service
dotNet Framework: Configuring Application
Rendere dinamica un’applicazione attraverso la
sua configurazione
dotNet Framework: Configuring Application
Parola d’ordine: evitare le configurazioni HardCoding, ovvero quelle inserite
all’interno del codice e quindi impossibili da modificare se non attraverso i sorgenti.
Sarà capitato a tutti, ad esempio, di inserire direttamente all’interno della propria
classe la stringa di connessione ad un DB e di dover poi rimettere mano al codice
perché l’RDBMS di rilascio usa credenziali diverse di quelle del sistama di Test
.
Ebbene, grazie agli strumenti messi a disposizione dal namespace
System.Configuration e ai file di confirgurazione XML sarà davvero semplice
evitare queste situazioni. Questo namespace include due classi principali:
ConfigurationManager e Configuration.
Di default ogni applicazione ha un proprio file di configurazione utilizzato per la sua
inizializzazione: nomeapplicazione.exe.conf
dotNet Framework: Configuring Application
Vediamo un esempio di file di configurazione:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<supportedRuntime version="v1.1.4322" />
</runtime>
<appSettings>
<add key="Foo" value="Hello World!"/>
</appSettings>
<connectionStrings>
<clear/>
<add name="AdventureWorksString" providerName="System.Data.SqlClient“
connectionString="Data Source=localhost; Initial Catalog=AdventureWorks; Integrated
Security=true"/>
</connectionStrings>
</configuration>
dotNet Framework: Configuring Application
In esso possiamo distinguere tre sezioni principali:
• <runtime>, in cui sono riportate le impostazioni riguardanti l’esecuzione
dell’assemby . Nel caso in esempio la versione del framework supportata.
•<appSettings>, sono parametri di cui si serve l’applicazione e che vengono
recuperati durante la sua esecuzione
•<connectionStrings>, questa sezione è dedicata a contenere esclusivamente
le stringhe di connessione alle base dati (possono essere più di una)
// C#
NameValueCollection AllAppSettings = ConfigurationManager.AppSettings;
Console.WriteLine(AllAppSettings["Foo"]);
Console.WriteLine(AllAppSettings[0]);
' VB
Dim AllAppSettings As NameValueCollection = ConfigurationManager.AppSettings
Console.WriteLine(AllAppSettings("Foo"))
Console.WriteLine(AllAppSettings(0))
Vediamo ora come recuperare i valori appSettings salvati nel file di Configurazione:
dotNet Framework: Configuring Application
La sezione ConnectionStrings è leggermente diversa poiché può contenere
dichiarazioni multiple:
possiamo sia recuperare la prima connectionString:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<clear/>
<add name="AdventureWorksString“ providerName="System.Data.SqlClient“ connectionString="Data
Source=localhost;Initial Catalog=AdventureWorks; Integrated Security=true"/>
<add name="MarsEnabledSqlServer2005String“ providerName="System.Data.SqlClient“
connectionString= "Server=Aron1;Database=pubs;
Trusted_Connection=True;MultipleActiveResultSets=true" />
</connectionStrings>
</configuration>
ConnectionStringSettings MySettings = ConfigurationManager.ConnectionStrings[0];
sia iterare su esse ottenendole la collection:
ConnectionStringSettingsCollection ConnectionStrings = ConnectionStringsSection.ConnectionStrings;
dotNet Framework: Configuring Application
Oltre al file di configurazione dell’applicazione dotNET prevede la possibilità di
creare molteplici file XML per evitare proprio l’hard coding e quindi contenere
coppie parametri/valori. Tali file vengono poi gestiti direttamente da una classe
wrapper specifica.
<userSettings>
<WindowsApplication2.SampleSettings />
</userSettings>
<applicationSettings>
<WindowsApplication2.SampleSettings>
<setting name="WebServiceUrl" serializeAs="String">
<value>http://www.adatum.com/myservice.asmx</value>
</setting>
</WindowsApplication2.SampleSettings>
</applicationSettings>
nell‘esempio sono visibili 2 aree userSettings e applicationSetting che
suddividono lo scope dei parametri.
dotNet Framework: Configuring Application
Per definire un nuovo file di configurazione ed auto-generare la relativa classe è
conveniente affidarsi a VS:
Aggiungere un nuovo elemento al
progetto di tipo Settings File
Dopo la creazione del Settings File, VS
aprirà un apposito Application Settings
Designer, che potrà essere utilizzato per
definire i parametri in ogni loro aspetto
(nome, tipo, visibilità-scope, valore).
dotNet Framework: Configuring Application
Salvando il nuovo file di settaggio, troveremo due nuovi elementi nel nostro
progetto:
1. Sample.settings, il file XML
2. Sample.cs (.vb), la relativa classe autogenerata
Per usare i parametri basta creare una nuova classe del tipo al punto 2 e
richiamarne le proprietà:
' VB
Dim mySettings as new SampleSettings()
Debug.WriteLine(mySettings.WebServiceUrl)
// C#
SampleSettings mySettings = new SampleSettings();
Debug.WriteLine(mySettings.WebServiceUrl);
dotNet Framework: Instrumentation
Ottimizzare la propria Applicazione
dotNet Framework: Instrumentation
Sotto il nome Instrumentation Micorsoft ha raccolto tutti gli strumenti (ovviamente
parliamo di classi, interfacce, enumeratori, ecc.) utilizzabili per:
• Gestire i Log
• Effettuare il Debugging ed il Tracing
• Monitorare le Performance
• Controllare i Device di Sistema, le Applicazioni ed il Sistema stesso
Tutti questi “strumenti” sono contenuti nel namespace System.Diagnostics
Da notare che operando direttamente a contatto con il sistema e siano di semplice
utilizzo, essi possono impattare sulle performance del sistema ed inoltre provocare
problemi di sicurezza nelle classi partial-trust (in particolare per i Log).
Quindi essi vanno utilizzati con parsimonia e molta attenzione.
dotNet Framework: Instrumentation
Praticamente è impossibile conoscere tutti gli scenari in cui la nostra applicazione
verrà eseguita, vista l’infinita possibilità di combinazioni Hw/Sw oggi possibili.
Inoltre nonostante ci si sforzi di creare applicazioni prive di bug, qualcuno, prima o
poi, ne verrà fuori.
Il problema è che sono sempre gli utenti finali ad accorgersi dei bug più insidiosi, e
difficilmente essi riescono a segnalare il problema in modo corretto (in fin dei conti
perché dovrebbero saperlo farlo, non sono certo dei tecnici!).
Proprio in questo scenario ci vengono in aiuto gli strumenti di Logging Events,
che permettono alla nostra applicazione di inserire informazioni specifiche
all’interno dei log (siano essi quelli di sistema o quelli proprietari dell’applicazione).
Tutto il meccanismo si basa sulla classe EventLog, attraverso la quale scrivere o
legge un evento in un log è un’operazione banale:
public static void CreateEventLog()
{
EventLog DemoLog = new
EventLog("Chap10Demo");
DemoLog.Source = "Chap10Demo";
DemoLog.WriteEntry("CreateEventLog
called", EventLogEntryType.Information);
}
public static void ReadEventLog()
{
EventLog DemoLog = new EventLog();
DemoLog.Log = "Chap10Demo";
foreach (EventLogEntry DemoEntry in
DemoLog.Entries)
{
Console.WriteLine(DemoEntry.Source +
":" + DemoEntry.Message);
}}
dotNet Framework: Instrumentation
La fase di Debugging è una delle più importanti nello sviluppo di un nuovo
applicativo, in quanto permette di analizzarne il comportamento alla ricerca di errori
nella logica di funzionamento.
Il dotNet framework basa l’azione di debugging su due classi fondamentali (sempre
del namespace Systems.Diagnostics): Debugger e Debug
Debugger permette di abilitare la connessione ad un debugger (operazione svolta
automaticamente dall’IDE VS). Infatti difficilmente si utilizzerà in modo diretto tale
classe, essendo possibile utilizzare gli strumenti integrati dell’IDE.
Ad esempio uno dei metodi di Debugger è debugger.Break() che permette di
inserire un punto di break-point all’interno del codice, esattamente come si fa
cliccando al lato sinistro in Visual Studio.
Prima di proseguire con la più interessante classe Debug è utile sottolineare che
tutte le operazioni connesse vengono eseguite solo se l’ambiente si è in fase di
debugging. In fase di compilazione per deployment, le righe di codice relative
vengono scartate. Nel caso si vogliano utilizzare tali funzionalità anche in ambiente
di deployment si fa ricorso alla classe Trace funzionalmente identica.
dotNet Framework: Instrumentation
La classe Debug consente un controllo fine sul processo di debugging, attraverso
metodi diretti, tra cui il più importante è sicuramente Debug.Assert.
Assert permette di testare se una determinata condizione è vera e, in caso
affermativo, generare un alert:
Similare ad Assert è Fail che permette di lanciare un alert senza verificare una
condizione.
Altri metodi presenti sono: debug.write, debug.writeif, debug.writeline,
debug.writelineif, debug.print, debug.flush.
NameValueCollection MySettings = ConfigurationManager.AppSettings;
Debug.Assert(MySettings != null && MySettings["Foo"] != null,
"There was a problem with either the configuration file or the Foo Setting");
Console.WriteLine(MySettings["Foo"]);
dotNet Framework: Instrumentation
Il dotNet framework permette di personalizzare la visualizzazione delle informazioni
che il debugger visualizza e come esso si comporta attraverso opportuni attributi,
che andiamo ad elencare:
•DebuggerBrowsableAttribute
•DebuggerDisplayAttribute
•DebuggerHiddenAttribute
•DebuggerNonUserCodeAttribute
•DebuggerStepperBoundaryAttribute
•DebuggerStepThroughAttribute
•DebuggerTypeProxyAttribute
•DebuggerVisualizerAttribute
la loro utilità dipende dal grado di dettaglio che lo sviluppatore vuole ottenere
durante il debugger e una loro padronanza è possibile solo con l’utilizzo pratico.
dotNet Framework: Instrumentation
Anche l’applicazione scritta in modo perfetto (teroricamente) non troverà
gradimento da parte del cliente se non risponde in modo tempestivo alle sue
richieste: ecco il concetto di performace.
Non ci addentreremo nel dettaglio di queste funzionalità (gestite attraverso le classi
Process e il componente/oggetto PerformanceCounter) anche perché si tratta di
attività di ottimizzazione dell’applicazione che vengono, purtroppo, raramente
effettuate.
La cosa da tener presente è che quando si verifica un problema non sempre esso
è dovuto ad un errore o un flusso di istruzioni errate, ma può anche essere dovuto
a scarse performance dell’ambiente che il cliente sente come errore
dell’applicativo.
dotNet Framework: Instrumentation
L’ultimo set di strumenti messi a disposizione dal namespece Systems.Diagnostics
sono i Windows Management Instrumentation (WMI).
Attraverso di essi è possibile ottenere informazioni riguardanti ogni aspetto delle
risorse del computer, come: Drive, Adattatori di Rete, Servizi, ecc.
WMI utilizza una serie di passi ed un linguaggio di interrogazione simile alle query
SQL, attraverso la classe DirectoryObjectSearcher. Infatti per ottenere
informazioni specifiche bisogna:
1. Dichiarare un’istanza di ConnectionOptions, e settare le proprietà UserName e
Password (per motivi di security di accesso);
2. Dichiarare un’istanza di DirectoryObjectSearcher;
3. Creare un oggetto ManagementScope e settarne le proprietà PathName e
ConnectionOptions
4. Creare un oggetto ObjectQuery e specificare la query da eseguire;
5. Creare un oggetto ManagementObjectCollection, e assegnargli il valore di ritorno
del metodo DirectoryObjectSearcher.Get;
dotNet Framework: Instrumentation
Vediamo un esempio concreto che consente l’enumerazione dei drive presenti nel sistema:
ConnectionOptions DemoOptions = new ConnectionOptions();
DemoOptions.Username = "Bill";
DemoOptions.Password = "mp99!!swa0";
ManagementScope DemoScope = new ManagementScope("machinename", DemoOptions);
ObjectQuery DemoQuery = new ObjectQuery("SELECT Size, Name FROM Win32_LogicalDisk
where DriveType=3");
ManagementObjectSearcher DemoSearcher = new ManagementObjectSearcher(DemoScope, DemoQuery);
ManagementObjectCollection AllObjects = DemoSearcher.Get();
foreach (ManagementObject DemoObject in AllObjects)
{
Console.WriteLine("Resource Name: " + DemoObject["Name"].ToString());
Console.WriteLine("Resource Size: " + DemoObject["Size"].ToString());
}
In modo altrettanto semplice è possibile ottenere informazioni riguardanti i Servizi di Sistema:
private static void ListPausedServices()
{
ManagementObjectSearcher DemoSearcher = new ManagementObjectSearcher("SELECT * FROM
Win32_Service WHERE Started = FALSE");
ManagementObjectCollection AllObjects = DemoSearcher.Get();
foreach (ManagementObject PausedService in AllObjects)
{
Console.WriteLine("Service = " + PausedService["Caption"]);
}}
dotNet Framework: Application Security
Garantire la Sicurezza delle proprie Applicazioni
dotNet Framework: Application Security
Realizzare applicazioni SICURE che non effettuino operazioni indesiderate e non
consentite sui sistemi degli utilizzatori è ormai diventato un must.
Attraverso le CAS, Code Access Security, è possibile limitare in modo granuale quello che il
managed code può fare.
Il concetto è simile ai diritti applicati ai ruoli degli utenti/gruppi (Role Based Security – RBS)
all’interno del Sistema Operativo.
Con CAS è possibile controllare l’accesso a:
• File System
• Registro
• Stampanti
• Log degli Eventi
• Accesso al Web
• …
CAS è utilizzabile solo per controllare managed code, mentre il codice non gestito
(come, tra l’altro, il codice gestito Full-Trust) non è soggetto alle restrizioni.
Inoltre in nessun caso il codice eseguito può avere più permessi di quelli
concessi all’utente che lo esegue.
CAS si compone di 5 elementi fontamentali: Evicende, Permission, Permission
Sets, Code Group e Security Policy.
dotNet Framework: Application Security
Prima di continuare è utili far presente che la difficoltà maggiore nell’apprendere le
CAS è nella terminologia usata, piuttosto che negli strumenti in se.
• Evidence Prove di protezione
• Strong name Nome sicuro
• Code Group Gruppi di codice
• Full trust Attendibilità totale
• Partial trust Attendibilità parziale
• Security Policy ...
• Permission Autorizzazioni
• Permission Set Set di autorizzazioni
• Stack Walk ...
• Assert ...
• Demand ...
• ... ...
dotNet Framework: Application Security
Per riconoscere e classificare un assembly CAS utilizza l’EVIDENCE (ovvero: chi
sei?), una sorta di identificativo dell’assembly.
Evidence è la prova oggettiva per sapere di chi è o da dove proviene il codice
managed
La CAS può identificare un assembly basandosi su:
•Cartella dove risiede
•Sito o Url da cui viene scaricato
•Zona in cui si trova
•Strong Name presente nel suo manifest
•Hash dell'assembly
•Firma digitale (Authenticode, stessa degli Activex)
Al caricamento dell'assembly in memoria, viene subito ricavata l'evidence.
È possibile da codice associare una evidence ad un AppDomain in modo da isolare
degli assembly.
dotNet Framework: Application Security
CAS utilizza le PERMISSION (ovvero: cosa vuoi fare?) per consentire ad un
assembly di porter effettuare determinate operazioni (per esempio l’apertura di una
File Dialog Box).
Il dotNet framework ne predefinisce un certo numero, trasversali a quelli più noti
come NTFS, SQL, etc.
Per ciascun permesso è possibile definire dei valori molto dettagliati, come mostra
l’empio relativo all’accesso ai socket:
dotNet prevede 19 permessi base contenuti nel namespace
System.Security.Permissions, realative ad altrettante attività che possono
impattare sulla sicurezza del sistema.
Questi 19 permessi vengono spesso combinati tra loro per creare i Permission
Sets ovvero set di permessi da applicare unitariamente ad un assembly.
dotNet Framework: Application Security
Il dotNet framework prevede 7 Permission Sets di default:
Permission Set Description
FullTrust Esenta l’Assembly dall’essere sottoposto alle restirzioni CAS
SkipVerification Permette all’Assembly di baipassare i controlli, aumentando le
performance a scapito della sicurezza.
Execution Garantisce all’Assembly solo il permesso di esecuzione e nessun altro
Nothing Nessun permesso concesso, nemmeno quello di esecuzione
LocalIntranet Garantisce un consistente numero di permessi, compreso quello di
stampa e di accesso ai logo. L’accesso al File System è invece limitato alla
sola apertura delle Open/Save dialog Box
Internet Garantisce un ristretto numero di permessi, proprio in virtù della
provenienza dell’assembly (internet) da un ambiente potenzialmente
ostile.
Everithing Garantisce tutti i permessi, ma a differenza del FullTrust l’assembly è
comunque soggetto al controllo CAS.
dotNet Framework: Application Security
In pratica grazie all’evidenze ed ai permission il codice gestito viene prima
controllato e se le autorizzazioni di cui dispone sono sufficiente all’attività che si
appresta a svolgere, ciò viene concesso.
Esecuzione
Permission
Evidence
CodiceAutenticazione
Autorizzazione
CAS
dotNet Framework: Application Security
Grazie ai Code Groups è possibile collegare indirettamente i Permission Sets agli
Assembli, consentendo in tal modo di riflettere una modifica del Pemission Sets
applicato al Code Groups a tutti gli assembly relativi.
Lo schema segue l’assegnazione degli utenti ai gruppi a cui sono associati i
permessi (RBS Role Based Security).
Permissi
on Sets
Code
Grups
Assembly
(assegnati al
gruppo
attraverso
l’evidence)
Di default esistono 5 Code Groups:
Code Group Evidence Permission Set
My_Computer_Zone Zone: My
Computer
FullTrust
LocalIntranet_Zone Zone: Local
Intranet
LocalIntranet
Internet_Zone Zone: Internet Internet
Restricted_Zone Zone: Untrusted
sites
Nothing
Trusted_Zone Zone: Trusted
sites
Internet
Ovviamente un assemby può
appartenere a più Code Grups. In tal
caso i permessi sono Il risultato
dell’unione dei permessi dei singoli
gruppi.
dotNet Framework: Application Security
L’ultimo elemento che compone le CAS è la SECURITY POLICY un insieme di
code group e permission sets che definiscono le politiche (policy) di sicurezza del
sistema.
Per default esistono 4 policy level di defalut: Enterprise, Machine (o Computer),
User (o Utente) e Application Domain, che consentono un controllo minuto dei diritti
concessi.
Come è facile intuire dai nomi essi vanno dal livello più alto dell’ambiente
(Enterprise) a quello più ristretto (Application Domain).
Volendo essere semplici possiamo dire che le restrizioni Enterprise si applicano a
tutto l’ambiente fino a giungere alle restrizioni di Application Domain specifiche del
dominio di esecuzione.
I diritti di cui un assembly dispone sono il risultato dell’intersezione delle policy dei
vari livelli
Enerprise
Machine
User
App. Domain
Al runtime viene fatto il calcolo dei permessi effettivi:
1.
2.
Unione
Intersezione
void SetAppDomainPolicy(PolicyLevel domainPolicy);
dotNet Framework: Application Security
dotNet Framework: Application Security
CAS può essere configurato sia tramite riga di comando, attraverso l’utility CasPol,
sia attraverso il Framework Configuration Toolkit
Code Groups
Permission Set
Security Policy
Policy Assemblies
dotNet Framework: Application Security
Fin ora abbiamo visto come gestire/impostare le CAS attraverso gli strumenti di sistema forniti
dal framework.
Ovviamente esiste la possibilità di impostare le permission di un assembly in modo esplicito
direttamente dal codice.
Per fare ciò è possibile ricorre a due soluzioni:
1. Declarative Security, fa uso di attributi ed è l’unico applicabile all’intero assembly;
2. Imperative Security, fa uso di apposite classi ed è utilizzabile per proteggere
specifiche porzioni di codice.
L’utilizzo degli attributi (quindi declarative) è utile anche all’amministratore di sistema per
determinare di quali diritti ha bisogno il nostro assembly.
[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:boot.ini")]
namespace DeclarativeExample
{
class Program {
…. Dichiarativo
Try {
// Assembly logic
}
Catch {
EventLogPermission errorPerms = new EventLogPermission(PermissionState.Unrestricted);
errorPerms.PermitOnly();
// Log event
CodeAccessPermission.RevertPermitOnly();
} Imperativo
Lo strumento principale sono le classi xxxPermission e relativi attributi che
permettono di chiedere alla CAS di eseguire controlli e, se necessario
lanciare la SecurityException
ISecurityEncodable
IStackWalk
IPermission
FileIOPermission UIPermission EnvironmentPermssion
PermissionSet
FileIOPermission
Attribute
UIPermission
Attribute
EnvironmentPermssion
Attribute
CodeAccessPermission
Attribute
CodeAccessPermission
dotNet Framework: Application Security
Nel caso si voglia applicare la CAS ad un assembly (obbligatoriamente in modo
Dichiarativo), bisogna definire la Action Property secondo uno dei seguenti valori
dell’enumerazione SecurityAction:
• RequestMinimum. La permission indicata è indispensabile per l'assembly
altrimenti l'assembly non viene caricato.
• RequestOptional. Permessi aggiuntivi rispetto al minimo indispensabile. Vengono
dati all'assembly solo se disponibili.
• RequestRefuse. Permessi che questo assembly decide di togliersi per evitare di
essere usato da codice malicious.
Se sono specificati RequestOptional e RequestRefuse allora verranno attribuiti
all'assembly solo quei permessi anche se le policy sono più alte.
Il tool PermView permette di vedere nell'assembly i requisiti di security
dotNet Framework: Application Security
SecurityException
• Demand
• disponibile anche come attributo
• verifica il permesso richiesto eseguendo lo
stack walk
• notare che Metodo4 non è soggetto ad alcun
controllo. Il controllo è sui chiamanti
• LinkDemand
• solo come attributo
• verifica il permesso richiesto solo sul chiamante
senza eseguire stack walk
• il controllo viene eseguito dal jitter
• InheritanceDemand
• solo come attributo
• verifica il permesso richiesto sulle classi che
derivano da quella su cui è applicato l'attributo
• il controllo viene eseguito al load
OK
Demand
Stack walk
Metodo1
Metodo2
Metodo3
Metodo4
permission
negata
Stack walk
// Richiesta imperativa ...
FileIOPermission perm = new FileIOPermission(
FileIOPermissionAccess.AllAccess, @"c:");
perm.Demand();
// ... oppure dichiarativa (classe/metodo)
[assembly:FileIOPermission(
SecurityAction.Demand, All = @"C:") ]
dotNet Framework: Application Security
Nel caso si voglia applicare la CAS a elementi del codice come classi, metodi o
parte di esso (in modo Diarativo o Imperativo), bisogna definire i cosiddetti stack
walk modifiers:
• Assert e RevertAssert
• Impediscono che la richiesta di permesso
si propaghi ai chiamanti
• Assert si chiama al più tardi e
RevertAssert al più presto
• Utile per evitare perdite di performance
dovute al controllo della CAS
• Deny e RevertDeny
• Impedisce che tutti i metodi chiamati da questo possano accedere alla risorsa (...)
• PermitOnly e RevertPermitOnly
• Permette a tutti i metodi chiamati da questo di poter accedere solo alla risorsa
specificata (...)
Gli stack walk modifiers vengono rimossi automaticamente alla fine del
metodo che li chiama oppure chiamando esplicitamente il corrispondente
Revert
Deny e PermitOnly si usano su un metodo chiamante mentre RequestRefuse
solo sull'assembly chiamato
OK
Demand
Stack walk
Metodo2
Metodo3
Metodo4
permission
negata
Assert
dotNet Framework: Application Security
Metodo1
dotNet Framework: Application Security
[FileIOPermission(SecurityAction.Demand, Write = @"C:Program Files")]
public static void createProgramFolder()
{
// Method logic
}
[WebPermission(SecurityAction.Demand, ConnectPattern = @"http://www.microsoft.com/.*")]
public static void requestWebPage()
{
public static void createProgramFolder()
{
try {
FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Write, @"C:Program Files");
filePermissions.Demand();
// Method logic
}
catch {
// Error-handling logic
}
}
public static void requestWebPage()
{
try {
Regex connectPattern = new Regex(@"http://www.microsoft.com/.*");
WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern);
webPermissions.Demand();
// Method logic
}
catch {
// Error-handling logic
}
}
Vediamo due esempi di una stessa restrizione attraverso l’uso dichiarativo e imperativo:
Dichiarativo
Imperativo
dotNet Framework: Application Security
Fin qui il discorso relativo alle CAS.
Poniamoci però un dubbio: cosa accade se il nostro applicativo ha bisogno di uno spazio
temporaneo su disco ma non ha sufficienti permission?
In tal caso si ricorre all’Isolated Storage, ovvero un area protetta e riservata su disco su cui
l’assembly può lavorare senza problemi.
Si può definire uno storage isolato sulla base di:
• Identità del codice dell'applicazione
• Identità dell'utente
e gestirlo attraverso il namespace dedicato: System.IO.IsolatedStorage
Come è prevedibile la lettura/scrittura nello storage isolato avviene tramite stream:
IsolatedStorageFile userStore = IsolatedStorageFile.GetUserStoreForAssembly();
IsolatedStorageFileStream userStream = new IsolatedStorageFileStream("UserSettings.set",
FileMode.Create, userStore);
StreamWriter userWriter = new StreamWriter(userStream);
userWriter.WriteLine("User Prefs");
userWriter.Close();
dotNet Framework: Application Security
Abbiamo detto che il CAS è applicato al codice managed e agli assembly Partially Trusted.
Se vogliamo, invece, rendere un assemply Full Trusted, dobbiamo firmarlo, ovvero
assegnargli uno Strong Name
Le specifiche ECMA raccomandano l'uso dello strong name (che dovrebbe essere "aziendale"
e ben custodito) solo per il versioning e MAI per sicurezza, questo perché esistono sistemi
per ingannare il CLR e far caricare un Assembly tampered anche se firmato con strong name.
Comunque lo Strong Name è necesarrio qualora si voglia registrare l’assembly nella GAC
(Global Assembly Cache)
Infine, se un assembly partially trusted chiama un full trusted con strong name, questo
necessita dell'attributo: [assembly:AllowPartiallyTrustedCallers]
dotNet Framework: Interoperation
Interoperare con altre tecnologie
dotNet Framework: Interoperation
Se si decide di utilizzare le potenzialità del dotNet Framework possiamo affidarci
tranquillamente alla sua infrastruttura e guardare con convinzione al managed code.
Esiste tuttavia la possibilità che, per svariati motivi, ci si trovi in una delle situazioni seguenti:
• necessità di riutilizzare componenti COM (Component Object Model) proprietari;
• necessità di creare componenti COM da integrare in sistemi legacy preesistenti
• necessità lavorare direttamente con le API di sistema;
Per assisterci in tutti e tre i casi, dotNet ci viene in aiuto con gli strumenti messi a disposizione
dal namespace: System.Runtime.InteropServices
dotNet Framework: Interoperation
Mettiamo che la nostra software house abbia impiegato mesi per sviluppare uno
specifico componente COM (analisi, progettazione, sviluppo, testing,
collaudo,ecc.), myCom.
Ora nuove necessità hanno portato l’azienda a sposare il dotNet Framework.
In questo nuovo contesto si desidera però recuperare myCom.
Come si può procedere?
Anche in questo caso il dotNet Framework ci da una mano, mettendoci a
disposizione gli strumenti per creare un wrapper chiamato Runtime Callable
Wrapper (RCW) che lavoro secondo il pattern proxy.
Detto in soldoni: si esegue un automatismo del framework (o
Visual Studio), si ottiene il wrapper e lo si usa come una normale
l libreria dotNet.
Purtroppo non sempre i tool automatizzati riescono a generare
l’RCW.In tal caso bisogna procedere manualmente come
mostrato a breve.
dotNet Framework: Interoperation
Per creare RCW possiamo servirci di Visual Studio o del tool da riga di comando
TlbImp.exe.
Nel caso si usi VS, basta selezionare il
progetto (nella finestra Esplora Soluzioni),
clicckare con il tasto destro e scegliere
Aggiungi Riferimento.
Nella finestra che si aprirà scegliere il Tab
“COM” e quindi il proprio componente.
Una volta confermata l’operazione VS, se tutto
va a buon fine,aggiunge il riferimento
dell’oggetto COM alla libreria e a quel punto è
possibile utilizzarlo come un normale
componete/libreria dotNET.
Se si desidera operare attraverso TlbImp, basta procedere dando il seguente comando:
il risultato sarà una libreria .dll con lo stesso nome dell’originale, da
referenziare nel nostro progetto dotNet.
tlbimp <dllname>.dll
dotNet Framework: Interoperation
Nell’uso di oggetti COM in .NET bisogna fare attenzione in particolare a due aspetti:
• COM supporta i parametri opzionali (così come VBASIC),mentre ciò non è vero per
C#. Per poter essere eseguito bisogna, in C#, comunque passare tutti i parametri, anche
se opzionali. dotNet in tal senso introduce un oggetto vuoto attraverso la classe
Type.Missing, che è possibile utilizzare allo scopo ed evidenziare subito il suo
significato (evitando la confusione che si creerebbe con oggetti “dummy” ovvero creati
solo per sopperire al problema). In realtà una best-practices è quella di usare tali oggetti
anche in VB, poiché Microsoft sembra intenzionata ad eliminare il supporto ai parametri
opzionali
' VB
Imports Microsoft.Office.Core
Imports Microsoft.Office.Interop.Excel
Dim NewExcelApp As New Microsoft.Office.Interop.Excel.Application
'This works fine
NewExcelApp.Worksheets.Add()
// C#
using Microsoft.Office.Core
using Microsoft.Office.Interop.Excel
Application NewExcelApp = new Application();
// This will not comipile.
NewExcelApp.Worksheets.Add();
dotNet Framework: Interoperation
Best practices
' VB
Module Module1
Private OptionalParamHandler As Object = Type.Missing
Sub Main()
Dim NewExcelApp As New Microsoft.Office.Interop.Excel.Application
NewExcelApp.Worksheets.Add(OptionalParamHandler, OptionalParamHandler, _
OptionalParamHandler, OptionalParamHandler)
End Sub
End Module
// C#
class Program
{
private static Object OptionalParamHandler = Type.Missing;
static void Main(string[] args)
{
Application NewExcelApp = new Application();
NewExcelApp.Worksheets.Add(ref OptionalParamHandler, ref OptionalParamHandler,
ref OptionalParamHandler, ref OptionalParamHandler);
} }
dotNet Framework: Interoperation
Il Secondo aspetto da tenere presente è la Gestione delle Eccezioni, ovvero come catturare
e rispondere alle eccezioni generate da un oggetto COM – RCW.
A differenza di quanto accedeva con la versione 1.x del dotNet Framework, nella versione 2 è
stata introdotta la classe RuntimeWrappedException appartenente al namespace
System.Runtime.CompilerServices, che permette di gestire le eccezioni COM esattamente
come se fossero eccezioni standard del CLS.
Al verificarsi di un’eccezione COM, dotNet crea un oggetto di tipo
RuntimeWrappedException, ed assegna alla sua proprietà WrappedException l’oggetto
stesso che ha catturato l’eccezione per consentire di gestirlo in modo appropriato.
Se per qualsiasi motivi si dovesse avere la necessità di gestire eventuali eccezioni COM
attraverso il nuovo formato, è possibili disabilitarle attraverso appositi attributi:
' VB
Imports System.Runtime.CompilerServices
[assembly: runtimeCompatibility(WrapNonExceptionThrows=false)]
// C#
using System.Runtime.CompilerServices;
[assembly: RuntimeCompatibility(WrapNonExceptionThrows=false)]
dotNet Framework: Interoperation
Private Sub IllustrateExceptions()
Try
‘Something that throws an exception
Catch ex As Exception
' In the previous versions this will catch only CLS-Compliant
' In the current version both CLS and Non CLS-Compliant will be caught by this block.
End Try ' There is no equivalent for Catch without an exception
' because it's considered unreachable.
End Sub
private static void IllustrateExceptions()
{
try{
// Something that throws an exception
}
catch (Exception ex){
// In the previous versions this will catch only CLS-Compliant
// In the current version both CLS and Non CLS-Compliant will be caught by this block.
}}
Gestione Eccezioni COM “2.0”
dotNet Framework: Interoperation
Oltre ad avere la necessità di riutilizzare componenti COM, potremmo trovarci nella situazione
di doverne creare di nuovi, per poter aggiungere funzionalità ad applicazioni esistenti.
Anche i tal caso dotNet ci viene in aiuto con un wrapper apposito: COM Callable
Wrapper (CCW), attraverso il quale possiamo esportare il componente scritto in dotNet in
modo che sia compatibile con COM.
Per creare la nostra nuova libreria COM-Compatibile usando VS, dobbiamo :
1. Creare un nuovo progetto
2. Aprire la dialog box “Propietà del Progetto”, clicckando con il tasto destro sul
progetto e scegliendo proprietà.
3. Spuntare l’opzione Registra per COM
4. Compilare il tutto
Creato il componente sarà il compilatore a preoccuparsi
di aggiungere le necessarie informazioni per renderlo
compatibile con COM.
dotNet Framework: Interoperation
Per creare correttamente un componente esportabile in COM, bisogna seguire alcune regole:
• Tutte le classi devono avere un costrutto senza parametri esplicitamente
dichiarato;
• Tutti i tipi che si vuole rendere visibili devono essere pubblici;
• Tutti i metodi che si vuole rendere visibili devono essere pubblici;
• Le classi Astratte non possono essere esportate;
Public Class ComVisiblePerson
Private _firstName As String
Private _lastName As String
Public Property FirstName() As String
Get
Return Me._firstName
End Get
Set(ByVal value As String)
Me._firstName = value
End Set
End Property
Public Property LastName() As String
Get
Return Me._lastName
End Get
Set(ByVal value As String)
Me._lastName = value
End Set
End Property
End Class
namespace NetForComDemoCS
{
class ComVisiblePerson
{
private String firstName;
private String lastName;
public String FirstName
{
get { return firstName; }
set { firstName = value; }
}
public String LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
Attraverso appositi attributi è
poi possibile indicare quali
Assembly e Metodi devono
essere visibili o meno come
COM:
' VB
<Assembly: ComVisible(False)>
// C#
[assembly: ComVisible(false)]
' VB
<ComVisible(False)> _
Public Class ComVisiblePerson
// C#
[ComVisible(false)]
class ComVisiblePerson
dotNet Framework: Interoperation
Abbiamo parlato fin ora di oggetti COM, ma cosa accade se abbiamo bisogno di utilizzare una
libreria non conforme allo standard COM o API di sistema non supportate direttamente dal
framework?
Utilizziamo il cosiddetto Platform Invoke (P/Invoke), ovvero andiamo a wrappare
manualmente la nostra dll:
Imports System.Text
Imports System.Runtime.InteropServices
Public Class WindowExample
Private Const BufferSize As Int32 = 256
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow()
As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function GetWindowText(ByVal _
hWnd As IntPtr, ByVal textValue As StringBuilder,
ByVal counter As Int32) As Int32
End Function
…
using System.Runtime.InteropServices;
namespace OptionalCS
{
class WindowExample
{
private const Int32 BufferSize = 256;
[DllImport("user32.dll")]
private static extern IntPtr
GetForegroundWindow();
[DllImport("user32.dll")]
private static extern Int32 GetWindowText(IntPtr
hWnd, StringBuilder textValue, Int32 counter);
….
dotNet Framework: Interoperation
Il modo migliore per lavoare con P/Invoke, è quello di creare una classe wrapper per ogni dll
che si vuole utilizzare (Encapsulating) e utilizzare solo quest’ultima all’interno del codice.
Questo “incapsulamento” porta a oggettivi vantaggi, quali:
• Gli utilizzatori della classe non si renderanno conto del fatto che sia una classe-
wrapper di una dll
• L’utilizzo nel proprio progetto della dll sarà nascosto, evitando di usare i
formalismi di P/Invoke che possono confondore
Nell’uso di P/Invoke bisogna fare molta attenzione alla tipologia dei dati trattati: infatti non è
detto che i vari tipi corrispondano tra loro. Bisogna quindi prendere le necessarie
contromisure (Data Type Converting e Marshaling) per ovviare ai problemi.
Inoltre bisogna ricordarsi che le CallBack (ovvero le chiamate a funzione) devono essere
mappate attraverso opportuni Delegati, da definire in base alla signature della callback
stessa.
dotNet Framework: Reflection
Scoprire il codice
dotNet Framework: Reflection
Attraverso la Reflection dotNET mette a disposizione un elaborato strumento che permette di
effettuare ad un programma l’auto analisi della sua struttura.
Attraverso la reflection è possibile, ad esempio, conoscere i tipi contenuti nell’assembly o il
Modulo a cui appartengono o ancora i suoi metadati.
Tutto questo è possibile attraverso le funzionalità messe a disposizione dal namespace
System.Reflection e dalla classe statica Assembly
dotNet Framework: Reflection
La domanda è: a cosa serve la Reflection?
Attraverso la Reflection è possibile ottenere informazioni da un assembly a runtime e quindi,
ad esempio, invocare un metodo costruendone dinamicamente la chiamata. Tutti noi
utilizziamo il completamento automatico dei metodi (e non solo) messo a disposizione di VS:
ebbene l’IDE Microsoft riesce a fare ciò proprio grazie alla reflection.
Questo strumento ha anche un’ulteriore potenzialità: creare un assembly runtime e addirittura
salvarlo su disco.
Per quanto riguarda la Reflection non andiamo oltre, essendo un argomento avanzato e che
necessita di approfondimenti ad-hoc.
dotNet Framework: Globalization
Rendere internazionali le proprie Applicazioni
dotNet Framework: Globalization
Il contesto geografico in cui un software verrà utilizzato è spesso un fattore sottovalutato
durante la sua realizzazione.
Immaginate però quanti problemi sorgono se il nostro prodotto inizialmente pensato solo per il
mercato italiano deve essere adattato a quello francese o a quello inglese.
dotNET viene incontro ai problemi di internazionalizzazione grazie al namespace
System.Globalization.
Partiamo però dal chiarire cosa intendiamo per Cultura (culture): per cultura si intendono un
insieme di elementi (calendario, valuta, lingua, ecc.) specifici di un determinata nazione o
area geografica.
Esistono tre tipologie di culture:
• Invariant Culture: è una cultura invariante (basata sul linguaggio inglese) da utilizzare
nel caso in cui si voglia garantire una sorta di consistenza internazionale degli elementi
culturali (es: data e ora di una trial application);
• Neutral Culture: una cultura è neutrale quando è associata solo ad una lingua
(Francese, Inglese, Tedesco) ma non alla rispettiva nazione o area geografica. Essa
viene indicata con le iniziali minuscole della lingua: fr, en, sp, it;
•Specific Culture: una cultura specifica è relativa ad una nazione e viene rappresentata
dalla cultura neutrale più l’abbreviazione maiuscola di quella specifica: fr-FR (francia)
en-US (stati uniti), en-EN (inghilterra);
dotNet Framework: Globalization
L’utilizzo della cultura appropriata è fondamentale quando, su sistemi internazionali, si
desidera che i valori analizzati siano interpretati allo stesso modo.
Prendiamo ad esempio una data italiana composta da gg/mm/aa:
05/11/2007 (5 novembre 2007).
Per noi è chiaro che le prime due cifre rappresentano il giorno, ma se la facciamo leggere ad
un americano la interpreterà come:
l’11 Maggio 2007 (mm/gg/aa).
Stesso problema relativo al significato del (.) e della (,) nei numeri.
La soluzione dotNET passa attraverso 2 classi principali CultureInfo e RegionInfo ed un
enumeratore CultureTypes.
dotNet Framework: Globalization
Vediamo alcune situazioni pratiche.
Per rilevare la cultura corrente possiamo procedere nel modo seguente:
Dim UsersCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
Console.WriteLine("The currentculture of this application is : “ + UsersCulture.Name)
CultureInfo UsersCulture = Thread.CurrentThread.CurrentCulture;
Console.WriteLine("The current culture of this application is : “ + UsersCulture.Name);
allo stesso modo è semplice cambiare la cultura attuale:
' VB
tbSalary.Text = Format(100000, "Currency")
// C#
tbSalary.Text = (100000).ToString("C");
Thread.CurrentThread.CurrentCulture = new CultureInfo("es-VE");
Per formattare correttamente una stringa (ad esempio un valore monetario variando il punto
con la virgola, ecc.) dobbiamo usare appositi strumenti di formattazione:
dotNet Framework: Globalization
CurrentCulture rappresenta la cultura con cui vengono effettuate le manipolazioni durante
l’esecuzione del programma.
Dualmente esiste CurrentUICulture che rappresenta la cultura utilizzata per l’Interfaccia
Utente, il cui valore può essere letto/variato esattamente come CurrentCulture (sostituendo
ovviamente CurrentUICulture).
La cosa importante da evidenziare è che CurrentUICulture può essere settata solo nella fase
di start dell’applicazione (nel main), al contrario di CurrentCulture
Alla classe CultureInfo il framework affianca RegionInfo che fornisce informazioni relative
all’area geografica specifica:
' VB
Dim UsersCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
Dim DemoRegion As RegionInfo = New RegionInfo(UsersCulture.LCID)
Or:
Dim DemoRegion As RegionInfo = New RegionInfo(UsersCulture.Name)
// C#
CultureInfo UsersCulture = Thread.CurrentThread.CurrentCulture;
RegionInfo DemoRegion = new RegionInfo(UsersCulture.LCID);
Or:
RegionInfo DemoRegion = new RegionInfo(UsersCulture.Name);
dotNet Framework: Globalization
Il risultato della
comparazione può essere
modificato passando alla
compare,come terzo
parametro, uno dei valori
di CompareOptions
' VB
Dim FirstString = "Coté"
Dim SecondString = "coté"
Dim DemoInfo As CompareInfo = New CultureInfo("fr-FR").CompareInfo
DemoInfo.Compare(FirstString, SecondString)
// C#
String FirstString = "Coté";
String SecondString = "coté";
CompareInfo DemoInfo = new CultureInfo("fr-FR").CompareInfo;
DemoInfo.Compare(FirstString, SecondString);
Quando abbiamo introdotto la globalization abbiamo fatto riferimento, come esempio, al
problema della rappresentazione delle date.
Per risolvere il relativo problema di internazionalizzazione si può far ricorso alla classe
DateTimeFormatInfo così come nel caso delle valute si può utilizzare la classe
NumberFormatInfo
L’ultima classe che evidenzieremo è la CompareInfo che unitariamente all’enumerator
CompareOptions permette di fare comparazioni tra stringhe.
La comparazione tra stringhe può avere esiti diversi in base alla cultura utilizzata. Ad esempio
alcune culture potrebbero ignorare la differenza di maiuscole e minuscole in una
stringa,mentre altre ritenerle fondamentali:
dotNet Framework: Globalization
Nel caso in cui avessimo bisogno di creare una nostra cultura (caso molto raro ma non
assurdo), possiamo far ricorso alla classe CultureRegionAndInfoBuilder e all’enumerator
CultureAndRegionModifiers.
In generale è possibile partire da una cultura esistente e variarne alcuni elementi di base:
' VB
Dim DemoBuilder As new CultureAndRegionInfoBuilder("en-US", CultureAndRegionModifiers.Neutral)
// C#
CultureAndRegionInfoBuilder DemoBuilder = new CultureAndRegionInfoBuilder("en-US",
CultureAndRegionModifiers.Neutral);
Dot net framework 2

More Related Content

Similar to Dot net framework 2

Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Marco Loregian
 
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08Alessandro Del Sole
 
JAMP DAY 2010 - ROMA (1)
JAMP DAY 2010 - ROMA (1)JAMP DAY 2010 - ROMA (1)
JAMP DAY 2010 - ROMA (1)jampslide
 
e-SUAP - General software architecture (Italiano)
e-SUAP - General software architecture (Italiano)e-SUAP - General software architecture (Italiano)
e-SUAP - General software architecture (Italiano)Sabino Labarile
 
Le Novita’ dello sviluppo applicazioni per IBM i
Le Novita’ dello sviluppo applicazioni per IBM iLe Novita’ dello sviluppo applicazioni per IBM i
Le Novita’ dello sviluppo applicazioni per IBM iS.info Srl
 
Evento 18 giugno - Sviluppo applicativo
Evento 18 giugno - Sviluppo applicativoEvento 18 giugno - Sviluppo applicativo
Evento 18 giugno - Sviluppo applicativoPRAGMA PROGETTI
 
Writing apps for android with .net
Writing apps for android with .net Writing apps for android with .net
Writing apps for android with .net Leonardo Alario
 
Tesi Discussione
Tesi DiscussioneTesi Discussione
Tesi DiscussioneYeser Rema
 
Sviluppo apps multipiattaforma con visual studio e xamarin
Sviluppo apps multipiattaforma con visual studio e xamarinSviluppo apps multipiattaforma con visual studio e xamarin
Sviluppo apps multipiattaforma con visual studio e xamarinFabio Cozzolino
 

Similar to Dot net framework 2 (20)

Introduzione a .Net Core
Introduzione a .Net CoreIntroduzione a .Net Core
Introduzione a .Net Core
 
Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3
 
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08
WPF & LINQ: VB T&T Community After Hour @ Microsoft Days 08
 
Open xml
Open xmlOpen xml
Open xml
 
JAMP DAY 2010 - ROMA (1)
JAMP DAY 2010 - ROMA (1)JAMP DAY 2010 - ROMA (1)
JAMP DAY 2010 - ROMA (1)
 
Reportage Delphi Day 2012
Reportage Delphi Day 2012Reportage Delphi Day 2012
Reportage Delphi Day 2012
 
e-SUAP - General software architecture (Italiano)
e-SUAP - General software architecture (Italiano)e-SUAP - General software architecture (Italiano)
e-SUAP - General software architecture (Italiano)
 
Le Novita’ dello sviluppo applicazioni per IBM i
Le Novita’ dello sviluppo applicazioni per IBM iLe Novita’ dello sviluppo applicazioni per IBM i
Le Novita’ dello sviluppo applicazioni per IBM i
 
Evento 18 giugno - Sviluppo applicativo
Evento 18 giugno - Sviluppo applicativoEvento 18 giugno - Sviluppo applicativo
Evento 18 giugno - Sviluppo applicativo
 
Livecode
LivecodeLivecode
Livecode
 
Da DotNet a DotNetCore
Da DotNet a DotNetCoreDa DotNet a DotNetCore
Da DotNet a DotNetCore
 
Writing apps for android with .net
Writing apps for android with .net Writing apps for android with .net
Writing apps for android with .net
 
Sistemi distribuiti
Sistemi distribuitiSistemi distribuiti
Sistemi distribuiti
 
Scenario Framework
Scenario FrameworkScenario Framework
Scenario Framework
 
Tesi Discussione
Tesi DiscussioneTesi Discussione
Tesi Discussione
 
GNU Linux Programming introduction
GNU Linux Programming introductionGNU Linux Programming introduction
GNU Linux Programming introduction
 
Sviluppo apps multipiattaforma con visual studio e xamarin
Sviluppo apps multipiattaforma con visual studio e xamarinSviluppo apps multipiattaforma con visual studio e xamarin
Sviluppo apps multipiattaforma con visual studio e xamarin
 
Wcf data services
Wcf data servicesWcf data services
Wcf data services
 
Cac Es3 2009
Cac Es3 2009Cac Es3 2009
Cac Es3 2009
 
World wide web
World wide webWorld wide web
World wide web
 

More from Felice Pescatore

Il Cinismo dell'Agilista Imbruttito
Il Cinismo dell'Agilista ImbruttitoIl Cinismo dell'Agilista Imbruttito
Il Cinismo dell'Agilista ImbruttitoFelice Pescatore
 
Intelligent Business Agility
Intelligent Business AgilityIntelligent Business Agility
Intelligent Business AgilityFelice Pescatore
 
Disciplined Agile, la Promessa, la Svolta e il Prestigio
Disciplined Agile, la Promessa, la Svolta e il PrestigioDisciplined Agile, la Promessa, la Svolta e il Prestigio
Disciplined Agile, la Promessa, la Svolta e il PrestigioFelice Pescatore
 
The Disciplined Approach to Change
The Disciplined Approach to ChangeThe Disciplined Approach to Change
The Disciplined Approach to ChangeFelice Pescatore
 
The Disciplined Approach to Change
The Disciplined Approach to ChangeThe Disciplined Approach to Change
The Disciplined Approach to ChangeFelice Pescatore
 
PMI Disciplined Agile: la Promessa, la Svolta e il Prestigio
PMI Disciplined Agile: la Promessa, la Svolta e il PrestigioPMI Disciplined Agile: la Promessa, la Svolta e il Prestigio
PMI Disciplined Agile: la Promessa, la Svolta e il PrestigioFelice Pescatore
 
Un cuore moderno per l'Agilità
Un cuore moderno per l'AgilitàUn cuore moderno per l'Agilità
Un cuore moderno per l'AgilitàFelice Pescatore
 
L'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingL'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingFelice Pescatore
 
Industry 4.0... a che punto siamo in Italia
Industry 4.0... a che punto siamo in ItaliaIndustry 4.0... a che punto siamo in Italia
Industry 4.0... a che punto siamo in ItaliaFelice Pescatore
 
Agile IoT & Eclipse Duttile
Agile IoT & Eclipse DuttileAgile IoT & Eclipse Duttile
Agile IoT & Eclipse DuttileFelice Pescatore
 
Fuffa Day - The Sixth Sense
Fuffa Day - The Sixth SenseFuffa Day - The Sixth Sense
Fuffa Day - The Sixth SenseFelice Pescatore
 
Value Focused Team: road to DevOps
Value Focused Team: road to DevOpsValue Focused Team: road to DevOps
Value Focused Team: road to DevOpsFelice Pescatore
 

More from Felice Pescatore (20)

Il Cinismo dell'Agilista Imbruttito
Il Cinismo dell'Agilista ImbruttitoIl Cinismo dell'Agilista Imbruttito
Il Cinismo dell'Agilista Imbruttito
 
Intelligent Business Agility
Intelligent Business AgilityIntelligent Business Agility
Intelligent Business Agility
 
AgileBIM, BIM mets Agile
AgileBIM, BIM mets AgileAgileBIM, BIM mets Agile
AgileBIM, BIM mets Agile
 
Disciplined Agile, la Promessa, la Svolta e il Prestigio
Disciplined Agile, la Promessa, la Svolta e il PrestigioDisciplined Agile, la Promessa, la Svolta e il Prestigio
Disciplined Agile, la Promessa, la Svolta e il Prestigio
 
AgileBIM overview
AgileBIM overviewAgileBIM overview
AgileBIM overview
 
The Disciplined Approach to Change
The Disciplined Approach to ChangeThe Disciplined Approach to Change
The Disciplined Approach to Change
 
The Disciplined Approach to Change
The Disciplined Approach to ChangeThe Disciplined Approach to Change
The Disciplined Approach to Change
 
PMI Disciplined Agile: la Promessa, la Svolta e il Prestigio
PMI Disciplined Agile: la Promessa, la Svolta e il PrestigioPMI Disciplined Agile: la Promessa, la Svolta e il Prestigio
PMI Disciplined Agile: la Promessa, la Svolta e il Prestigio
 
Agilozzi le testa tutte
Agilozzi le testa tutteAgilozzi le testa tutte
Agilozzi le testa tutte
 
39bit al secondo
39bit al secondo39bit al secondo
39bit al secondo
 
Un cuore moderno per l'Agilità
Un cuore moderno per l'AgilitàUn cuore moderno per l'Agilità
Un cuore moderno per l'Agilità
 
L'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingL'Occhio di Ra sul Testing
L'Occhio di Ra sul Testing
 
#NoElevator4DevOps
#NoElevator4DevOps#NoElevator4DevOps
#NoElevator4DevOps
 
Don't Dirty my Backlog!
Don't Dirty my Backlog!Don't Dirty my Backlog!
Don't Dirty my Backlog!
 
Industry 4.0... a che punto siamo in Italia
Industry 4.0... a che punto siamo in ItaliaIndustry 4.0... a che punto siamo in Italia
Industry 4.0... a che punto siamo in Italia
 
Agile IoT & Eclipse Duttile
Agile IoT & Eclipse DuttileAgile IoT & Eclipse Duttile
Agile IoT & Eclipse Duttile
 
Fuffa Day - The Sixth Sense
Fuffa Day - The Sixth SenseFuffa Day - The Sixth Sense
Fuffa Day - The Sixth Sense
 
#NoElevator for DevOps
#NoElevator for DevOps#NoElevator for DevOps
#NoElevator for DevOps
 
Value Focused Team
Value Focused TeamValue Focused Team
Value Focused Team
 
Value Focused Team: road to DevOps
Value Focused Team: road to DevOpsValue Focused Team: road to DevOps
Value Focused Team: road to DevOps
 

Dot net framework 2

  • 1. Ing. Felice Pescatore felice.pescatore@poste.it Microsoft .Net Framework DotNet FRAMEWORK Architettura e Dettagli
  • 2. Quale versione del Framework.NET? Questo corso sarà incentrato sulla versione 2 del dotNet Framework,nonostante sia stata rilasciata in versione definitiva la 3 e sia quasi pronta la 3.5. Ciò perché la versione 3 non modifica, sostanzialmente, l’architettura della versione 2, ma aggiunge uno strato superiore con funzionalità specifiche. Inoltre la maggior parte del software attualmente sviluppato, la maggior parte del materiale didattico, così come le Certificazioni Microsoft (MCTS e MCPD) si basano proprio sulla versione 2. Anche se non esplicitamente evidenziato tutto quanto presentato di seguito sarà comunque riferito alla versione 2. A conclusione del corso verrà dedicato uno spazio proprio per presentare le novità del dotNet 3.5, la cui uscita è prevista per gli inizi del 2008.
  • 3. Certificazioni Microsoft: i percorsi Application Development Foundation Esame 70-536 Windows Based Client Development Esame 70-526 Design e Developing Windows Based Application Esame 70-548 Web Based Client Development Esame 70-528 Design e Developing Web Based Application Esame 70-547 Distribuited Applicatin Development Esame 70-529 Design e Developing Enterprise Application Esame 70-549
  • 5. Certificazioni Microsoft: SQL Server Esame 70-431
  • 6. Certificazioni Microsoft: Compendi Consigliati Serie Passo-Passo, di base Serie Programming • msdn2.microsoft.com/it-it/library/default.aspx • www.ugidotnet.org • www.aspitalia.com • www.dotnet2themax.it Autori: •Francesco Balena •Dino Esposito •Marco Bellinaso •David Sheppa
  • 7. Architettura del dotNet Framework CONCETTI INTRODUTTIVI
  • 8. Cos’ è il dotNet Framework Il Framework .NET e' la piattaforma di sviluppo di riferimento per il mondo Windows, grazie alla quale e' possibile realizzare soluzioni software a 360° in grado di rispondere alle esigenze più complesse. I Punti di forza sono da ricercarsi nella sconfinata libreria di base, in un architettura a strati che permette una evoluzione natura (.Net 3/3.5), nell’indipendenza dal linguaggio di programmazione e nella disponibilità del più evoluto ambiente di sviluppo attualmente esistente (Visual Studio)
  • 9. dotNet presenta un’architettura a strati. Al suo interno possiamo distinguere gli elementi portanti dell’intero framework: Il dotNET Framework  CLR - Common Language Runtime  BCL - Base Class Library  ADO.NET & XML  Windows Forms  ASP.Net, Web Forms & Web Services  CLS – Common Language Specification  CTS – Common Type System
  • 10. Il dotNET Framework: il CLR (1) Il COMMON LANGUAGE RUNTIME (CLR) è uno strato posto al di sopra dei servizi del sistema operativo. Esso è responsabile dell'esecuzione vera e propria delle applicazioni: assicura che vengano rispettate tutte le dipendenze, gestisce la memoria, la sicurezza, l'integrazione del linguaggio e così via. Il runtime fornisce numerosi servizi che consentono di semplificare la stesura del codice, la distribuzione dell'applicazione e di migliorare l'affidabilità della stessa.
  • 11. Il .NET Framework: il CLR (2) •Semplificare lo sviluppo • Definire standard per il riuso del codice • Fornire servizi come gestione della memoria e garbage collection • Semplificare deployment delle applicazioni •I componenti usano i metadati anzichè la registrazione •Supportare più versioni • Deployement da riga di comando XCOPY e disinstallazione DEL • Eliminare l’uso del registro per i componenti e la necessità di codice extra per la loro gestione. • Supportare i linguaggi di sviluppo •Fornendo classi di base per gli strumenti e i linguaggi degli sviluppatori • Supportare più linguaggi di programmazione •Definire i CTS utilizzati da tutti i linguaggi .NET Con il CLR, Microsoft ha posto alcuni obiettivi cardine della propria tecnologia:
  • 12. Il dotNET Framework: il CLR (3) Volendo fare un paragone con il mondo JAVA, il CLR è l’equivalente .NET della Java Virtual Machine (JVM): attiva gli oggetti, li sottopone a controlli di sicurezza, li dispone in memoria. Gli eseguibili .NET sono composti da Codice e Metadati. Questi ultimi contengono la definizione di tipo, informazione sulla versione e riferimenti ad Assembly esterni. Il Class Loader utilizza i metadati per il caricamento delle classi .NET, mentre i compilatori Just in Time (JIT) li utilizzano per compilare l’ Intermediate Language (IL)
  • 13. Tutti i linguaggi .NET, attraverso il CLR, accedono al medesimo sistema di tipi e alle stesse classi base, ottenendo cosi una convergenza tra Linguaggi e Modelli di Programmazione Il codice eseguito dal CLR viene detto CODICE GESTITO (MANAGED CODE) ed è memorizzato come Codice+Metadati in un formato standard Windows PE (portable executable) che consente di: • Leggere i metadati • Fornire lo stack del codice • Gestire le eccezioni • Recuperare informazioni di sicurezza • Esistono “versioni del CLR” anche sotto Linux e Mac Os X (vedi MONO) che consentono di eseguire il codice in formato PE anche in macchine non Windows rendendo .NET un framework multipiattaforma Il dotNET Framework: il CLR (4)
  • 14. Il dotNET Framework: la BCL Difatti, prima del dotNet Framework, gli Ingegneri del Software dovevano pianificare la realizzazione di una applicazione scegliendo preventivamente il linguaggio e di conseguenza si limitavano le librerie in base a quelle supportate dallo stesso. La BCL permette invece di scegliere, teoricamente anche in fase di sviluppo, il linguaggio più opportuno per il task da eseguire, permettendone una sostanziale interoperabilità, al patto di rispettare le specifiche minime necessarie. La Base Class Library (BCL) è l’insieme delle librerie funzionali comuni ad ogni linguaggio basato sulla piattaforma dotNet, e comprende: Tipi dato complessi (collezioni), Networking, Accesso al file system, Interfaccia utente, Sicurezza,Programmazione concorrente, XML e molto altro
  • 15. Il dotNET Framework: ADO.NET Praticamente tutte le applicazioni hanno la necessità di interrogare o aggiornare dati persistenti memorizzati in file piatti, database relazionali o altri tipi di supporto di memorizzazione. Per ovviare a tale necessità, il .NET Framework include ADO.NET, un sottosistema per l'accesso ai dati ottimizzato per ambienti N-tier ed interoperabile con l'XML ed i documenti XML. ADO.NET è stato progettato per gli ambienti debolmente accoppiati e per fornire servizi per l'accesso ai dati ad applicazioni scalabili e servizi basati sul Web. ADO.NET mette a disposizione API ad elevate Prestazioni per modelli di dati sia connessi sia Disconnessi particolarmente adatti alla restituzione di dati alle applicazioni Web.
  • 16. Il dotNET Framework: XML XML svolge un ruolo fondamentale in nel .NET framework, sin dalla sua prima versione, essendo utilizzato per descrivere la applicazioni (metadati) in modo analogo ai deployment descriptor di Java. Inoltre è spesso la base di comunicazione per lo scambio dei dati tra due applicazioni (marshaling e serializzazione)
  • 17. ASP.NET fornisce un modello applicativo sotto forma di CONTROLLI che semplifica la creazione di applicazioni Web. L’infrastruttura include un insieme di Controlli Lato Server che ricalcano gli oggetti Widget delle tipiche interfacce utente HTML (tra cui, caselle di riepilogo, caselle di testo e pulsanti), ed un insieme aggiuntivo di Controlli Web Evoluti (come calendari e rotator). Essi sono posizionati attraverso drag&drop sulle pagine, indicate in gergo Web Form. I controlli vengono eseguiti in realtà sul server Web ed inviano la propria interfaccia utente ad un browser sotto forma di HTML. Sul server, i controlli espongono un modello di programmazione orientato all'oggetto che mette a disposizione degli sviluppatori Web tutta la ricchezza di questo tipo di programmazione. Il dotNET Framework: ASP.NET
  • 18. Il dotNET Framework: WindowsForms Le WindowsForms sono praticamente le classiche finestre di un’applicazione windows che contengono i classici elementi come bottoni,combobox, aree di testo ecc. Tutti i controlli posizionati su una WindowsForm (e la WindowsForm stessa, che è ugualmente un controllo) risponde ad una serie di eventi che lo sviluppatore può gestire attraverso l’implementazione di appositi Handler.
  • 19. Il dotNET Framework: ASP.NET - WebForms Le WebForms sono l’equivalente delle WindowsForms, eccetto che il loro ambiente di esecuzione è il Browser Web. Esse vengono elaborate sul server e successivamente passate al browser nel classico formato HTML La fase di implementazione di una WebForm non si discosta molta da quella di una classica WindowsForm.
  • 20. Il dotNET Framework: ASP.NET – XML Web Services I Web Services sono una sorta di piccoli servizi disponibili in rete, che forniscono funzionalità specializzate. Essi vengono interrogati attraverso il protocollo SOAP basato su XML.
  • 21. Il dotNET Framework: CTS Il COMMON TYPE SYSTEM (CTS) definisce un insieme standard di tipi di dato e di regole necessarie per la realizzazione di nuovi tipi, consentendo ai vari linguaggi di interoperare correttamente tra loro CTS fornisce due tipi principali entrambi ereditati da System.Object: •Tipi a Valore, utili per rappresentare tipi di dati semplici e quelli definiti dall’utente (ovvero le strutture); •Tipi a Riferimento, ovvero: tipi di oggetti, tipi di interfacce e tipi di puntatori Da notare che in .NET tutti i tipi sono orientati agli oggetti (ogni elemento è un oggetto) e i tipi a valore possono essere convertiti in tipi a riferimento mediante il boxing CTS definisce, inoltre, un modello uniforme per la gestione delle eccezioni, indipendentemente dal linguaggio utilizzato (ovviamente con sintassi diverse). Si tratta in praticamente di definire i seguenti passi:  Lanciare un’eccezione  Catturare un’eccezione  Codice di uscita da un blocco controllato (finally)
  • 22. Il dotNET Framework: CTS, Tipi a Valore I Tipi a Valore non possono essere null (tranne i NULLABLE che sono un discorso a parte) e devono sempre contenere dei dati. Possono essere: : primitivi, strutture e enumerazioni; Per creare un tipo a valore personalizzato anche derivandolo da una classe System.ValueType, proprio come fa dotNet per i tipi primitivi. Prendendo ad esempio il tipo int, si vede che esso non è nient’altro che un alias di System.Int32 derivato appunto da Sysem.ValueType. E’ utile sottolineare che il passaggio di un Tipo a Valore in una funzione avviene per copia
  • 23. Il dotNET Framework: CTS, Tipi a Riferimento I Tipi a Riferimento rappresentano il riferimento ad oggetti allocati nell’Heap (a differenza dei tipi a valore allocati sullo Stack), ed ammettono il valore Null. Il passaggio a funzioni avviene per riferimento, cioè viene passato un indirizzo o un puntatore ad un oggetto. Questi tipi vengono gestiti dal CLR e sottoposti a GARBAGE COLLECTION. CTS promuove la sicurezza di tipi in modo da migliorare la stabilità del codice, attraverso definizioni dei tipi completamente note e che non possono essere alterate. Ciò si traduce nel fatto che i riferimenti agli oggetti siano strongly-typed,ovvero durante la fase di assegnazione di un oggetto ad un reference viene verificato se sono di tipo compatibile.
  • 24. Il dotNET Framework: CLS Le COMMON LANGUAGE SPECIFICATION, definiscono le regole che un linguaggio deve avere per essere gestito dal framework, permettendo l’interoperabilità tra i vari linguaggi supportati dal dotNet Framework. In pratica descrivo come il compilatore deve trasformare il codice sorgente nel codice intermedio (Intermediate Language – IL), per essere compatibile con le specifiche del framework. Il CLS, inoltre, definisce le API dei componenti .NET
  • 25. Esecuzione di applicazioni dotNET Compilazione ed Esecuzione di codice dotNET
  • 26. Source code C++, C#, Visual Basic o Altria language .NET es: Csc.exe o Vbc.exe Compilatore Assembly DLL o EXE L’applicativo, scritto in uno dei linguaggi supportati, viene compilato, generando così un ASSEMBLY, che nonostante l’estensione,è un packaging composto da più elementi. Compilazione e Generazione dell’Assembly
  • 27. Un ASSEMBLY rappresenta il costituente fondamentale di un'applicazione .NET. Si tratta dell’unità elementare di rilascio del codice e di gestione delle versioni, ed è composto da: •Un manifesto (Manifest); •Un insieme di uno o più moduli (dll o exe); •Un insieme opzionale di risorse Tutti i tipi e le risorse gestiti sono contrassegnati in uno dei seguenti modi: • accessibili solo all'interno della propria unità implementativa • esportabili per essere utilizzati dal codice al di fuori di tale unità. Assembly dotNet
  • 28. Type Descriptions Classes Base classes Implemented interfaces Data members Methods Name Version Culture Assembly Manifest Other assemblies Security permissions Exported types Assembly Manifest: Descrizione • Stabilisce l'identità dell'assembly in termini di nome, versione, livello di condivisione tra applicazioni diverse, firma digitale. •Definisce quali file (nome e file hash) costituiscono l'implementazione dell'assembly. •Specifica le dipendenze in fase di compilazione da altri assembly. •Specifica i tipi e le risorse che costituiscono l'assembly, inclusi quelli che vengono esportati dall'assembly. •Specifica l'insieme dei permessi necessari al corretto funzionamento dell'assembly. Gli assembly si autodescrivono tramite il proprio MANIFEST (manifesto), che costituisce una parte integrante di ogni assembly stesso:
  • 29. Assembly Manifest: Uso dei Metadati I METADATI sono indipendenti dal linguaggio, consentendo a quelli che vi accedono di interpretarli in modo univoco. Questo permette di creare progetti (assistiti da IDE come Visual Studio) che contengono al loro interno codice sorgente di linguaggi diversi, proprio grazie alle informazioni contenute nei metadati dei componenti; Il compilatore genera i metadati di un componente dal codice sorgente che vengono memorizzati nel codice compilato in un formato PE (Portable Executable); Un tools per la visualizzazione dei metadati è ildasm.exe
  • 30. Assembly: Execution Model, Intermediate Language Come detto il Codice Sorgente viene compilato nel linguaggio intermedio IL, spesso indicato anche come MSIL (Microsoft IL) e CIL (Common IL). Vediamo un esempio di IL: .method private hidebysig static void Main() cil managed { .entrypoint // Code size 11 (0xb) .maxstack 8 IL_0000: ldstr "Hello, world!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ret } // end of method HelloWorld::Main
  • 31. Assembly: Execution Model Gli assembly sono caricati in memoria dal CLR solo all’occorrenza: • prima viene determinata la versione • poi viene cercato l’assembly nella Global Assembly Cache (GAC) oppure nel percorso locale indicato dal codice base; Prima dell’esecuzione il CLR deve procedere alla compilazione dell’ IL (non eseguibile direttamente dal processore) per la generazione di codice nativo. Ci sono due possibilità: • Compilazione del Metodo a Tempo di Esecuzione, Just In Time (JIT); • Compilazione di tutto l’Assembly prima dell’esecuzione Come già accennato in precedenza, il CLR permette di limitare le funzionalità del codice eseguito.
  • 32. Assembly: Execution Model Schema Compilazione Esecuzione JIT Compiler Native Code MSIL Code Metadata Source Code Language Compiler Assembly (.exe o .dll) Compilazione Just in Time
  • 33. Assembly: Deployment semplificato Sicuramente il Deployment rappresenta uno dei grossi vantaggi di dotNet, permettendo: • Un’installazione senza effetti collaterali (dll Hell), le applicazioni e i componenti possono sempre essere condivisi o privati; • L’esecuzione Side-by-Side, diverse versioni dello stesso componente possono coesistere, anche nello stesso processo Il Deployment può avvenire in modi diversi: • XCOPY, per le applicazioni ASP.NET • .CAB, per le applicazioni • Windows Forms - Code Download • .MSI (Windows Installer), per le Applicazioni Windows Forms l’ installazione in GAC di assembly condivisi, la Configurazione di shortcut
  • 34. Assembly Location Avendo parlato di deployment è utile specificare le location che gli assembly possono assumere in base alla loro tipologia:  Assembly privati, directory applicazione (e sub-directory)  Assembly condivisi, Global Assembly Cache (GAC) (c:windowsassembly)  Assembly scaricati da URL, download cache (c:Documents and Settings%USERNAME% Local SettingsApplication Dataassemblydl2) Il Tool per esaminare GAC e download cache è GACUTIL.EXE
  • 35. Oggetti “vivi” Oggetti non raggiungibili Spazio libero Garbage Collection(1) Fase 1: Mark Durante l’esecuzione del codice gli oggetti non più utilizzati come vengono deallocati dalla memoria? dotNet distrugge automaticamente gli oggetti quando non sono più referenziati, utilizzando un sofisticato algoritmo di Mark-and-Compact NextObjPtr Root set
  • 36. www.devleap.it Oggetti “vivi” Spazio libero NextObjPtr Root set Spazio recuperato Garbage Collection (2) Fase 2: Compact
  • 37. Non sempre ci si può affidare in modo “cieco” al Garbage Collector, ma in alcuni casi serve un comportamento di finalizzazione deterministica. In particolare quando si hanno:  Riferimenti a oggetti non gestiti  Utilizzo di risorse che devono essere rilasciate appena termina il loro utilizzo Non si possono usare i finalizzatori (come si fa in C++), che sono richiamabili direttamente, ma bisogna utilizzare l’interfaccia IDisposable, implementando il metodo Dispose. Garbage Collection(3)
  • 38. Linguaggi del dotNet Framework Come più volte ribadito fin ora , il dotNet Framework è LANGUAGE- INDEPENDED, ovvero non dipende dal linguaggio scelto. Microsoft fornisce direttamente il supporto (ed il compilatore) per: •C++, C#, J#, VB 2005, Jscript Esistono, comunque, implementazione di terze parti che spesso permettono di portare sulla piattaforma dotNet linguaggi molto utilizzati: •Perl, Ruby, Python, Pascal, APL, COBOL, Eiffel, Haskell, ML, Oberon, Scheme, Smalltalk… Chiunque, rispettando le CLS può realizzare (in teoria) il proprio linguaggio dotNet Ready
  • 39. Base Class ed Interfacce Le Classi e le Interfacce della BCL
  • 40. Base Class ed Interfacce: Namespaces Base Class Library Data Xml Web Services User Interface
  • 41. Base Class ed Interfacce: Namespaces System System.Data System.Xml System.Web Globalization Diagnostics Configuration Collections Resources Reflection IO Threading Text Security SqlClient OleDb SQLTypes Common Runtime InteropServices Remoting Serialization Configuration SessionState Caching Security UI HtmlControls WebControls System.Drawing Imaging Drawing2D Text Printing System.Windows.Forms Design ComponentModel XPath Xsl Serialization Schema Hosting Handlers Compilation
  • 42. Base Class ed Interfacce: Namespaces System Threading Text Security Resources Reflection IO Globalization Diagnostics Configuration Collections Runtime Serialization Remoting InteropServices
  • 43. Base Class ed Interfacce: Namespaces System.Data System.Xml OracleClient OleDb Sql/SqlClient Common Serialization Schema XPath Xsl Odbc Messaging
  • 46. Input / Output Input e Output con dotNet
  • 47. dotNet Framework: Input / Output Una delle principali attività che ci si trova a realizzare è l’interazione con il (i) file system. dotNet mette a disposizione un insieme di classi raccolte nel NameSpace System.IO, che permettono di effettuare operazioni di vario genere su file, directory,ecc:  FILEINFO and DIRECTORYINFO (base class: FileSystemInfo), permettono di interrogare ogni elemento del file system per ottenerne informazioni di vario genere  DRIVEINFO, permette di ottenere informazione sulle periferiche di I/O  FILE, DIRECTORY, PATH sono le “utility class” che permettono, attraverso metodi statici, di effettuare varie operazioni sui corrispettivi elementi
  • 48. dotNet Framework: I/O, ottenere informazioni dal File System FileInfo ourFile = new FileInfo(@"c:boot.ini "); if (ourFile.Exists) { Console.WriteLine("Filename : {0}", ourFile.Name); Console.WriteLine("Path : {0}", ourFile.FullName); } Ottenere informazione su un file FileInfo ourFile = new FileInfo(@"c:boot.ini"); ourFile.CopyTo(@"c:boot.bak"); Copiare un file Enumerare I file in una directory DirectoryInfo ourDir = new DirectoryInfo(@"c:windows"); Console.WriteLine("Directory: {0}", ourDir.FullName); foreach (FileInfo file in ourDir.GetFiles()) { Console.WriteLine("File: {0}", file.Name); }
  • 49. Per effettuare le operazioni di lettura e scrittura, il.Net Framework fornisce gli STREAM (letteralmente flussi), che permettono un accesso sequenziale e random ai dati. La classe base (di tipo astratta) è Stream,che fornisce una serie di funzionalità comuni a tutti gli stream specializzati. Tra gli Stream Personalizzati: •FileStream, specializzato nelle operazioni di lettura/scrittura dei file •Memory Stream, crea un flusso in memoria (utili per operazioni temporanee) •Buffered Stream, crea un wrapper per lo stream specifico allo scopo di migliorarne le performance; •StreamReader (base class: TextReader), consente operazioni di lettura su stream generici •StreamWriter (base class: TextWriter), consente operazioni di scrittura su stream generici dotNet Framework: Gli Stream
  • 50. dotNet Framework: aprire uno Stream su file L’apertura degli Stream, relativi alla lettura/scrittura file, avviene attraverso una serie di classi statiche ed enumeratori presenti nel Namespace System.IO: •File, fornisce le funzionalità di base per leggere e scrivere da file; •Directory, per effettuare operazioni sulle directory •FileAccess Enumeration, che specifica i diritti (Read, Write, ReadWrite) da applicare all’apertura di un file; •FileMode Enumeration specifica i diritti sui file che si andranno ad aprire. File.Open(@"C:boot.ini", FileMode.Open, FileAccess.Read); File.Create(@"c:somefile.txt"); Aprire un file per la lettura Creare un file
  • 51. dotNet Framework: Leggere/Scrivere da un file FileStream theFile = File.Open(@"C:boot.ini", FileMode.Open, FileAccess.Read); StreamReader rdr = new StreamReader(theFile); Console.Write(rdr.ReadToEnd()); rdr.Close(); theFile.Close(); StreamReader rdr = File.OpenText(@"C:boot.ini"); Console.Write(rdr.ReadToEnd()); rdr.Close(); FileStream theFile = File.Create(@"c:somefile.txt"); StreamWriter writer = new StreamWriter(theFile); writer.WriteLine("Hello"); writer.Close(); theFile.Close(); File.WriteAllText(@"c:somefile.txt", "Hello"); Leggere da file Scrivere su file
  • 52. dotNet Framework: Usare il MemoryStream MemoryStream memStrm = new MemoryStream(); StreamWriter writer = new StreamWriter(memStrm); writer.WriteLine("Hello"); writer.WriteLine("Goodbye"); writer.Flush(); FileStream theFile = File.Create(@"c:inmemory.txt"); memStrm.WriteTo(theFile); writer.Close(); theFile.Close(); memStrm.Close(); Scrittura su uno Stream in Memoria e successivo riversamento su file
  • 53. dotNet Framework: Stream Speciali Oltre agli stream che permetto una normale lettura/scrittura da file o memoria, esistono una speciale categoria di stream definiti Compression Stream Attualmente sono disponibili due Compression Stream: •GZipStream, che permette la compressione compatibile con lo standard de- facto zip; •DeflateStream, che crea una compressione proprietaria. In realtà entrambi gli stream usano lo stesso algoritmo per la compressione. La differenza sta nel fatto che la compatibilità con lo standard zip richiede un header apposito e quindi un ulteriore (anche se lieve) aggravio di risorse. Una sostanziale differenza con gli stream classici, come vedremo dagli esempi, è che i compression stream scrivono i dati su stream di appoggio (file, memoria). Infine, è da precisare, che entrambi i compression stream possono gestire al massimo 4Gb di dati. L’esempio che segue mostra la compressione/decompressione con la classe GZipStream. Per utilizzare il DelfateStream basta sostituire l’istruzione di creazione dell‘oggetto
  • 54. FileStream sourceFile = File.OpenRead(inFilename); FileStream destFile = File.Create(outFilename); GZipStream compStream = new GZipStream(destFile, CompressionMode.Compress); int theByte = sourceFile.ReadByte(); while (theByte != -1) { compStream.WriteByte((byte)theByte); theByte = sourceFile.ReadByte(); } FileStream sourceFile = File.OpenRead(inFilename); FileStream destFile = File.Create(outFilename); GZipStream compStream = new GZipStream(sourceFile, CompressionMode.Decompress); int theByte = compStream.ReadByte(); while (theByte != -1) { destFile.WriteByte((byte)theByte); theByte = compStream.ReadByte(); } dotNet Framework: Comprimere/Decomprimere con gli stream Compressione Decompressione
  • 55. dotNet Framework: Working with Text Lavorare con le Stringhe ed il Testo
  • 56. dotNet Framework: Working with Text  Lavorare con le stringhe di testo è un task comune per tutti gli sviluppatori  .Net mette a disposizione un insieme di classi raccolte nel NameSpace System.Text, che permettono di effettuare operazioni di vario genere sulle stringhe:  StringBuilder, permette una manipolazione efficiente delle stringhe;  Regex, Match, Group, permettono l’elaborazione delle stringhe attraverso le Espressioni Regolari;  Encode/Decode, sono specializzate nella codifica/decofica delle stringhe nei vari formati internazionali.
  • 57. dotNet Framework: La classe StringBuilder e la classe String • Normalmente si è portati ad utilizzare la classe String per le operazioni sulle stringhe. • In realtà un oggetto di tipo String è un oggetto atipico, perché ogni operazione su di esso (tipo l’aggiunta di nuovi caratteri alla stringa), non modifica l’oggetto esistente, bensì ne crea uno nuovo aggiornando il reference. • Per migliorare le performance (ed evitare inutili sprechi di memoria) il .Net framework prevede la classe StringBuilder, che crea un oggetto stringa dinamico System.Text.StringBuilder sb = new System.Text.StringBuilder(30); sb.Append("wombat"); sb.Append(" kangaroo"); sb.Append(" wallaby"); sb.Append(" koala"); string s = sb.ToString(); Console.WriteLine(s);
  • 58. dotNet Framework: Regular Expression • Le Espressioni Regolari (Regular Expression) sono una eredità del mondo Unix e del linguaggio PERL. • Rappresentano un modo efficiente per effettuare elaborazioni complesse sulle stringhe di testo. Come esempio, la seguente Regular Expression, permette di validare un indirizzo email: • Nella pratica l’utilizzo di Espressioni Regolari può risultare anche molto complesso e non approfondiremo uteriormente l’argomento. •Comunque il modo più semplice per verificare se una stringa supera la verifica rispetto ad una Espressione Regolare (MATCH) è quello di usare la classe Regex e il metodo statico IsMatch: ^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$ Regex.IsMatch("pattern", @"ApatternZ")
  • 59. dotNet Framework: Codifica del Testo • Vista l’eterogeneità dei sistemi informatici esistenti, non meraviglia che esistano diversi modi di codificare (o meglio, rappresentare) le stringhe. • La codifica più utilizzata nello scorso decennio è stata quella ASCII (7bit), ormai insufficiente per codificare adeguatamente anche caratteri speciali (come le lettere degli alfabeti orientali). •Oggi la codifica più utilizzata è la Unicode UTF-16 (16 bit), che è anche la codifica base utilizzata dal framework .Net. •Nel caso si renda necessario lavorare con codifiche differenti, è possibile utilizzare le classi Encode/Decode come mostrato dall’esempio seguente: // Get Korean encoding Encoding e = Encoding.GetEncoding("Korean"); // Convert ASCII bytes to Korean encoding byte[] encoded; encoded = e.GetBytes("Hello, world!"); // Display the byte codes for (int i = 0; i < encoded.Length; i++) Console.WriteLine("Byte {0}: {1}", i, encoded[i]);
  • 60. dotNet Framework: Collection & Generics Lavorare su insiemi di oggetti
  • 61. dotNet Framework: Collection & Generics Ogni framework che si rispetti deve avere un’ampia serie di collezioni (collection) che permettono di gestire un insieme di oggetti, più o meno omogeneei, attraverso i classici costrutti dei moderni linguaggi. dotNet contempla al suo interno l’implementazione delle COLLECTION CLASSICHE e dei GENERICS La differenza sostanziale tra le due è che le Collection Classiche memorizzano al loro interno gli oggetti attraverso il casting, mentre nel caso dei Generics la categoria di oggetti contenuti nella collection è tipizzata. Le Generics portano in dote vantaggi di performance (molto limitati in realtà), ma soprattutto una programmazione type-safe, in cui è il compilatore a verificare il corretto utilizzo della collezzione.
  • 62. dotNet Framework: Collection Classiche Le Collection Classiche sono contenute nel NameSpace System.Collections e annoverano: •ArrayList, una semplice collezione ridimensionabile ed indicizzata; •SortedList, una collezione ordinata composta da coppie nome/valore; •Queue, la classica coda; struttura FIFO (first in first out); •Stack, ovvero last in last out, LIFO; •Hashtable, una collezione nome/valore; •BitArray, una collezione compatta di valorie Boolean; •StringCollection, collezione specializzata per le stringhe; •StringDictionay, ovvero una hashtable specializzata per le stringhe; •ListDictionary, una collezione nome/valore ottimizzata per piccole quantità di dati; •HybridDictionary, una collezione nome/valore ibrida. Per piccole quantità di dati si comporta come una ListDictionary, altrimenti come una Hashtable La System.Collctions prevede tre interfacce di base: •IEnumerable (rende enumerabile la lista), •ICollection (rende omogenee le funzionalità di base), •IList (estende le funzionalità di base comuni).
  • 63. La System.Collctions prevede tre interfacce di base: •IEnumerable (rende enumerabile la lista), •ICollection (rende omogenee le funzionalità di base), •IList (estende le funzionalità di base comuni). Grazie a queste tre interfacce è possibile operare uniformemente sulle varie Collection Classiche, attraverso i classici metodi: insert, remove, add, e così via. dotNet Framework: Collection Classiche Nota a parte merita la possibilità di effettuare l’ordinamento (collection.Sort) di una Collezione. Infatti l’ordinamento varia in base al tipo di oggetto inserito nella collezione Per questo è possibile definire un propria classe di ordinamento (che deve implementare l‘interfaccia IComparer) e della quale un’istanza può essere passata al metodo Sort: coll.Sort(new CaseInsensitiveComparer());
  • 64.  I generics sono stati introdotti dalla versione 2 del framework e sono formati da classi e metodi che lavorano in modo uniforme su tipi differenti  Benefici  Le variabili sono di un tipo ben preciso e non Object  Non è necessario un cast (errori in fase di compilazione)  Riutilizzo reale del codice class Stack<T> { private T[] store; private int size; public Stack() { store = new T[10]; size = 0; } public void Push(T x) { // push code goes here } public T Pop() { return store[--size]; } } void Add(Stack<int> s) { int x = s.Pop(); int y = s.Pop(); s.Push(x+y); } dotNet Framework: Generics
  • 65. dotNet Framework: Generics Tra le implementazioni si annoverano: • List<>, ovvero una semplice lista di elementi; • Queue<>, la coda (FIFO); • Stack<>, lo stack (LIFO); • Dictionary<>, collezione nome/valore; • SortedList<>, lista di elementi ordinati; • SortedDictionay <>, collezione nome/valore ordinata; • NameValuePair<>, coppia nome/valore (in pratica è ritornato dall’iterazione sul dictionary); • LinkedList<>, lista linkata; Tra le interfacce troviamo: •IList<ItemType> •IDictionary<K, V> •IEnumerable<ItemType> •IComparable<OperandType>
  • 66. ArrayList myList = new ArrayList(); myList.Add(1); // Viene effettuato il boxing (value -> refernce) myList.Add(2); // Viene effettuato il boxing (value -> refernce) myList.Add(“3"); // Provocherà un errore a runtime int i = (int)myList[0]; // Necessario effettuare il cast List<int> myList = new List<int>(); myList.Add(1); // Nessun boxing myList.Add(2); // Nessun boxing // myList.Add(“3"); // Errore a tempo di compilazione int i = myList[0]; // Nessun casting richiesto Non-generic Generic dotNet Framework: Generics vs. Collection Classiche
  • 67. Rappresentazione senza Generics Rappresentazione con Generics intobject intobject intobject intobject intobject int int int int int int int Push Pop Box Unbox Push Pop dotNet Framework: Generics vs. Collection Classiche
  • 69. dotNet Framework: Serialization La Serializzazione permette di salvare un oggetto (e, quindi, il suo attuale stato) in modo persistente su un supporto di memorizzazione, ma anche di inviare lo stesso oggetto attraverso le reti per un utilizzo remoto. Il dotNet Framework permette la serializzazione attraverso opportune classi contenute nel namespace System.Runtime.Serialization: •BinayFormatter, permette di serializzare un oggetto in formato binario e trasferirlo su uno stream; •SoapFormatter , permette di serializzare un oggetto in formato XML aderente allo standard SOAP e nel namespace System.Xml.Serialization •XmlSerializer, permette di serializzare un oggetto in formato XML
  • 70. dotNet Framework: Serialization Il dotNet framework utilizza un modo semplice ed immediato per rendere serializzabile un oggetto: [Serializable] class ShoppingCartItem { public int productId; public decimal price; … … <Serializable()> Class ShoppingCartItem Public productId As Integer Public price As Decimal … … VBC# basta, in pratica, inserire l’attributo [serializable] per rendere la classe serializzabile. La serializzazione comunque può essere parziale, escludendo alcuni elementi, come di seguito mostrato: ' VB <NonSerialized()> Public total As Decimal // C# [NonSerialized] public decimal total;
  • 71. dotNet Framework: Serialization, IDeserializationCallback Nel caso in cui si abbiano dei membri (variabili di istanza) calcolati, si può decidere di non serializzarne il valore (per risparmiare spazio), facendolo ricalcolare durante la serializzazione attraverso l’interfaccia IDeserializationCallback ed il relativo metodo IDeserializationCallback.OnDeserialization [Serializable] class ShoppingCartItem : IDeserializationCallback { public int productId; public decimal price; public int quantity; [NonSerialized] public decimal total; public ShoppingCartItem(int _productID, decimal _price, int _quantity) { productId = _productID; price = _price; quantity = _quantity; total = price * quantity; } void IDeserializationCallback.OnDeserialization(Object sender) { / / After deserialization, calculate the total total = price * quantity; } }
  • 72. dotNet Framework: Binary Serialization Come detto, la serializzazione avviene mediante le classi prima descritte e riversando i dati ottenuti su uno stream. Vediamo alcuni esempi: string data = "This must be stored in a file."; FileStream fs = new FileStream("SerializedString.Data", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); // Use the BinaryFormatter object to serialize the data to the file bf.Serialize(fs, data); fs.Close(); FileStream fs = new FileStream("SerializedString.Data", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); // Create the object to store the deserialized data string data = ""; data = (string) bf.Deserialize(fs); fs.Close(); Serializzazione Deserializzazione Se si volesse utilizzare la serializzazione compatibile con lo standard SOAP, basta sostiture BinaryFormatter con SoapFormatter
  • 73. dotNet Framework: XML Serialization Nel caso in cui si voglia rendere il risultato della serializzazione il più portabile possibile, si può ricorrere alla serializzazione XML. L’oggetto da serializzare, in questo caso, deve seguire regole più stringenti: • la classe deve essere pubblica • tutti i membri da serializzare devono essere pubblici • deve esistere un costruttore senza parametri espressamente dichiarato // Create file to save the data to FileStream fs = new FileStream("SerializedDate.XML", FileMode.Create); // Create an XmlSerializer object to perform the serialization XmlSerializer xs = new XmlSerializer(typeof(DateTime)); xs.Serialize(fs, System.DateTime.Now); fs.close(); // Open file to read the data from FileStream fs = new FileStream("SerializedDate.XML", FileMode.Open); // Create an XmlSerializer object to perform the deserialization XmlSerializer xs = new XmlSerializer(typeof(DateTime)); DateTime previousTime = (DateTime)xs.Deserialize(fs); fs.close(); Serializzazione Deserializzazione
  • 74. dotNet Framework: Custom Serialization Se le precedenti forme di serializzazione non sono adeguate ai nostri scopi (caso molto raro) possiamo procedere a realizzare una serializzazione personalizzata. Ciò si può ottenere implementando l’Interfaccia Iserializable (conseguentemente il metodo GetObjectData) ed applicando l’attributo [serializable] alla classe. Tuttavia esiste un metodo più immediato, ma ovviamente meno flessibile, per controllare la serializzazione e quindi personalizzarla: i Serialization Events. Si tratta di eventi che si scatenano utilizzando il BinaryFormatter e sono: •Serializing, scatenato prima di inizializzare la serializzazione; •Serialized, scatenato dopo la serializzazione; •Deserializing, scatenato prima della deserializzazione; •Deserialized, scatenato dopo la deserializzazione; è possibile catturare questi eventi, e gestirli nel modo più opportuno, creando degli appositi metodi all’interno della classe da serializzare e applicandovi l’attributo corrispondente all’evento da catturare: [OnDeserialized] void CheckTotal(StreamingContext sc) { if (total == 0) { CalculateTotal(sc); } }
  • 76. dotNet Framework: Graphics dotNet offre gli strumenti base per la creazione di semplici elementi grafici (linee, cerchi, ecc.), tutti contenuti nel namespace System.Drawing. Attraverso gli strumenti offerti è possibile: •Aggiungere forme all’UI in modo dinamico; •Creare diagrammi; •Editare e ridimensionare immagini; •Cambiare il grado di compressione di una immagine; •Zoommare immagini; •Aggiungere un logo di copyright o del testo ad una immagine. Tra le classi primarie di questo namespace troviamo: Bitmap, Brush, Font, Graphics, Icon, Image e Pen. Un ruolo importante rivestono inoltre le strutture di questo namespace (ad es. Color o Point), che permettono di impostare o modificare gli elementi del disegno.
  • 77. dotNet Framework: Graphics Per disegnare una linea o una figura base, bisogna eseguire tre passi fondamentali: •Creare un oggetto Graphics, partendo dalla form o dal controllo attuale, attraverso il metodo System.Windows.Forms.Control.CreateGraphics; •Creare un oggetto Pen; •Chiamare un metodo dell’oggetto Graphics per disegnare sul controllo usando l’oggetto Pen // Create a graphics object from the form Graphics g = this.CreateGraphics(); // Create a pen object with which to draw Pen p = new Pen(Color.Red, 7); // Draw the line g.DrawLine(p, 1, 1, 100, 100); Graphics g = this.CreateGraphics(); Pen p = new Pen(Color.Blue, 3); g.DrawPie(p, 1, 1, 100, 100, -30, 60);
  • 78. dotNet Framework: Graphics Invece di utilizzare l’oggetto Pen, in abbinamento con il metodo graphics.drawXXX, è possibile utilizzare un oggetto Brush (e il metodo graphics.fillXXX) per poter creare figure con riempimento. Graphics g = this.CreateGraphics(); Brush b = new SolidBrush(Color.Maroon); Point[] points = new Point[] {new Point(10, 10), new Point(10, 100), new Point(50, 65), new Point(100, 100), new Point(85, 40)}; g.FillPolygon(b, points);
  • 79. dotNet Framework: Graphics Grazie alle implementazioni della classe astratta System.Drawing.Image, è possibile elaborare immagini esistenti o crearne di nuove. Questa classe astratta trova nel framework due implementazione: •System.Drawing.Bitmp, per le immagini; •System.Drwaing.Imaging.Metafile, per le immagini animate; Nonostante Image sia una abastract class è possibile utilizzare i metodi implementati per ottenerne un’istanza: Image i = Image.FromFile(@"C:windowsgone fishing.bmp"); pictureBox1.BackgroundImage = i; Se si vuole creare e salvare una nuova immagine si lavoro pressappoco come negli esempi precedenti, eccetto che la creazione dell’oggetto graphics avviene attraverso un metodo apposito: Bitmap bm = new Bitmap(600, 600); Graphics g = Graphics.FromImage(bm); Brush b = new LinearGradientBrush(new Point(1, 1), new Point(600, 600), Color.White,Color.Red); Point[] points = new Point[] {new Point(10, 10), new Point(77, 500), new Point(590, 100), new Point(250, 590), new Point(300, 410)}; g.FillPolygon(b, points); bm.Save("bm.jpg", ImageFormat.Jpeg);
  • 80. dotNet Framework: Graphics Attraverso le funzionalità di System.Drawing è possibile creare anche testo sotto forma di immagine, utile, ad esempio, se si desidera “marchiare” una foto con un proprio identificativo. Graphics g = this.CreateGraphics(); Font f = new Font("Arial", 40, FontStyle.Bold); g.DrawString("Hello, World!", f, Brushes.Blue, 10, 10); Infine il namespace in esame prevede una classe apposita per disegnare/visualizzare le icone di sistema: Graphics g = this.CreateGraphics(); g.DrawIcon(SystemIcons.Question, 40, 40);
  • 81. dotNet Framework: Threading Esecuzione Concorrente di Attività
  • 82. dotNet Framework: Threading Grazie all’utilizzo dei Thread è possibile dotare l’applicazione di più flussi paralleli che possono portare ad un sostanziale miglioramento prestazionale della stessa ed a una migliore interazione utente-applicazione. Il namespace di riferimento è il System.Threading, che annovera la classe Thread e l’enumerazione ThreadState. La creazione di un nuovo thread, effettuabile in qualsiasi parte del codice, avviene attraverso i seguenti passi: 1. Creare un metodo senza parametri e senza argomenti di ritorno: 2. Creare un delegato ThredStart, specificando il metodo creato nel passo 1 3. Creare un nuovo oggetto Thread, specificando il treadstart creato al passo 2 4. Chiamare il metodo Thred.Start per avviare il thread static void SimpleWork() { Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId); }
  • 83. dotNet Framework: Threading In sintesi ecco il codice per avviare un thread: Se è necessario passare informazioni al thread è possibile utilizzare il delegate ParameterizedThreadStart: ThreadStart operation = new ThreadStart(SimpleWork); // Creates, but does not start, a new thread Thread theThread = new Thread(operation); // Starts the work on a new thread theThread.Start(); ThreadStart operation = new ThreadStart(SimpleWork); for (int x = 1; x <= 5; ++x) { Thread theThread = new Thread(operation); theThread.Start(); } static void WorkWithParameter(object o) { string info = (string) o; for (int x = 0; x < 10; ++x) { Console.WriteLine("{0}: {1}", info, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); } } ParameterizedThreadStart operation = new ParameterizedThreadStart(WorkWithParameter); Thread theThread = new Thread(operation); theThread.Start("Hello"); hread newThread = new Thread(operation); newThread.Start("Goodbye"); Il parametro passato deve essere sempre un oggetto.
  • 84. dotNet Framework: richiamo I DELEGATI (Delegate) sono una sorta di CallBack (per chi conosce C++) o più semplicemente puntatori a funzione (C). Essi vengono ampiamente usati in .Net per la gestione degli eventi, quindi per la definizione dell’Handler. La grande differenza rispetto ai linguaggi non gestiti è che i Delegati sono type- safe, ovvero vengono gestiti esattamente come un oggetto la cui signature è ben definita.
  • 85. dotNet Framework: Threading La classe Thread fornisce i metodi necessari per gestire in modo corretto il flusso di eleborazione: • Thread.Start, avvia il thread • Thread.Join, ferma l’esecuzione del chiamante finchè il nuovo thread non termina; • Thread.Abort, abortisce l’esecuzione del thread e solleva una ThreadAbortException; • Thread.IsAlive, verifica se il thread corrente è in esecuzione; • Thread.Priority, ritorna/setta la priorità del thread; • Thread.IsBackground, ritorna/setta lo stato di background del thread;
  • 86. dotNet Framework: Threading Per consentire la condivisione di memoria (quindi dei parametri) tra i vari thread evitando collisioni, il dotNet framework fornisce opportuni strumenti che ne regolano l’accesso. Se stiamo lavorando con variabili condivise ed abbiamo necessità di fare operazioni di in(de)cremento possiamo avvalerci delle funzionlità della classe Interlocked: la classe interlocked “blinda” il parametro su cui opera, rendendo le operazioni atomiche static void UpdateCount() { for (int x = 1; x <= 10000; ++x) { Interlocked.Increment(ref Counter.Count); } }
  • 87. dotNet Framework: Threading Se abbiamo bisogno di “blindare” più istruzioni in modo da renderle atomiche, possiamo ricorre al lock: in realtà il lock è basato sulla classe Monitor che permette un più fine controllo sul codice da blindare. Il dotNet framework supporta inoltre i seguenti metodi di sincronizzazione: • Mutex class • Semaphore class • AutoResetEvent class • ManualResetEvent class public void UpdateCount() { lock (this) { count = _count + 1; if (Count % 2 == 0) evenCount = evenCount + 1; } }
  • 88. dotNet Framework: Threading Non è sempre necessario creare un nuovo thread per avviare attività in parallelo. Infatti il framework dotNet implementa l’ Asynchronous Programming Model (APM), che permette l’esecuzione di porzioni di codice in thread separati. Molte classi supportano APM fornendo le versioni BeginXXX e EndXXX di metodi classici. Come esempio immaginiamo la lettura di un file: potremmo voler realizzare la lettura (lenta) attraverso un flusso parallelo per non bloccare la fruibilità dell’applicativo. Invece di creare un thread separato, possiamo procedere come di seguito: byte[] buffer = new byte[100]; string filename = string.Concat(Environment.SystemDirectory, "mfc71.pdb"); FileStream strm = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous); // Make the asynchronous call IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null); // Do some work here while you wait // Calling EndRead will block until the Async work is complete int numBytes = strm.EndRead(result); // Don't forget to close the stream strm.Close(); Console.WriteLine("Read {0} Bytes", numBytes); Console.WriteLine(BitConverter.ToString(buffer));
  • 89. dotNet Framework: Threading L’utilizzo del modello APM pone una domanda fondamentale: quando verificare la fine del flusso parallelo avviato? Possiamo individuare tre stili: •Wait-until-done, consiste nell’avviare la richiesta, eseguire (eventualmente) altre operazioni ed attendere il termine della richiesta; •Polling, simile al precedente solo che c’è un controllo ciclico del termine della richiesta; •Callback, si crea un delegato che viene passato alla BeginXXX e viene invocato dal framework al termine della richiesta. L’ultimo aspetto da rilevare è che eventuali exception vengono sollevate solo in seguito alla chiamata della EndXXX e non nel momento in cui si verificano
  • 90. dotNet Framework: Threading ….. // Make the asynchronous call strm.Read(buffer, 0, buffer.Length); IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null); // Do some work here while you wait // Calling EndRead will block until the Async work is complete int numBytes = strm.EndRead(result); ….. // Make the asynchronous call IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null); // Poll testing to see if complete while (!result.IsCompleted) { // Do more work here if the call isn't complete Thread.Sleep(100); } // Finished, so we can call EndRead and it will return without blocking int numBytes = strm.EndRead(result); ….
  • 91. …. // Make the asynchronous call IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(CompleteRead), strm); } … //Callback Metod static void CompleteRead(IAsyncResult result) { Console.WriteLine("Read Completed"); FileStream strm = (FileStream) result.AsyncState; // Finished, so we can call EndRead and it will return without blocking int numBytes = strm.EndRead(result); // Don't forget to close the stream strm.Close(); Console.WriteLine("Read {0} Bytes", numBytes); Console.WriteLine(BitConverter.ToString(buffer)); } dotNet Framework: Threading
  • 92. dotNet Framework: Application Domain & Services Isolare il Codice Sconosciuto
  • 93. Spesso è necessario lavorare con assembly esterni, che possono impattare sia sulle performace/stabilità del nostro applicativo, sia sulla sicurezza. Il modo migliore per utilizzare un assembly esterno è quello di utilizzare un Application Domain. dotNet Framework: Application Domain & Services Processo AppDomain Assembly Shared Assembly AppDomain Assembly Shared Assembly Grazie agli Application Domain è possibile caricare, all’interno dello stesso processo, più assembly separatamente tra di loro, in modo che eventuali anomalie non vadano ad impattare l’intero processo o gli altri assembly
  • 94. La classe che permette la gestione degli Application Domain è la System.AppDomain. Vediamo ora alcuni esempi concreti di utilizzo degli Application Domain: dotNet Framework: Application Domain & Services AppDomain d = AppDomain.CreateDomain("NewDomain"); AppDomain d = AppDomain.CreateDomain("NewDomain"); d.ExecuteAssemblyByName("Assembly"); AppDomain d = AppDomain.CreateDomain("NewDomain"); ….. AppDomain.Unload(d) Creazione Caricamento di un Assembly Unload di un Assembly E’ bene notare che se sa in anticipo di voler effettuare l’unload dell’assembly che si va a caricare, l’unico modo per farlo è attraverso un Application Domain
  • 95. Come ci si aspetterebbe, per poter controllare il livello di isolamento di un Application Domain (quindi utilizzarlo come una sandbox) il dotNet framework mette a disposizione una serie di strumenti per configurarlo, attraverso gli Evidence, di cui discuteremo in seguito. Basti sapere che essi permettono di classificare l’AppDomain ed assegnargli delle restrizioni e dei permessi sulle attività da svolgere. Possiamo applicare le restrizioni sia per un singolo assembly e sia per l’intero Application Domain: dotNet Framework: Application Domain & Services object [] hostEvidence = {new Zone(SecurityZone.Internet)}; Evidence internetEvidence = new Evidence(hostEvidence, null); AppDomain myDomain = AppDomain.CreateDomain("MyDomain"); myDomain.ExecuteAssembly("SecondAssembly.exe", internetEvidence); object [] hostEvidence = {new Zone(SecurityZone.Internet)}; Evidence appDomainEvidence = new Evidence(hostEvidence, null); AppDomain d = AppDomain.CreateDomain("MyDomain", appDomainEvidence); d.ExecuteAssembly("SecondAssembly.exe"); Attraverso la classe System. AppDomainSetup è infine possibile controllare ogni ulteriore aspetto di configurazione (dir di base, configuration file, ecc.)
  • 96. I Windows Services (servizi di windows) sono una gamma di applicativi privi di UI che, funzionando in background, svolgono attività cicliche monitorando lo stato del sistema. La creazione di un servizio avviene utilizzando il template di VS ServiceBase, ed implementando il metodi OnStart e OnStop come minimo. Successivamente bisogna creare un’Installer per il servizio che non può essere avviato come semplici applicativo. Una volta installato, il servizio può essere gestito sia attraverso il classici Strumenti di Amministrazione di Windows che attraverso codice (quindi da un assembly esterno) attraverso la classe System.ServiceProcess.ServiceController: dotNet Framework: Application Domain & Services // Connect to the Server service ServiceController sc = new ServiceController("Server"); sc.Stop(); // Stop the service // Wait two seconds before starting the service Thread.Sleep(2000); sc.Start(); // Start the service
  • 97. dotNet Framework: Configuring Application Rendere dinamica un’applicazione attraverso la sua configurazione
  • 98. dotNet Framework: Configuring Application Parola d’ordine: evitare le configurazioni HardCoding, ovvero quelle inserite all’interno del codice e quindi impossibili da modificare se non attraverso i sorgenti. Sarà capitato a tutti, ad esempio, di inserire direttamente all’interno della propria classe la stringa di connessione ad un DB e di dover poi rimettere mano al codice perché l’RDBMS di rilascio usa credenziali diverse di quelle del sistama di Test . Ebbene, grazie agli strumenti messi a disposizione dal namespace System.Configuration e ai file di confirgurazione XML sarà davvero semplice evitare queste situazioni. Questo namespace include due classi principali: ConfigurationManager e Configuration. Di default ogni applicazione ha un proprio file di configurazione utilizzato per la sua inizializzazione: nomeapplicazione.exe.conf
  • 99. dotNet Framework: Configuring Application Vediamo un esempio di file di configurazione: <?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <supportedRuntime version="v1.1.4322" /> </runtime> <appSettings> <add key="Foo" value="Hello World!"/> </appSettings> <connectionStrings> <clear/> <add name="AdventureWorksString" providerName="System.Data.SqlClient“ connectionString="Data Source=localhost; Initial Catalog=AdventureWorks; Integrated Security=true"/> </connectionStrings> </configuration>
  • 100. dotNet Framework: Configuring Application In esso possiamo distinguere tre sezioni principali: • <runtime>, in cui sono riportate le impostazioni riguardanti l’esecuzione dell’assemby . Nel caso in esempio la versione del framework supportata. •<appSettings>, sono parametri di cui si serve l’applicazione e che vengono recuperati durante la sua esecuzione •<connectionStrings>, questa sezione è dedicata a contenere esclusivamente le stringhe di connessione alle base dati (possono essere più di una) // C# NameValueCollection AllAppSettings = ConfigurationManager.AppSettings; Console.WriteLine(AllAppSettings["Foo"]); Console.WriteLine(AllAppSettings[0]); ' VB Dim AllAppSettings As NameValueCollection = ConfigurationManager.AppSettings Console.WriteLine(AllAppSettings("Foo")) Console.WriteLine(AllAppSettings(0)) Vediamo ora come recuperare i valori appSettings salvati nel file di Configurazione:
  • 101. dotNet Framework: Configuring Application La sezione ConnectionStrings è leggermente diversa poiché può contenere dichiarazioni multiple: possiamo sia recuperare la prima connectionString: <?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <clear/> <add name="AdventureWorksString“ providerName="System.Data.SqlClient“ connectionString="Data Source=localhost;Initial Catalog=AdventureWorks; Integrated Security=true"/> <add name="MarsEnabledSqlServer2005String“ providerName="System.Data.SqlClient“ connectionString= "Server=Aron1;Database=pubs; Trusted_Connection=True;MultipleActiveResultSets=true" /> </connectionStrings> </configuration> ConnectionStringSettings MySettings = ConfigurationManager.ConnectionStrings[0]; sia iterare su esse ottenendole la collection: ConnectionStringSettingsCollection ConnectionStrings = ConnectionStringsSection.ConnectionStrings;
  • 102. dotNet Framework: Configuring Application Oltre al file di configurazione dell’applicazione dotNET prevede la possibilità di creare molteplici file XML per evitare proprio l’hard coding e quindi contenere coppie parametri/valori. Tali file vengono poi gestiti direttamente da una classe wrapper specifica. <userSettings> <WindowsApplication2.SampleSettings /> </userSettings> <applicationSettings> <WindowsApplication2.SampleSettings> <setting name="WebServiceUrl" serializeAs="String"> <value>http://www.adatum.com/myservice.asmx</value> </setting> </WindowsApplication2.SampleSettings> </applicationSettings> nell‘esempio sono visibili 2 aree userSettings e applicationSetting che suddividono lo scope dei parametri.
  • 103. dotNet Framework: Configuring Application Per definire un nuovo file di configurazione ed auto-generare la relativa classe è conveniente affidarsi a VS: Aggiungere un nuovo elemento al progetto di tipo Settings File Dopo la creazione del Settings File, VS aprirà un apposito Application Settings Designer, che potrà essere utilizzato per definire i parametri in ogni loro aspetto (nome, tipo, visibilità-scope, valore).
  • 104. dotNet Framework: Configuring Application Salvando il nuovo file di settaggio, troveremo due nuovi elementi nel nostro progetto: 1. Sample.settings, il file XML 2. Sample.cs (.vb), la relativa classe autogenerata Per usare i parametri basta creare una nuova classe del tipo al punto 2 e richiamarne le proprietà: ' VB Dim mySettings as new SampleSettings() Debug.WriteLine(mySettings.WebServiceUrl) // C# SampleSettings mySettings = new SampleSettings(); Debug.WriteLine(mySettings.WebServiceUrl);
  • 106. dotNet Framework: Instrumentation Sotto il nome Instrumentation Micorsoft ha raccolto tutti gli strumenti (ovviamente parliamo di classi, interfacce, enumeratori, ecc.) utilizzabili per: • Gestire i Log • Effettuare il Debugging ed il Tracing • Monitorare le Performance • Controllare i Device di Sistema, le Applicazioni ed il Sistema stesso Tutti questi “strumenti” sono contenuti nel namespace System.Diagnostics Da notare che operando direttamente a contatto con il sistema e siano di semplice utilizzo, essi possono impattare sulle performance del sistema ed inoltre provocare problemi di sicurezza nelle classi partial-trust (in particolare per i Log). Quindi essi vanno utilizzati con parsimonia e molta attenzione.
  • 107. dotNet Framework: Instrumentation Praticamente è impossibile conoscere tutti gli scenari in cui la nostra applicazione verrà eseguita, vista l’infinita possibilità di combinazioni Hw/Sw oggi possibili. Inoltre nonostante ci si sforzi di creare applicazioni prive di bug, qualcuno, prima o poi, ne verrà fuori. Il problema è che sono sempre gli utenti finali ad accorgersi dei bug più insidiosi, e difficilmente essi riescono a segnalare il problema in modo corretto (in fin dei conti perché dovrebbero saperlo farlo, non sono certo dei tecnici!). Proprio in questo scenario ci vengono in aiuto gli strumenti di Logging Events, che permettono alla nostra applicazione di inserire informazioni specifiche all’interno dei log (siano essi quelli di sistema o quelli proprietari dell’applicazione). Tutto il meccanismo si basa sulla classe EventLog, attraverso la quale scrivere o legge un evento in un log è un’operazione banale: public static void CreateEventLog() { EventLog DemoLog = new EventLog("Chap10Demo"); DemoLog.Source = "Chap10Demo"; DemoLog.WriteEntry("CreateEventLog called", EventLogEntryType.Information); } public static void ReadEventLog() { EventLog DemoLog = new EventLog(); DemoLog.Log = "Chap10Demo"; foreach (EventLogEntry DemoEntry in DemoLog.Entries) { Console.WriteLine(DemoEntry.Source + ":" + DemoEntry.Message); }}
  • 108. dotNet Framework: Instrumentation La fase di Debugging è una delle più importanti nello sviluppo di un nuovo applicativo, in quanto permette di analizzarne il comportamento alla ricerca di errori nella logica di funzionamento. Il dotNet framework basa l’azione di debugging su due classi fondamentali (sempre del namespace Systems.Diagnostics): Debugger e Debug Debugger permette di abilitare la connessione ad un debugger (operazione svolta automaticamente dall’IDE VS). Infatti difficilmente si utilizzerà in modo diretto tale classe, essendo possibile utilizzare gli strumenti integrati dell’IDE. Ad esempio uno dei metodi di Debugger è debugger.Break() che permette di inserire un punto di break-point all’interno del codice, esattamente come si fa cliccando al lato sinistro in Visual Studio. Prima di proseguire con la più interessante classe Debug è utile sottolineare che tutte le operazioni connesse vengono eseguite solo se l’ambiente si è in fase di debugging. In fase di compilazione per deployment, le righe di codice relative vengono scartate. Nel caso si vogliano utilizzare tali funzionalità anche in ambiente di deployment si fa ricorso alla classe Trace funzionalmente identica.
  • 109. dotNet Framework: Instrumentation La classe Debug consente un controllo fine sul processo di debugging, attraverso metodi diretti, tra cui il più importante è sicuramente Debug.Assert. Assert permette di testare se una determinata condizione è vera e, in caso affermativo, generare un alert: Similare ad Assert è Fail che permette di lanciare un alert senza verificare una condizione. Altri metodi presenti sono: debug.write, debug.writeif, debug.writeline, debug.writelineif, debug.print, debug.flush. NameValueCollection MySettings = ConfigurationManager.AppSettings; Debug.Assert(MySettings != null && MySettings["Foo"] != null, "There was a problem with either the configuration file or the Foo Setting"); Console.WriteLine(MySettings["Foo"]);
  • 110. dotNet Framework: Instrumentation Il dotNet framework permette di personalizzare la visualizzazione delle informazioni che il debugger visualizza e come esso si comporta attraverso opportuni attributi, che andiamo ad elencare: •DebuggerBrowsableAttribute •DebuggerDisplayAttribute •DebuggerHiddenAttribute •DebuggerNonUserCodeAttribute •DebuggerStepperBoundaryAttribute •DebuggerStepThroughAttribute •DebuggerTypeProxyAttribute •DebuggerVisualizerAttribute la loro utilità dipende dal grado di dettaglio che lo sviluppatore vuole ottenere durante il debugger e una loro padronanza è possibile solo con l’utilizzo pratico.
  • 111. dotNet Framework: Instrumentation Anche l’applicazione scritta in modo perfetto (teroricamente) non troverà gradimento da parte del cliente se non risponde in modo tempestivo alle sue richieste: ecco il concetto di performace. Non ci addentreremo nel dettaglio di queste funzionalità (gestite attraverso le classi Process e il componente/oggetto PerformanceCounter) anche perché si tratta di attività di ottimizzazione dell’applicazione che vengono, purtroppo, raramente effettuate. La cosa da tener presente è che quando si verifica un problema non sempre esso è dovuto ad un errore o un flusso di istruzioni errate, ma può anche essere dovuto a scarse performance dell’ambiente che il cliente sente come errore dell’applicativo.
  • 112. dotNet Framework: Instrumentation L’ultimo set di strumenti messi a disposizione dal namespece Systems.Diagnostics sono i Windows Management Instrumentation (WMI). Attraverso di essi è possibile ottenere informazioni riguardanti ogni aspetto delle risorse del computer, come: Drive, Adattatori di Rete, Servizi, ecc. WMI utilizza una serie di passi ed un linguaggio di interrogazione simile alle query SQL, attraverso la classe DirectoryObjectSearcher. Infatti per ottenere informazioni specifiche bisogna: 1. Dichiarare un’istanza di ConnectionOptions, e settare le proprietà UserName e Password (per motivi di security di accesso); 2. Dichiarare un’istanza di DirectoryObjectSearcher; 3. Creare un oggetto ManagementScope e settarne le proprietà PathName e ConnectionOptions 4. Creare un oggetto ObjectQuery e specificare la query da eseguire; 5. Creare un oggetto ManagementObjectCollection, e assegnargli il valore di ritorno del metodo DirectoryObjectSearcher.Get;
  • 113. dotNet Framework: Instrumentation Vediamo un esempio concreto che consente l’enumerazione dei drive presenti nel sistema: ConnectionOptions DemoOptions = new ConnectionOptions(); DemoOptions.Username = "Bill"; DemoOptions.Password = "mp99!!swa0"; ManagementScope DemoScope = new ManagementScope("machinename", DemoOptions); ObjectQuery DemoQuery = new ObjectQuery("SELECT Size, Name FROM Win32_LogicalDisk where DriveType=3"); ManagementObjectSearcher DemoSearcher = new ManagementObjectSearcher(DemoScope, DemoQuery); ManagementObjectCollection AllObjects = DemoSearcher.Get(); foreach (ManagementObject DemoObject in AllObjects) { Console.WriteLine("Resource Name: " + DemoObject["Name"].ToString()); Console.WriteLine("Resource Size: " + DemoObject["Size"].ToString()); } In modo altrettanto semplice è possibile ottenere informazioni riguardanti i Servizi di Sistema: private static void ListPausedServices() { ManagementObjectSearcher DemoSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service WHERE Started = FALSE"); ManagementObjectCollection AllObjects = DemoSearcher.Get(); foreach (ManagementObject PausedService in AllObjects) { Console.WriteLine("Service = " + PausedService["Caption"]); }}
  • 114. dotNet Framework: Application Security Garantire la Sicurezza delle proprie Applicazioni
  • 115. dotNet Framework: Application Security Realizzare applicazioni SICURE che non effettuino operazioni indesiderate e non consentite sui sistemi degli utilizzatori è ormai diventato un must. Attraverso le CAS, Code Access Security, è possibile limitare in modo granuale quello che il managed code può fare. Il concetto è simile ai diritti applicati ai ruoli degli utenti/gruppi (Role Based Security – RBS) all’interno del Sistema Operativo. Con CAS è possibile controllare l’accesso a: • File System • Registro • Stampanti • Log degli Eventi • Accesso al Web • … CAS è utilizzabile solo per controllare managed code, mentre il codice non gestito (come, tra l’altro, il codice gestito Full-Trust) non è soggetto alle restrizioni. Inoltre in nessun caso il codice eseguito può avere più permessi di quelli concessi all’utente che lo esegue. CAS si compone di 5 elementi fontamentali: Evicende, Permission, Permission Sets, Code Group e Security Policy.
  • 116. dotNet Framework: Application Security Prima di continuare è utili far presente che la difficoltà maggiore nell’apprendere le CAS è nella terminologia usata, piuttosto che negli strumenti in se. • Evidence Prove di protezione • Strong name Nome sicuro • Code Group Gruppi di codice • Full trust Attendibilità totale • Partial trust Attendibilità parziale • Security Policy ... • Permission Autorizzazioni • Permission Set Set di autorizzazioni • Stack Walk ... • Assert ... • Demand ... • ... ...
  • 117. dotNet Framework: Application Security Per riconoscere e classificare un assembly CAS utilizza l’EVIDENCE (ovvero: chi sei?), una sorta di identificativo dell’assembly. Evidence è la prova oggettiva per sapere di chi è o da dove proviene il codice managed La CAS può identificare un assembly basandosi su: •Cartella dove risiede •Sito o Url da cui viene scaricato •Zona in cui si trova •Strong Name presente nel suo manifest •Hash dell'assembly •Firma digitale (Authenticode, stessa degli Activex) Al caricamento dell'assembly in memoria, viene subito ricavata l'evidence. È possibile da codice associare una evidence ad un AppDomain in modo da isolare degli assembly.
  • 118. dotNet Framework: Application Security CAS utilizza le PERMISSION (ovvero: cosa vuoi fare?) per consentire ad un assembly di porter effettuare determinate operazioni (per esempio l’apertura di una File Dialog Box). Il dotNet framework ne predefinisce un certo numero, trasversali a quelli più noti come NTFS, SQL, etc. Per ciascun permesso è possibile definire dei valori molto dettagliati, come mostra l’empio relativo all’accesso ai socket: dotNet prevede 19 permessi base contenuti nel namespace System.Security.Permissions, realative ad altrettante attività che possono impattare sulla sicurezza del sistema. Questi 19 permessi vengono spesso combinati tra loro per creare i Permission Sets ovvero set di permessi da applicare unitariamente ad un assembly.
  • 119. dotNet Framework: Application Security Il dotNet framework prevede 7 Permission Sets di default: Permission Set Description FullTrust Esenta l’Assembly dall’essere sottoposto alle restirzioni CAS SkipVerification Permette all’Assembly di baipassare i controlli, aumentando le performance a scapito della sicurezza. Execution Garantisce all’Assembly solo il permesso di esecuzione e nessun altro Nothing Nessun permesso concesso, nemmeno quello di esecuzione LocalIntranet Garantisce un consistente numero di permessi, compreso quello di stampa e di accesso ai logo. L’accesso al File System è invece limitato alla sola apertura delle Open/Save dialog Box Internet Garantisce un ristretto numero di permessi, proprio in virtù della provenienza dell’assembly (internet) da un ambiente potenzialmente ostile. Everithing Garantisce tutti i permessi, ma a differenza del FullTrust l’assembly è comunque soggetto al controllo CAS.
  • 120. dotNet Framework: Application Security In pratica grazie all’evidenze ed ai permission il codice gestito viene prima controllato e se le autorizzazioni di cui dispone sono sufficiente all’attività che si appresta a svolgere, ciò viene concesso. Esecuzione Permission Evidence CodiceAutenticazione Autorizzazione CAS
  • 121. dotNet Framework: Application Security Grazie ai Code Groups è possibile collegare indirettamente i Permission Sets agli Assembli, consentendo in tal modo di riflettere una modifica del Pemission Sets applicato al Code Groups a tutti gli assembly relativi. Lo schema segue l’assegnazione degli utenti ai gruppi a cui sono associati i permessi (RBS Role Based Security). Permissi on Sets Code Grups Assembly (assegnati al gruppo attraverso l’evidence) Di default esistono 5 Code Groups: Code Group Evidence Permission Set My_Computer_Zone Zone: My Computer FullTrust LocalIntranet_Zone Zone: Local Intranet LocalIntranet Internet_Zone Zone: Internet Internet Restricted_Zone Zone: Untrusted sites Nothing Trusted_Zone Zone: Trusted sites Internet Ovviamente un assemby può appartenere a più Code Grups. In tal caso i permessi sono Il risultato dell’unione dei permessi dei singoli gruppi.
  • 122. dotNet Framework: Application Security L’ultimo elemento che compone le CAS è la SECURITY POLICY un insieme di code group e permission sets che definiscono le politiche (policy) di sicurezza del sistema. Per default esistono 4 policy level di defalut: Enterprise, Machine (o Computer), User (o Utente) e Application Domain, che consentono un controllo minuto dei diritti concessi. Come è facile intuire dai nomi essi vanno dal livello più alto dell’ambiente (Enterprise) a quello più ristretto (Application Domain). Volendo essere semplici possiamo dire che le restrizioni Enterprise si applicano a tutto l’ambiente fino a giungere alle restrizioni di Application Domain specifiche del dominio di esecuzione. I diritti di cui un assembly dispone sono il risultato dell’intersezione delle policy dei vari livelli Enerprise Machine User App. Domain
  • 123. Al runtime viene fatto il calcolo dei permessi effettivi: 1. 2. Unione Intersezione void SetAppDomainPolicy(PolicyLevel domainPolicy); dotNet Framework: Application Security
  • 124. dotNet Framework: Application Security CAS può essere configurato sia tramite riga di comando, attraverso l’utility CasPol, sia attraverso il Framework Configuration Toolkit Code Groups Permission Set Security Policy Policy Assemblies
  • 125. dotNet Framework: Application Security Fin ora abbiamo visto come gestire/impostare le CAS attraverso gli strumenti di sistema forniti dal framework. Ovviamente esiste la possibilità di impostare le permission di un assembly in modo esplicito direttamente dal codice. Per fare ciò è possibile ricorre a due soluzioni: 1. Declarative Security, fa uso di attributi ed è l’unico applicabile all’intero assembly; 2. Imperative Security, fa uso di apposite classi ed è utilizzabile per proteggere specifiche porzioni di codice. L’utilizzo degli attributi (quindi declarative) è utile anche all’amministratore di sistema per determinare di quali diritti ha bisogno il nostro assembly. [assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:boot.ini")] namespace DeclarativeExample { class Program { …. Dichiarativo Try { // Assembly logic } Catch { EventLogPermission errorPerms = new EventLogPermission(PermissionState.Unrestricted); errorPerms.PermitOnly(); // Log event CodeAccessPermission.RevertPermitOnly(); } Imperativo
  • 126. Lo strumento principale sono le classi xxxPermission e relativi attributi che permettono di chiedere alla CAS di eseguire controlli e, se necessario lanciare la SecurityException ISecurityEncodable IStackWalk IPermission FileIOPermission UIPermission EnvironmentPermssion PermissionSet FileIOPermission Attribute UIPermission Attribute EnvironmentPermssion Attribute CodeAccessPermission Attribute CodeAccessPermission dotNet Framework: Application Security
  • 127. Nel caso si voglia applicare la CAS ad un assembly (obbligatoriamente in modo Dichiarativo), bisogna definire la Action Property secondo uno dei seguenti valori dell’enumerazione SecurityAction: • RequestMinimum. La permission indicata è indispensabile per l'assembly altrimenti l'assembly non viene caricato. • RequestOptional. Permessi aggiuntivi rispetto al minimo indispensabile. Vengono dati all'assembly solo se disponibili. • RequestRefuse. Permessi che questo assembly decide di togliersi per evitare di essere usato da codice malicious. Se sono specificati RequestOptional e RequestRefuse allora verranno attribuiti all'assembly solo quei permessi anche se le policy sono più alte. Il tool PermView permette di vedere nell'assembly i requisiti di security dotNet Framework: Application Security
  • 128. SecurityException • Demand • disponibile anche come attributo • verifica il permesso richiesto eseguendo lo stack walk • notare che Metodo4 non è soggetto ad alcun controllo. Il controllo è sui chiamanti • LinkDemand • solo come attributo • verifica il permesso richiesto solo sul chiamante senza eseguire stack walk • il controllo viene eseguito dal jitter • InheritanceDemand • solo come attributo • verifica il permesso richiesto sulle classi che derivano da quella su cui è applicato l'attributo • il controllo viene eseguito al load OK Demand Stack walk Metodo1 Metodo2 Metodo3 Metodo4 permission negata Stack walk // Richiesta imperativa ... FileIOPermission perm = new FileIOPermission( FileIOPermissionAccess.AllAccess, @"c:"); perm.Demand(); // ... oppure dichiarativa (classe/metodo) [assembly:FileIOPermission( SecurityAction.Demand, All = @"C:") ] dotNet Framework: Application Security Nel caso si voglia applicare la CAS a elementi del codice come classi, metodi o parte di esso (in modo Diarativo o Imperativo), bisogna definire i cosiddetti stack walk modifiers:
  • 129. • Assert e RevertAssert • Impediscono che la richiesta di permesso si propaghi ai chiamanti • Assert si chiama al più tardi e RevertAssert al più presto • Utile per evitare perdite di performance dovute al controllo della CAS • Deny e RevertDeny • Impedisce che tutti i metodi chiamati da questo possano accedere alla risorsa (...) • PermitOnly e RevertPermitOnly • Permette a tutti i metodi chiamati da questo di poter accedere solo alla risorsa specificata (...) Gli stack walk modifiers vengono rimossi automaticamente alla fine del metodo che li chiama oppure chiamando esplicitamente il corrispondente Revert Deny e PermitOnly si usano su un metodo chiamante mentre RequestRefuse solo sull'assembly chiamato OK Demand Stack walk Metodo2 Metodo3 Metodo4 permission negata Assert dotNet Framework: Application Security Metodo1
  • 130. dotNet Framework: Application Security [FileIOPermission(SecurityAction.Demand, Write = @"C:Program Files")] public static void createProgramFolder() { // Method logic } [WebPermission(SecurityAction.Demand, ConnectPattern = @"http://www.microsoft.com/.*")] public static void requestWebPage() { public static void createProgramFolder() { try { FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Write, @"C:Program Files"); filePermissions.Demand(); // Method logic } catch { // Error-handling logic } } public static void requestWebPage() { try { Regex connectPattern = new Regex(@"http://www.microsoft.com/.*"); WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern); webPermissions.Demand(); // Method logic } catch { // Error-handling logic } } Vediamo due esempi di una stessa restrizione attraverso l’uso dichiarativo e imperativo: Dichiarativo Imperativo
  • 131. dotNet Framework: Application Security Fin qui il discorso relativo alle CAS. Poniamoci però un dubbio: cosa accade se il nostro applicativo ha bisogno di uno spazio temporaneo su disco ma non ha sufficienti permission? In tal caso si ricorre all’Isolated Storage, ovvero un area protetta e riservata su disco su cui l’assembly può lavorare senza problemi. Si può definire uno storage isolato sulla base di: • Identità del codice dell'applicazione • Identità dell'utente e gestirlo attraverso il namespace dedicato: System.IO.IsolatedStorage Come è prevedibile la lettura/scrittura nello storage isolato avviene tramite stream: IsolatedStorageFile userStore = IsolatedStorageFile.GetUserStoreForAssembly(); IsolatedStorageFileStream userStream = new IsolatedStorageFileStream("UserSettings.set", FileMode.Create, userStore); StreamWriter userWriter = new StreamWriter(userStream); userWriter.WriteLine("User Prefs"); userWriter.Close();
  • 132. dotNet Framework: Application Security Abbiamo detto che il CAS è applicato al codice managed e agli assembly Partially Trusted. Se vogliamo, invece, rendere un assemply Full Trusted, dobbiamo firmarlo, ovvero assegnargli uno Strong Name Le specifiche ECMA raccomandano l'uso dello strong name (che dovrebbe essere "aziendale" e ben custodito) solo per il versioning e MAI per sicurezza, questo perché esistono sistemi per ingannare il CLR e far caricare un Assembly tampered anche se firmato con strong name. Comunque lo Strong Name è necesarrio qualora si voglia registrare l’assembly nella GAC (Global Assembly Cache) Infine, se un assembly partially trusted chiama un full trusted con strong name, questo necessita dell'attributo: [assembly:AllowPartiallyTrustedCallers]
  • 134. dotNet Framework: Interoperation Se si decide di utilizzare le potenzialità del dotNet Framework possiamo affidarci tranquillamente alla sua infrastruttura e guardare con convinzione al managed code. Esiste tuttavia la possibilità che, per svariati motivi, ci si trovi in una delle situazioni seguenti: • necessità di riutilizzare componenti COM (Component Object Model) proprietari; • necessità di creare componenti COM da integrare in sistemi legacy preesistenti • necessità lavorare direttamente con le API di sistema; Per assisterci in tutti e tre i casi, dotNet ci viene in aiuto con gli strumenti messi a disposizione dal namespace: System.Runtime.InteropServices
  • 135. dotNet Framework: Interoperation Mettiamo che la nostra software house abbia impiegato mesi per sviluppare uno specifico componente COM (analisi, progettazione, sviluppo, testing, collaudo,ecc.), myCom. Ora nuove necessità hanno portato l’azienda a sposare il dotNet Framework. In questo nuovo contesto si desidera però recuperare myCom. Come si può procedere? Anche in questo caso il dotNet Framework ci da una mano, mettendoci a disposizione gli strumenti per creare un wrapper chiamato Runtime Callable Wrapper (RCW) che lavoro secondo il pattern proxy. Detto in soldoni: si esegue un automatismo del framework (o Visual Studio), si ottiene il wrapper e lo si usa come una normale l libreria dotNet. Purtroppo non sempre i tool automatizzati riescono a generare l’RCW.In tal caso bisogna procedere manualmente come mostrato a breve.
  • 136. dotNet Framework: Interoperation Per creare RCW possiamo servirci di Visual Studio o del tool da riga di comando TlbImp.exe. Nel caso si usi VS, basta selezionare il progetto (nella finestra Esplora Soluzioni), clicckare con il tasto destro e scegliere Aggiungi Riferimento. Nella finestra che si aprirà scegliere il Tab “COM” e quindi il proprio componente. Una volta confermata l’operazione VS, se tutto va a buon fine,aggiunge il riferimento dell’oggetto COM alla libreria e a quel punto è possibile utilizzarlo come un normale componete/libreria dotNET. Se si desidera operare attraverso TlbImp, basta procedere dando il seguente comando: il risultato sarà una libreria .dll con lo stesso nome dell’originale, da referenziare nel nostro progetto dotNet. tlbimp <dllname>.dll
  • 137. dotNet Framework: Interoperation Nell’uso di oggetti COM in .NET bisogna fare attenzione in particolare a due aspetti: • COM supporta i parametri opzionali (così come VBASIC),mentre ciò non è vero per C#. Per poter essere eseguito bisogna, in C#, comunque passare tutti i parametri, anche se opzionali. dotNet in tal senso introduce un oggetto vuoto attraverso la classe Type.Missing, che è possibile utilizzare allo scopo ed evidenziare subito il suo significato (evitando la confusione che si creerebbe con oggetti “dummy” ovvero creati solo per sopperire al problema). In realtà una best-practices è quella di usare tali oggetti anche in VB, poiché Microsoft sembra intenzionata ad eliminare il supporto ai parametri opzionali ' VB Imports Microsoft.Office.Core Imports Microsoft.Office.Interop.Excel Dim NewExcelApp As New Microsoft.Office.Interop.Excel.Application 'This works fine NewExcelApp.Worksheets.Add() // C# using Microsoft.Office.Core using Microsoft.Office.Interop.Excel Application NewExcelApp = new Application(); // This will not comipile. NewExcelApp.Worksheets.Add();
  • 138. dotNet Framework: Interoperation Best practices ' VB Module Module1 Private OptionalParamHandler As Object = Type.Missing Sub Main() Dim NewExcelApp As New Microsoft.Office.Interop.Excel.Application NewExcelApp.Worksheets.Add(OptionalParamHandler, OptionalParamHandler, _ OptionalParamHandler, OptionalParamHandler) End Sub End Module // C# class Program { private static Object OptionalParamHandler = Type.Missing; static void Main(string[] args) { Application NewExcelApp = new Application(); NewExcelApp.Worksheets.Add(ref OptionalParamHandler, ref OptionalParamHandler, ref OptionalParamHandler, ref OptionalParamHandler); } }
  • 139. dotNet Framework: Interoperation Il Secondo aspetto da tenere presente è la Gestione delle Eccezioni, ovvero come catturare e rispondere alle eccezioni generate da un oggetto COM – RCW. A differenza di quanto accedeva con la versione 1.x del dotNet Framework, nella versione 2 è stata introdotta la classe RuntimeWrappedException appartenente al namespace System.Runtime.CompilerServices, che permette di gestire le eccezioni COM esattamente come se fossero eccezioni standard del CLS. Al verificarsi di un’eccezione COM, dotNet crea un oggetto di tipo RuntimeWrappedException, ed assegna alla sua proprietà WrappedException l’oggetto stesso che ha catturato l’eccezione per consentire di gestirlo in modo appropriato. Se per qualsiasi motivi si dovesse avere la necessità di gestire eventuali eccezioni COM attraverso il nuovo formato, è possibili disabilitarle attraverso appositi attributi: ' VB Imports System.Runtime.CompilerServices [assembly: runtimeCompatibility(WrapNonExceptionThrows=false)] // C# using System.Runtime.CompilerServices; [assembly: RuntimeCompatibility(WrapNonExceptionThrows=false)]
  • 140. dotNet Framework: Interoperation Private Sub IllustrateExceptions() Try ‘Something that throws an exception Catch ex As Exception ' In the previous versions this will catch only CLS-Compliant ' In the current version both CLS and Non CLS-Compliant will be caught by this block. End Try ' There is no equivalent for Catch without an exception ' because it's considered unreachable. End Sub private static void IllustrateExceptions() { try{ // Something that throws an exception } catch (Exception ex){ // In the previous versions this will catch only CLS-Compliant // In the current version both CLS and Non CLS-Compliant will be caught by this block. }} Gestione Eccezioni COM “2.0”
  • 141. dotNet Framework: Interoperation Oltre ad avere la necessità di riutilizzare componenti COM, potremmo trovarci nella situazione di doverne creare di nuovi, per poter aggiungere funzionalità ad applicazioni esistenti. Anche i tal caso dotNet ci viene in aiuto con un wrapper apposito: COM Callable Wrapper (CCW), attraverso il quale possiamo esportare il componente scritto in dotNet in modo che sia compatibile con COM. Per creare la nostra nuova libreria COM-Compatibile usando VS, dobbiamo : 1. Creare un nuovo progetto 2. Aprire la dialog box “Propietà del Progetto”, clicckando con il tasto destro sul progetto e scegliendo proprietà. 3. Spuntare l’opzione Registra per COM 4. Compilare il tutto Creato il componente sarà il compilatore a preoccuparsi di aggiungere le necessarie informazioni per renderlo compatibile con COM.
  • 142. dotNet Framework: Interoperation Per creare correttamente un componente esportabile in COM, bisogna seguire alcune regole: • Tutte le classi devono avere un costrutto senza parametri esplicitamente dichiarato; • Tutti i tipi che si vuole rendere visibili devono essere pubblici; • Tutti i metodi che si vuole rendere visibili devono essere pubblici; • Le classi Astratte non possono essere esportate; Public Class ComVisiblePerson Private _firstName As String Private _lastName As String Public Property FirstName() As String Get Return Me._firstName End Get Set(ByVal value As String) Me._firstName = value End Set End Property Public Property LastName() As String Get Return Me._lastName End Get Set(ByVal value As String) Me._lastName = value End Set End Property End Class namespace NetForComDemoCS { class ComVisiblePerson { private String firstName; private String lastName; public String FirstName { get { return firstName; } set { firstName = value; } } public String LastName { get { return lastName; } set { lastName = value; } } } } Attraverso appositi attributi è poi possibile indicare quali Assembly e Metodi devono essere visibili o meno come COM: ' VB <Assembly: ComVisible(False)> // C# [assembly: ComVisible(false)] ' VB <ComVisible(False)> _ Public Class ComVisiblePerson // C# [ComVisible(false)] class ComVisiblePerson
  • 143. dotNet Framework: Interoperation Abbiamo parlato fin ora di oggetti COM, ma cosa accade se abbiamo bisogno di utilizzare una libreria non conforme allo standard COM o API di sistema non supportate direttamente dal framework? Utilizziamo il cosiddetto Platform Invoke (P/Invoke), ovvero andiamo a wrappare manualmente la nostra dll: Imports System.Text Imports System.Runtime.InteropServices Public Class WindowExample Private Const BufferSize As Int32 = 256 <DllImport("user32.dll")> _ Private Shared Function GetForegroundWindow() As IntPtr End Function <DllImport("user32.dll")> _ Private Shared Function GetWindowText(ByVal _ hWnd As IntPtr, ByVal textValue As StringBuilder, ByVal counter As Int32) As Int32 End Function … using System.Runtime.InteropServices; namespace OptionalCS { class WindowExample { private const Int32 BufferSize = 256; [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern Int32 GetWindowText(IntPtr hWnd, StringBuilder textValue, Int32 counter); ….
  • 144. dotNet Framework: Interoperation Il modo migliore per lavoare con P/Invoke, è quello di creare una classe wrapper per ogni dll che si vuole utilizzare (Encapsulating) e utilizzare solo quest’ultima all’interno del codice. Questo “incapsulamento” porta a oggettivi vantaggi, quali: • Gli utilizzatori della classe non si renderanno conto del fatto che sia una classe- wrapper di una dll • L’utilizzo nel proprio progetto della dll sarà nascosto, evitando di usare i formalismi di P/Invoke che possono confondore Nell’uso di P/Invoke bisogna fare molta attenzione alla tipologia dei dati trattati: infatti non è detto che i vari tipi corrispondano tra loro. Bisogna quindi prendere le necessarie contromisure (Data Type Converting e Marshaling) per ovviare ai problemi. Inoltre bisogna ricordarsi che le CallBack (ovvero le chiamate a funzione) devono essere mappate attraverso opportuni Delegati, da definire in base alla signature della callback stessa.
  • 146. dotNet Framework: Reflection Attraverso la Reflection dotNET mette a disposizione un elaborato strumento che permette di effettuare ad un programma l’auto analisi della sua struttura. Attraverso la reflection è possibile, ad esempio, conoscere i tipi contenuti nell’assembly o il Modulo a cui appartengono o ancora i suoi metadati. Tutto questo è possibile attraverso le funzionalità messe a disposizione dal namespace System.Reflection e dalla classe statica Assembly
  • 147. dotNet Framework: Reflection La domanda è: a cosa serve la Reflection? Attraverso la Reflection è possibile ottenere informazioni da un assembly a runtime e quindi, ad esempio, invocare un metodo costruendone dinamicamente la chiamata. Tutti noi utilizziamo il completamento automatico dei metodi (e non solo) messo a disposizione di VS: ebbene l’IDE Microsoft riesce a fare ciò proprio grazie alla reflection. Questo strumento ha anche un’ulteriore potenzialità: creare un assembly runtime e addirittura salvarlo su disco. Per quanto riguarda la Reflection non andiamo oltre, essendo un argomento avanzato e che necessita di approfondimenti ad-hoc.
  • 148. dotNet Framework: Globalization Rendere internazionali le proprie Applicazioni
  • 149. dotNet Framework: Globalization Il contesto geografico in cui un software verrà utilizzato è spesso un fattore sottovalutato durante la sua realizzazione. Immaginate però quanti problemi sorgono se il nostro prodotto inizialmente pensato solo per il mercato italiano deve essere adattato a quello francese o a quello inglese. dotNET viene incontro ai problemi di internazionalizzazione grazie al namespace System.Globalization. Partiamo però dal chiarire cosa intendiamo per Cultura (culture): per cultura si intendono un insieme di elementi (calendario, valuta, lingua, ecc.) specifici di un determinata nazione o area geografica. Esistono tre tipologie di culture: • Invariant Culture: è una cultura invariante (basata sul linguaggio inglese) da utilizzare nel caso in cui si voglia garantire una sorta di consistenza internazionale degli elementi culturali (es: data e ora di una trial application); • Neutral Culture: una cultura è neutrale quando è associata solo ad una lingua (Francese, Inglese, Tedesco) ma non alla rispettiva nazione o area geografica. Essa viene indicata con le iniziali minuscole della lingua: fr, en, sp, it; •Specific Culture: una cultura specifica è relativa ad una nazione e viene rappresentata dalla cultura neutrale più l’abbreviazione maiuscola di quella specifica: fr-FR (francia) en-US (stati uniti), en-EN (inghilterra);
  • 150. dotNet Framework: Globalization L’utilizzo della cultura appropriata è fondamentale quando, su sistemi internazionali, si desidera che i valori analizzati siano interpretati allo stesso modo. Prendiamo ad esempio una data italiana composta da gg/mm/aa: 05/11/2007 (5 novembre 2007). Per noi è chiaro che le prime due cifre rappresentano il giorno, ma se la facciamo leggere ad un americano la interpreterà come: l’11 Maggio 2007 (mm/gg/aa). Stesso problema relativo al significato del (.) e della (,) nei numeri. La soluzione dotNET passa attraverso 2 classi principali CultureInfo e RegionInfo ed un enumeratore CultureTypes.
  • 151. dotNet Framework: Globalization Vediamo alcune situazioni pratiche. Per rilevare la cultura corrente possiamo procedere nel modo seguente: Dim UsersCulture As CultureInfo = Thread.CurrentThread.CurrentCulture Console.WriteLine("The currentculture of this application is : “ + UsersCulture.Name) CultureInfo UsersCulture = Thread.CurrentThread.CurrentCulture; Console.WriteLine("The current culture of this application is : “ + UsersCulture.Name); allo stesso modo è semplice cambiare la cultura attuale: ' VB tbSalary.Text = Format(100000, "Currency") // C# tbSalary.Text = (100000).ToString("C"); Thread.CurrentThread.CurrentCulture = new CultureInfo("es-VE"); Per formattare correttamente una stringa (ad esempio un valore monetario variando il punto con la virgola, ecc.) dobbiamo usare appositi strumenti di formattazione:
  • 152. dotNet Framework: Globalization CurrentCulture rappresenta la cultura con cui vengono effettuate le manipolazioni durante l’esecuzione del programma. Dualmente esiste CurrentUICulture che rappresenta la cultura utilizzata per l’Interfaccia Utente, il cui valore può essere letto/variato esattamente come CurrentCulture (sostituendo ovviamente CurrentUICulture). La cosa importante da evidenziare è che CurrentUICulture può essere settata solo nella fase di start dell’applicazione (nel main), al contrario di CurrentCulture Alla classe CultureInfo il framework affianca RegionInfo che fornisce informazioni relative all’area geografica specifica: ' VB Dim UsersCulture As CultureInfo = Thread.CurrentThread.CurrentCulture Dim DemoRegion As RegionInfo = New RegionInfo(UsersCulture.LCID) Or: Dim DemoRegion As RegionInfo = New RegionInfo(UsersCulture.Name) // C# CultureInfo UsersCulture = Thread.CurrentThread.CurrentCulture; RegionInfo DemoRegion = new RegionInfo(UsersCulture.LCID); Or: RegionInfo DemoRegion = new RegionInfo(UsersCulture.Name);
  • 153. dotNet Framework: Globalization Il risultato della comparazione può essere modificato passando alla compare,come terzo parametro, uno dei valori di CompareOptions ' VB Dim FirstString = "Coté" Dim SecondString = "coté" Dim DemoInfo As CompareInfo = New CultureInfo("fr-FR").CompareInfo DemoInfo.Compare(FirstString, SecondString) // C# String FirstString = "Coté"; String SecondString = "coté"; CompareInfo DemoInfo = new CultureInfo("fr-FR").CompareInfo; DemoInfo.Compare(FirstString, SecondString); Quando abbiamo introdotto la globalization abbiamo fatto riferimento, come esempio, al problema della rappresentazione delle date. Per risolvere il relativo problema di internazionalizzazione si può far ricorso alla classe DateTimeFormatInfo così come nel caso delle valute si può utilizzare la classe NumberFormatInfo L’ultima classe che evidenzieremo è la CompareInfo che unitariamente all’enumerator CompareOptions permette di fare comparazioni tra stringhe. La comparazione tra stringhe può avere esiti diversi in base alla cultura utilizzata. Ad esempio alcune culture potrebbero ignorare la differenza di maiuscole e minuscole in una stringa,mentre altre ritenerle fondamentali:
  • 154. dotNet Framework: Globalization Nel caso in cui avessimo bisogno di creare una nostra cultura (caso molto raro ma non assurdo), possiamo far ricorso alla classe CultureRegionAndInfoBuilder e all’enumerator CultureAndRegionModifiers. In generale è possibile partire da una cultura esistente e variarne alcuni elementi di base: ' VB Dim DemoBuilder As new CultureAndRegionInfoBuilder("en-US", CultureAndRegionModifiers.Neutral) // C# CultureAndRegionInfoBuilder DemoBuilder = new CultureAndRegionInfoBuilder("en-US", CultureAndRegionModifiers.Neutral);