Dot net framework 2
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Dot net framework 2

  • 2,288 views
Uploaded on

Dot net framework 2 overview

Dot net framework 2 overview

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
2,288
On Slideshare
2,288
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
13
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Microsoft .Net Framework DotNet FRAMEWORK Architettura e DettagliIng. Felice Pescatorefelice.pescatore@poste.it
  • 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 Distribuited Windows Based Web Based Client Applicatin Client Development Development Development Esame 70-526 Esame 70-528 Esame 70-529 Design e Developing Design e Developing Design e Developing Windows Based Web Based Enterprise Application Application Application Esame 70-548 Esame 70-547 Esame 70-549
  • 4. Certificazioni Microsoft: testi ufficiali
  • 5. Certificazioni Microsoft: SQL Server Esame 70-431
  • 6. Certificazioni Microsoft: Compendi Consigliati Serie Passo-Passo, di base Serie Programming Autori: • msdn2.microsoft.com/it-it/library/default.aspx •Francesco Balena • www.ugidotnet.org •Dino Esposito • www.aspitalia.com •Marco Bellinaso • www.dotnet2themax.it •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. Il dotNET Framework dotNet presenta un‟architettura a strati. Al suo interno possiamo distinguere gli elementi portanti dell‟intero 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 dellesecuzione vera e propria delle applicazioni: assicura che vengano rispettate tutte le dipendenze, gestisce la memoria, la sicurezza, lintegrazione del linguaggio e così via. Il runtime fornisce numerosi servizi che consentono di semplificare la stesura del codice, la distribuzione dellapplicazione e di migliorare laffidabilità della stessa.
  • 11. Il .NET Framework: il CLR (2) Con il CLR, Microsoft ha posto alcuni obiettivi cardine della propria tecnologia: •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
  • 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. Il dotNET Framework: il CLR (4) 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
  • 14. Il dotNET Framework: la BCL 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 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.
  • 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 laccesso ai dati ottimizzato per ambienti N-tier ed interoperabile con lXML ed i documenti XML. ADO.NET è stato progettato per gli ambienti debolmente accoppiati e per fornire servizi per laccesso 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. Il dotNET Framework: ASP.NET 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 alloggetto che mette a disposizione degli sviluppatori Web tutta la ricchezza di questo tipo di programmazione.
  • 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 apputno 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 assegnanzione 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 Framewor. In pratica descrivo come il compilatore deve trasformare il codice sorgente nel codice intermedio (Itermediate 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. Compilazione e Generazione dell’Assembly Source code Assembly Compilatore es: Csc.exe o Vbc.exe C++, C#, Visual DLL o EXE Basic o altri linguaggi .NET L‟applicativo, scritto in uno dei linguaggi supportati, viene compilato, generando cosìun ASSEMBLY, che nonostante l‟estensione,è un packaging composto da più elementi.
  • 27. Assembly dotNet Un ASSEMBLY rappresenta il costituente fondamentale di unapplicazione .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 allinterno della propria unità implementativa • esportabili per essere utilizzati dal codice al di fuori di tale unità.
  • 28. Assembly Manifest: Descrizione Gli assembly si autodescrivono tramite il proprio MANIFEST (manifesto), che costituisce una parte integrante di ogni assembly stesso: • Stabilisce lidentità dellassembly in Type Descriptions termini di nome, versione, livello di Classes condivisione tra applicazioni diverse, Base classes firma digitale. Implemented interfaces •Definisce quali file (nome e file hash) Data members costituiscono limplementazione Methods dellassembly. •Specifica le dipendenze in fase di Assembly Manifest compilazione da altri assembly. Name •Specifica i tipi e le risorse che Version costituiscono lassembly, inclusi quelli Culture che vengono esportati dallassembly. Other assemblies •Specifica linsieme dei permessi Security permissions necessari al corretto funzionamento Exported types dellassembly.
  • 29. Assembly Manifest: Uso dei MetadatiI METADATI sono indipendenti dal linguaggio, consentendo a quelli che vi accedonodi interpretarli in modo univoco.Questo permette di creare progetti (assistiti da IDE come Visual Studio) checontengono al loro interno codice sorgente di linguaggi diversi, proprio grazie alleinformazioni contenute nei metadati dei componenti;Il compilatore genera i metadati di un componente dal codice sorgente chevengono 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 assemblies 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 Assembly Source Language Code (.exe o .dll) Code Compiler MSIL Metadata Compilazione Just in Time Esecuzione Native JIT Code Compiler
  • 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. Garbage Collection (1) 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 Fase 1: Mark NextObjPtr Root set Oggetti “vivi” Oggetti non raggiungibili Spazio libero
  • 36. Garbage Collection (2) Fase 2: Compact Spazio recuperato NextObjPtr Root set Oggetti “vivi” Spazio libero www.devleap.it
  • 37. Garbage Collection (3) Non sempre ci si può affidare in modo “cieco” al Garbage Collecto, 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.
  • 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 Web Services User Interface Data Xml Base Class Library
  • 41. Base Class ed Interfacce: Namespaces System.Web System.Windows.Forms UI Design ComponentModel Compilation HtmlControls Handlers WebControls Hosting System.Drawing Caching Security Drawing2D Printing Configuration SessionState Imaging Text System.Data System.Xml OleDb Common Xsl Schema SqlClient SQLTypes XPath Serialization System Collections IO Security Runtime Configuration Reflection Text InteropServices Diagnostics Resources Threading Remoting Globalization Serialization
  • 42. Base Class ed Interfacce: Namespaces System Collections Security Configuration Text Diagnostics Threading Globalization Runtime IO InteropServices Reflection Remoting Resources Serialization
  • 43. Base Class ed Interfacce: Namespaces System.Data Common OleDb Sql/SqlClient OracleClient Odbc Messaging System.Xml Schema Xsl Serialization XPath
  • 44. dotNet Framework: Namespaces System.Web Management UI Compilation HtmlControls Handlers WebControls Hosting Caching Security Configuration SessionState Profile Services
  • 45. dotNet Framework: Namespaces System.Windows.Forms Design ComponentModel VisualStyles System.Drawing Drawing Printing Design Text
  • 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 Ottenere informazione su un file FileInfo ourFile = new FileInfo(@"c:boot.ini "); if (ourFile.Exists) { Console.WriteLine("Filename : {0}", ourFile.Name); Console.WriteLine("Path : {0}", ourFile.FullName); } Copiare un file Enumerare I file in una directory FileInfo ourFile = new DirectoryInfo ourDir = new FileInfo(@"c:boot.ini"); DirectoryInfo(@"c:windows"); ourFile.CopyTo(@"c:boot.bak"); Console.WriteLine("Directory: {0}", ourDir.FullName); foreach (FileInfo file in ourDir.GetFiles()) { Console.WriteLine("File: {0}", file.Name); }
  • 49. dotNet Framework: Gli Stream 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
  • 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. Aprire un file per la lettura File.Open(@"C:boot.ini", FileMode.Open, FileAccess.Read); Creare un file File.Create(@"c:somefile.txt");
  • 51. dotNet Framework: Leggere/Scrivere da un file Leggere da 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(); Scrivere su file 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");
  • 52. dotNet Framework: Usare il MemoryStream Scrittura su uno Stream in Memoria e successivo riversamento su file 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();
  • 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. dotNet Framework: Comprimere/Decomprimere con gli stream 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(); } Compressione 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(); 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: ^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$ • 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: 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. dotNet Framework: Collection Classiche 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 interfaccie è possibile operare uniformemente sulle varie Collection Classiche, attraverso i classici metodi: insert, remove, add, e così via. 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. dotNet Framework: Generics class Stack<T>  I generics sono stati introdotti { dalla versione 2 del framework private T[] store; private int size; e sono formati da classi e metodi che lavorano in modo public Stack() { uniforme su tipi differenti store = new T[10];  Benefici size = 0; }  Le variabili sono di un tipo ben preciso e non Object public void Push(T x) {  Non è necessario un cast // push code goes here } (errori in fase di compilazione) public T Pop() {  Riutilizzo reale del codice return store[--size]; } } void Add(Stack<int> s) { int x = s.Pop(); int y = s.Pop(); s.Push(x+y); }
  • 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. dotNet Framework: Generics vs. Collection Classiche Non-generic 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 Generic 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
  • 67. dotNet Framework: Generics vs. Collection Classiche Rappresentazione senza Generics Rappresentazione con Generics object int int object int int object int int object int object int Box Unbox int int int int Push Pop Push Pop
  • 68. dotNet Framework: Serialization Persistere lo Stato di un Oggetto
  • 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 Frameworok 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 { <Serializable()> Class ShoppingCartItem public int productId; Public productId As Integer public decimal price; Public price As Decimal … … … C# … VB 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(); Serializzazione 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(); 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(); Serializzazione // 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(); 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); } }
  • 75. dotNet Framework: Graphics Grafica elementare
  • 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(); Graphics g = this.CreateGraphics(); Pen p = new Pen(Color.Blue, 3); // Create a pen object with which to draw g.DrawPie(p, 1, 1, 100, 100, -30, 60); Pen p = new Pen(Color.Red, 7); // Draw the line g.DrawLine(p, 1, 1, 100, 100);
  • 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; Bitmap bm = new Bitmap(600, 600); Graphics g = Graphics.FromImage(bm); Se si vuole creare e salvare una Brush b = new LinearGradientBrush(new Point(1, 1), new Point(600, 600), Color.White,Color.Red); nuova immagine si lavoro pressappoco Point[] points = new Point[] come negli esempi precedenti, {new Point(10, 10), eccetto che la creazione new Point(77, 500), new Point(590, 100), dell‟oggetto graphics avviene new Point(250, 590), attraverso un metodo apposito: 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: static void SimpleWork() { Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId); } 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
  • 83. dotNet Framework: Threading In sintesi ecco il codice per avviare un thread: ThreadStart operation = new ThreadStart(SimpleWork); ThreadStart operation = new ThreadStart(SimpleWork); for (int x = 1; x <= 5; ++x) // Creates, but does not start, a new thread { Thread theThread = new Thread(operation); Thread theThread = new Thread(operation); theThread.Start(); } // Starts the work on a new thread theThread.Start(); Se è necessario passare informazioni al thread è possibile utilizzare il delegate ParameterizedThreadStart: static void WorkWithParameter(object o) ParameterizedThreadStart operation = { new ParameterizedThreadStart(WorkWithParameter); string info = (string) o; for (int x = 0; x < 10; ++x) Thread theThread = new Thread(operation); { Console.WriteLine("{0}: {1}", info, theThread.Start("Hello"); Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); 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: static void UpdateCount() { for (int x = 1; x <= 10000; ++x) { Interlocked.Increment(ref Counter.Count); } } la classe interlocked “blinda” il parametro su cui opera, rendendo le operazioni atomiche
  • 87. dotNet Framework: Threading Se abbiamo bisogno di “blindare” più istruzioni in modo da renderle atomiche, possiamo ricorre al lock: public void UpdateCount() { lock (this) { count = _count + 1; if (Count % 2 == 0) evenCount = evenCount + 1; } } 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
  • 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); // Dont 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 isnt complete Thread.Sleep(100); } // Finished, so we can call EndRead and it will return without blocking int numBytes = strm.EndRead(result); ….
  • 91. dotNet Framework: Threading …. // 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); // Dont forget to close the stream strm.Close(); Console.WriteLine("Read {0} Bytes", numBytes); Console.WriteLine(BitConverter.ToString(buffer)); }
  • 92. dotNet Framework: Application Domain & Serices Isolare il Codice Sconosciuto
  • 93. dotNet Framework: Application Domain & Serices 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. Processo AppDomain AppDomain Assembly Assembly Shared 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. dotNet Framework: Application Domain & Serices La classe che permette la gestione degli Application Domain è la System.AppDomain. Vediamo ora alcuni esempi concreti di utilizzo degli Application Domain: Creazione AppDomain d = AppDomain.CreateDomain("NewDomain"); Caricamento di un Assembly AppDomain d = AppDomain.CreateDomain("NewDomain"); d.ExecuteAssemblyByName("Assembly"); Unload di un Assembly AppDomain d = AppDomain.CreateDomain("NewDomain"); ….. AppDomain.Unload(d) 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. dotNet Framework: Application Domain & Serices 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: 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. dotNet Framework: Application Domain & Serices 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: // 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) Vediamo ora come recuperare i valori appSettings salvati nel file di Configurazione: // 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))
  • 101. dotNet Framework: Configuring Application La sezione ConnectionStrings è leggermente diversa poiché può contenere dichiarazioni multiple: <?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> possiamo sia recuperare la prima connectionString: 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);
  • 105. dotNet Framework: Instrumentation Ottimizzare la propria Applicazione
  • 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 ReadEventLog() public static void CreateEventLog() { { EventLog DemoLog = new EventLog(); EventLog DemoLog = new DemoLog.Log = "Chap10Demo"; EventLog("Chap10Demo"); foreach (EventLogEntry DemoEntry in DemoLog.Source = "Chap10Demo"; DemoLog.Entries) { DemoLog.WriteEntry("CreateEventLog Console.WriteLine(DemoEntry.Source + called", EventLogEntryType.Information); ":" + 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: 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"]); 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.
  • 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 dellassembly •Firma digitale (Authenticode, stessa degli Activex) Al caricamento dellassembly in memoria, viene subito ricavata levidence. È 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. CAS Autenticazione Codice Evidence Autorizzazione Permission Esecuzione
  • 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). Di default esistono 5 Code Groups: Permissi on Sets Code Group Evidence Permission Set Assembly My_Computer_Zone Zone: My FullTrust Code (assegnati al Computer Grups gruppo attraverso l’evidence) LocalIntranet_Zone Zone: Local LocalIntranet Intranet Ovviamente un assemby può Internet_Zone Zone: Internet Internet appartenere a più Code Grups. In tal Restricted_Zone Zone: Untrusted Nothing caso i permessi sono Il risultato sites dell’unione dei permessi dei singoli gruppi. Trusted_Zone Zone: Trusted Internet sites
  • 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. dotNet Framework: Application Security Al runtime viene fatto il calcolo dei permessi effettivi: 1. Unione 2. Intersezione void SetAppDomainPolicy(PolicyLevel domainPolicy);
  • 124. dotNet Framework: Application Security CAS può essere configurato sia tramite riga di comando, attraverso l‟utility CasPol, sia attraverso il Framework Configuration Toolkit Security Policy Code Groups Permission Set 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. dotNet Framework: Application Security 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 PermissionSet ISecurityEncodable IStackWalk CodeAccessPermission IPermission FileIOPermission UIPermission EnvironmentPermssion FileIOPermission UIPermission EnvironmentPermssion Attribute Attribute Attribute CodeAccessPermission Attribute
  • 127. 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 lassembly altrimenti lassembly non viene caricato. • RequestOptional. Permessi aggiuntivi rispetto al minimo indispensabile. Vengono dati allassembly 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 allassembly solo quei permessi anche se le policy sono più alte. Il tool PermView permette di vedere nellassembly i requisiti di security
  • 128. 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: Metodo1 • Demand • disponibile anche come attributo permission • verifica il permesso richiesto eseguendo lo negata Metodo2 SecurityException stack walk Stack walk • notare che Metodo4 non è soggetto ad alcun Metodo3 OK controllo. Il controllo è sui chiamanti Stack walk • LinkDemand Metodo4 Demand • solo come attributo • verifica il permesso richiesto solo sul chiamante senza eseguire stack walk • il controllo viene eseguito dal jitter // Richiesta imperativa ... FileIOPermission perm = new FileIOPermission( • InheritanceDemand FileIOPermissionAccess.AllAccess, @"c:"); • solo come attributo perm.Demand(); • verifica il permesso richiesto sulle classi che derivano da quella su cui è applicato lattributo // ... oppure dichiarativa (classe/metodo) • il controllo viene eseguito al load [assembly:FileIOPermission( SecurityAction.Demand, All = @"C:") ]
  • 129. dotNet Framework: Application Security • Assert e RevertAssert Metodo1 • Impediscono che la richiesta di permesso si propaghi ai chiamanti permission • Assert si chiama al più tardi e Metodo2 negata RevertAssert al più presto • Utile per evitare perdite di performance dovute al controllo della CAS Metodo3 Assert OK Stack walk Metodo4 Demand • 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 sullassembly chiamato
  • 130. dotNet Framework: Application Security Vediamo due esempi di una stessa restrizione attraverso l‟uso dichiarativo e imperativo: [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() { Dichiarativo 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 } } 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 dellapplicazione • Identità dellutente 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 luso 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 dellattributo: [assembly:AllowPartiallyTrustedCallers]
  • 133. dotNet Framework: Interoperation Interoperare con altre tecnologie
  • 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: tlbimp <dllname>.dll il risultato sarà una libreria .dll con lo stesso nome dell‟originale, dareferenziare nel nostro progetto dotNet.
  • 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 Gestione Eccezioni COM “2.0” 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 its 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. }}
  • 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 namespace NetForComDemoCS Private _firstName As String { Attraverso appositi attributi è Private _lastName As String poi possibile indicare quali class ComVisiblePerson Public Property FirstName() As String { Assembly e Metodi devono Get private String firstName; essere visibili o meno come Return Me._firstName private String lastName; COM: End Get Set(ByVal value As String) public String FirstName VB Me._firstName = value <Assembly: ComVisible(False)> { End Set // C# End Property get { return firstName; } [assembly: ComVisible(false)] set { firstName = value; } Public Property LastName() As String } Get public String LastName VB Return Me._lastName { <ComVisible(False)> _ End Get Public Class ComVisiblePerson get { return lastName; } Set(ByVal value As String) // C# Me._lastName = value set { lastName = value; } [ComVisible(false)] End Set } class ComVisiblePerson End Property } End Class }
  • 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 using System.Runtime.InteropServices; Imports System.Runtime.InteropServices namespace OptionalCS { Public Class WindowExample class WindowExample Private Const BufferSize As Int32 = 256 { <DllImport("user32.dll")> _ private const Int32 BufferSize = 256; Private Shared Function GetForegroundWindow() [DllImport("user32.dll")] As IntPtr private static extern IntPtr End Function GetForegroundWindow(); <DllImport("user32.dll")> _ [DllImport("user32.dll")] Private Shared Function GetWindowText(ByVal _ private static extern Int32 GetWindowText(IntPtr hWnd As IntPtr, ByVal textValue As StringBuilder, hWnd, StringBuilder textValue, Int32 counter); ByVal counter As Int32) As Int32 …. End Function …
  • 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.
  • 145. dotNet Framework: Reflection Scoprire il codice
  • 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: 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: VB tbSalary.Text = Format(100000, "Currency") // C# tbSalary.Text = (100000).ToString("C");
  • 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 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: VB Il risultato della Dim FirstString = "Coté" Dim SecondString = "coté" comparazione può essere Dim DemoInfo As CompareInfo = New CultureInfo("fr-FR").CompareInfo modificato passando alla DemoInfo.Compare(FirstString, SecondString) compare,come terzo parametro, uno dei valori // C# di CompareOptions String FirstString = "Coté"; String SecondString = "coté"; CompareInfo DemoInfo = new CultureInfo("fr-FR").CompareInfo; DemoInfo.Compare(FirstString, SecondString);
  • 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);
  • 155. dotNet Framework: Conclusioni Il dotNET framework è una infrastruttura contenente sterminate funzionalità in tutti gli ambiti attualmente richiesti per lo sviluppo di applicazioni potenti ed efficienti. Tutti gli strumenti presenti sono volti a semplificare l‟attività del developer consentondogli di concentrare le proprie energie sulle tematiche di Business Solution. Il consiglio è quello conoscere ogni aspetto di .NET ed approfondirlo man mano che si presenta la necessità (occasione) di applicarlo concretamente. E‟ importante ricordare comunque che .NET e Visual Studio, così come tutti gli altri ambienti/linguaggi, sono solo strumenti e che la qualità di un applicativo dipende solo dalla professionalità e dalle intuizioni del Team di Sviluppo.