EURO RIVISTA Effi I CORSI DA ZERO PER IMPARARE: VISUAL BASIC, C++, C#, VB.NET, JAVA E JSP RIVISTA+SUPPLEMENTO "PROGRAMMARE IL WEB"+2CD € 7,70 Periodicità mensile • FEBBRAIO 2003 • ANNO Vili, N.2 (66) Poste Italiane • Spedizione in a.p. - 45% • art. 2 comma 20/b legge 662/96 - AUT. N. DCDC/033/01/CS/CAL ROGRAMMO Grafica Digitale e Visual Basic Sviluppiamo una completa applicazione di fotoritocco XML & FLASH Impariamo a integrare le tecnologie che hanno rivoluzionato il Web WinZIP fai da te! Le tecniche di compressione utilizzate dalle librerie Open Sourc SISTEMA Impronte digitali: realizziamo un'applicazione VB per il riconoscimento e la catalogazione Tecniche per ottimizzare le applicazioni Delphi ELETTRONICA • Lego Mindstorms: un laboratorio di robotica sulla tua scrivania • Moduli Rabbit: elettrodomestici, luci, allarmi, telecamere, ora li controlli dal Web! • Realizziamo un software per il controllo dei motori passo passo ISSN 1128-594X 30066 Una COLOBI nsp.nrr.8 ADVANCED Reti locali, cablate, wireless: analizziamone il comportamento con Network Simulator 9 '771128 594009 è una pubblicazione SIR jg^ HACKER Scovare le password di alcuni router ADSL MASTER INCORSO CROBOTS: I RISULTATI DEL CONCORSO 2002 Contents EOGRAMMO Grafica Digitale e Visual Basic Anno VII - n. 2 (66) Febbraio 2003 ► ► ► ► Povere le mie orecchie!? Sovente le mie orecchie hanno dovuto ascoltare frasi campate in aria, elucubrazioni mentali e disquisizioni su nuovi fantomatici dispositivi che cambieranno il modo di vivere: TV virtuali che si materializzano al solo pensiero, ascensori spaziali in grado di catapultarci sulla luna, auto volanti e chi più ne ha, più ne metta. Questo nuovo anno porta con se nuovi "pettegolezzi": orologi da polso e magneti da attaccare al frigorifero, sistemi in grado di integrare le comuni operazioni quotidiane, e l'individuo stesso, all'interno della grande Rete. Fantascienza o realtà? Sara forse questa, una delle tante altre novelle che le mie povere orecchie dovranno ancora una volta sopportare? I dubbi sono tanti ma, forse questa volta, ci sarà da scommet- tere qualcosa, soprattutto se a prospet- tare il progetto è lui, l'innominabile: Bill Gates: "Da qui a breve ogni dispositivo, piccolo o grande che esso sia, sarà dotato del nuovo .NET Compact Framework, tutto e tutti saranno in grado di interfacciarsi alla Rete'. Frasi che non restano nella fervida immaginazione del buon zio Bill ma che trovano raffronto al Consumer Electronics Show, dove, per la prima volta, sono stati presentati i primi dispositivi che utilizzeranno la nuova tecnologia. Fossil, Citizen e Suunto, produttori d'orologi da polso, prevedono di mettere in commercio modelli predisposti ad accogliere SPOT (questo il nome del progetto) entro la fine dell'anno. Gli orologi si colle- gheranno al PC per compiere una serie d'operazioni: regolazione automatica dell'ora, download di software e collegamento wire- less a dati streaming trasmessi mediante segnali radio FM, per ricevere informazioni di qualunque genere. Per non parlare delle calamite per il frigorifero, veri gioiellini che ricevono informazio- ni wireless sul traffico locale o sulle specialità dei ristoranti della zona. Scommettiamo che a breve sarà possibile, semplicemente parlando al proprio orologio, scongelare la cena e azionare il forno a micronde? Scommetto le mie orecchie! Gianfranco Forlino (gforlino@edmaster.it) News 6 Soluzioni 10 ► Frasi anagrammate Teoria & Tecnica 14 XML e Flash: l'utile ed il dilettevole si incontrano Password a "portata di mano" Librerie di compressione Open Source Fotolab: filtri fotografici in Visual Basic Animazione in Java 3D (3 a parte) Biblioteca 14 21 26 31 37 41 Tips&Tricks 42 C Robot 45 ► Il Torneo di Crobots 2k2 Sistema 47 ► Ottimizzare programmi Delphi Lego 52 ► LEGO Mindstorms Robotics Invention System 2.0 Elettronica 55 ► ► Un Software di controllo per motori Passo-Passo Un server WEB nel taschino della giacca Exploit 55 60 64 ► Non c'è password che tenga! I corsi di ioProgrammo 66 JSP • Procedure memorizzate con JDBC 66 VB .Net • Le finestre di dialogo 70 C# • Passaggio di argomenti, tre casi particolari 74 C++ • Risoluzione di ambiguità ed ereditarietà multipla 78 Java • Java Media Framework: strumenti di acquisizione audio e video 82 VB • Come realizzare un FTP Client 87 JoR OJUto Multimedia 91 ► 3DSM • Modellazione tramite Surface Advanced Edition 97 ► Simulare una rete con Network Simulator ► Testare il codice con JUnit Software sul CD-Rom 97 101 106 FAQ 109 In Box 113 Il Sito del mese 114 OGRAMMO Anno VII - N.ro 2 (66) - Febbraio 2003 - Periodicità: Mensile Reg. Trib. di CS al n.ro 593 - Cod. ISSN 1128-594X E-mail: ioprogrammo@edmaster.it http://www.edmaster.it/ioprogrammo Dir. Editoriale Massimo Sesti Dir. Responsabile Romina Sesti Product Manager Antonio Meduri Editor Gianfranco Forlino Coordinatore Redazionale Raffaele del Monaco Redazione Thomas Zaffino, Antonio Pasqua Collaboratori S. Ascheri, M. Autiero, R. Bandiera, L. Buono, P. Canini, E. Cobisi, F. Grimaldi, M. Del Gobbo, E. Florio, A. Marroccelli, S. Meschini, G. Palumbo, A. Panella, A. Pelleriti, C Pelliccia, P. Perrotta, S. Serra, L. Spuntoni, G. Uboldi, F. Vaccaro. Per l'inserto hanno collaborato M. Battista, A. Cangiano, P. Capitani, C Giustozzi, F. Mestrone, G. Uboldi Segreteria di Redazione Veronica Longo REALIZZAZIONE GRAFICA CROMATIKA Sri Cda Lecco, zona ind. - 87030 Rende (CS) Tel. 0984 8319 - Fax 0984 8319225 Coord. grafico: Paolo Cristiano Coord. tecnico: Giancarlo Sicilia Impaginazione elettronica: Aurelio Monaco PUBBLICITÀ Edizioni Master S.r.l. Responsabile Vendite Ernesto Redaelli Agenti Vendita Elisabetta Februo, Serenella Scarpa, Cornelio Morari Segreteria Ufficio Vendite Daisy Zonato Via Cesare Correnti, 1 - 20123 Milano Tel. 02 8321612 - Fax 02 8321764 e-mail: advertising@edmaster.it EDITORE Edizioni Master S.r.l. Sede di Milano: Via Cesare Correnti, 1 - 20123 Milano Tel. 02 8321482 - Fax 02 8321699 Sede di Cosenza: Cda Lecco, zona ind. - 87030 Rende (CS) Amministratore Unico: Massimo Sesti Responsabile Amministraz. e Finanza: Benedetto Celsa Produzione e Logistica: Michele Carere Diffusione: Alessandra Cervello Marketing: Giuseppina Bruno, Leonardo Petrone, Antonio Meduri ABBONAMENTO E ARRETRATI Costo abbonamento annuale (11 numeri): Italia € 84,70 Promo- zione Sconto 30% € 59,00 Costo abbonamento annuale (11 numeri): estero € 169,40 Costo arretrati (a copia): il doppio del prezzo di copertina + € 5,16 spese (spedizione con corriere). Verificare la disponibilità delle copie arretrate allo 028321482. La richiesta contenente i Vs. dati anagrafici e il nome della rivista, dovrà essere inviata via fax allo 028321699, oppure via posta a EDIZIONI MASTER via Cesare Correnti, 1 - 20123 Milano, dopo avere effettuato il pagamento, secondo le modalità di seguito elencate: - cc/p n. 16821878 o vaglia postale (inviando copia della rice- vuta del versamento insieme alla richiesta); - assegno bancario non trasferibile (da inviarsi in busta chiusa insieme alla richiesta); - carta di credito, circuito visa, cartasi; mastercard/ eurocard, (inviando la Vs. autorizzazione, il numero della carta, la data di scadenza e la Vs. sottoscrizione insieme alla richiesta). SI PREGA DI UTILIZZARE IL MODULO RICHIESTA ABBONAMENTO POSTO nelle pagine interne della rivista. L'abbonamento verrà attiva- to sul primo numero utile, successivo alla data della richiesta. Sostituzioni: Inviare il CD-Rom difettoso in busta chiusa a: Edizioni Master Servizio Clienti - Via Cesari Correnti, 1 20123 Milano Assistenza tecnica: ioprogrammo@edmaster.it ^ Servizio Abbonati: ®tel.02 8321482 @ e-mail: servizioabbonati@edmaster.it Stampa: Elcograf Industria Grafica (LC) Stampa CD-Rom: Disctronics Italia (MI) Distribuzione per l'Italia: Parrini & C S.p.A. - Roma Finito di stampare nel mese di Gennaio 2003 Nessuna parte della rivista può essere in alcun modo riprodotta senza autorizzazione scritta della Edizioni Master. Manoscritti e foto originali, anche se non pubblicati, non si restituiscono. Edizioni Master non si assume alcuna re- sponsabilità per eventuali errori od omissioni di qualunque tipo. Nomi e marchi protetti sono citati senza indicare i relativi brevetti. Edizioni Master non sarà in alcun caso responsabile per i danni diretti e/o indiretti derivanti dall'u- tilizzo dei programmi contenuti nel CD-Rom e/o per even- tuali anomalie degli stessi. Nessuna responsabilità è, inol- tre, assunta dalla Edizioni Master per danni o altro derivanti da virus informatici non riconosciuti dagli antivirus ufficiali all'atto della masterizzazione del supporto. sili A.N.E.S. L'Universo Tecnologico MASTER % «™™ Edizioni Master edita: Idea Web, GolOnLine Internet Magazine, Win Magazine, Quale Compu- ter, DVD Magazine, Office Magazine, ioProgrammo, Linux Magazine, Softline Software World, MPC, Discovery DVD, Computer Games Gold, inDVD, I Fantastici CD-Rom, PC VideoGuide, Il CD-Rom di Idea Web, I Corsi di Win Magazine, Le Collection. w News Bill Gates presenta OneNote, la nuova applicazione per prendere appunti in modo più produttivo OneNote unisce l'efficienza tipica dei contenuti digitali alla possibilità di prendere appunti in modo flessibile In occasione del COMDEX 2002, Bill Gates, Chairman and Chief Software Architect di Microsoft Corp., ha annun- ciato la prossima disponibilità di Micro- soft One Note, un'applicazione che per- mette agli utenti di prendere appunti, or- ganizzarli e utilizzarli in modo più pro- duttivo. OneNote, sul mercato a metà del 2003, consentirà di prendere appunti su un desktop o su un computer portati- le, mentre gli utenti di Tablet PC potran- no acquisire anche note scritte a mano, immagini e diagrammi. "OneNote com- bina la flessibilità di un taccuino cartaceo con l'efficienza tipica del contenuto digi- tale, mantenendo lo stile con cui ciascu- no di noi fissa sulla carta i propri pensie- ri e organizza gli appunti", ha dichiarato Jeff Raikes, Group Vice President of Pro- ductivity and Business Services di Micro- soft. "Con la creazione di nuove applica- zioni come OneNote, Microsoft è impe- gnata nel mantenere la suite Office sem- pre aggiornata e al passo con i tempi, e nel migliorare la produttività degli infor- ma tion worker." Da un recente studio condotto da Microsoft Research, risulta che il 91 % degli informa tion worker - tutti coloro che nello svolgimento della propria attività professionale gestiscono T informazione attraverso il PC e i dispo- sitivi digitali - intervistati prende rego- larmente appunti, il 26% li trasferisce in messaggi di posta elettronica, mentre il 23% ha ammesso di non riuscire sempre a ritrovare le informazioni desiderate. Inoltre, il 36% ha dichiarato di essere pronto ad adottare un nuovo sistema di appunti. VeriSign annuncia un toolkit per la sicurezza nei WebServices Una implementazione Open Source di un interessante tool che consentirà agli sviluppatori di gestire problematiche come firma digitale e cifratura VeriSign ha introdotto una implemen- tazione completamente gratuita e Open Source di un toolkit rivolto agli svi- luppatori che semplifica Y integrazione della firma digitale e della cifratura air interno di Web Services. VeriSign afferma che l'implementazione sarà distribuita attraverso delle specifiche librerie open source che daranno agli svi- luppatori la possibilità di costruire Web Service "sicuri" che utilizzino lo standard WS-Security. Il Trust Service Integration Kit è un tool Java-based che permette di gestire XML Signature, XML Encryption ed altre funzioni relative alla sicurezza. Il TSIK è composto da tre componenti fon- damentali: il Messaging Framework, il Trust Layer ed alcune risorse specifiche per XML. • Il Messaging Framework può essere uti- lizzato per specificare firma e chiave di cifratura per garantire autentica- zione, integrità dei dati e riservatezza delle comunicazioni. • Il Trust Laywe fornisce delle API per la sicurezza dei messaggi XML attraver- so una infrastruttura a chiave pubbli- ca (PKI), e include le implementazio- ne delle specifiche W3C per la firma digitale e la cifratura XML. • Le risorse per XML permettono di manipolare direttamente file XML, navigare nella struttura dei documen- ti, validare gli schema oltre a sempli- ficare T interfacciamento con i parser. www.verisign.com IBM pronta a pubblicare il codice della nuova tecnologia di Storaging Con lo scopo di attirare nuovi sviluppatori, IBM sta approntando una versione Open Source di Storage Tank I ricercatori di IBM sono al lavoro per proporre una versione Open Source di una delle più attese release del 2003: Storage Tank. Una tecnologia sviluppata per meglio gestire i sistemi di storaging esistenti e facilitare la cooperazione fra piattaforma diverse. Il modello di upgra- de proposto da IBM si può definire dun- que rivoluzionario: Storage Tank consen- te di integrare vecchi e nuovi sistemi, il tutto a vantaggio dei costi e della prati- cità. Storage Tank è una sorta di file system virtuale che si basa sul concetto di metadata, attraverso cui gestisce posi- zione, dimensione e permessi associati ai file immagazzinati. La caratteristica più interessante che Storage Tank offre consi- ste nella possibilità di far interagire siste- mi assolutamente eterogenei: gli stessi file saranno accessibili da sistemi opera- tivi differenti e potranno essere salvati su Windows, AIX e Linux in modo del tutto trasparente agli utenti. L' eterogeneità non si ferma ai server e ai sistemi opera- tivi, ma arriva al supporto per dischi e nastri (fondamentale per preservare i sistemi già esistenti) di qualsiasi marca e modello. Storage Tank sarà dunque capace di scendere a livello dei singoli blocchi che compongono un file, identifi- carne il formato e tradurlo in modo da renderlo compatibile al dispositivo che 10 aveva richiesto. www.ibm.com/it/ Il PocketPC ci fa le multe 11 Palmare sta cambiando in meglio il nostro mondo L/ applicazione PocketPM permette agli f agenti di Polizia Municipale di sosti- tuire il vecchio blocchetto delle multe con un innovativo palmare, migliorando l'effi- cienza del loro lavoro e fornendo un migliore servizio al cittadino. 6 ►►► F b b http: //www. itportal.it w Il prodotto software, realizzato da una gio- vane e innovativa società di Cosenza, la NoTangle, informatizza l'intero processo di attività degli agenti di Polizia Municipale, dal rilevamento informatico delle infrazio- ne al Codice della Stradare alla gestione informatizzata del Back Office con un note- vole risparmio di tempo e danaro. U applicazione client, sviluppata per PocketPC utilizzando l'eMbedded Visual C++ e SQLCE 2.0, consente tramite una interfaccia amichevole di registrare i dati dell' infrazione e di scattare una foto attra- verso una camera digitale integrata con il dispositivo. I dati raccolti sono successiva- mente sincronizzati con il server centrale utilizzando una connessione sicura GPRS ed elaborati in modo efficace e veloce. L'applicazione lato server è stata intera- mente sviluppata sfruttando le nuove opportunità offerte dalla piattaforma .NET di Microsoft. FileMaker presenta la gamma educational Al MacWorld EXPO di San Francisco, presentata la gamma di soluzioni database K12 per le scuole Grazie alle numerose e potenti carat- teristiche, ai template per Y insegna- mento pronti per Fuso e alle centinaia di soluzioni per la vasta comunità di esperti sviluppatori, FileMaker Pro 6 rende ancora più economica ed efficiente la gestione e la condivisione delle informa- zioni, offrendo soluzioni concrete a migliaia di scuole nei principali distretti del Nord America. Ili "1 i ì % \ Jump-start your productivity with the leading database software for workgroups. Get Started Free Solutions | Products | About Us FileMaker Il mese scorso FileMaker ha lanciato un nuovo sito web, K-12 Education, che offre soluzioni software gratuite per Y in- segnamento, ultime notizie che riguarda- no le proposte di FileMaker per il mondo delle scuole, aggiornamenti sulle confe- renze e sugli eventi dove gli insegnanti possono assistere a dimostrazione sull'u- so di FileMaker e offerte speciali dei pro- dotti FileMaker destinate agli insegnanti e amministratori nel segmento K-12. www.filemaker.com/education Macromedia Director diventa MX Disponibile la versione MX di Macromedia Director, il più noto software di authoring multimediale La nuova versione di Macromedia Director è stata arricchita di funziona- lità per gli utenti che realizzano prodotti interattivi su CD-ROM destinati alla pub- blicazione su Internet. Director MX si inte- gra perfettamente con tutti gli altri prodotti della gamma MX. Il software permette la creazione di contenuti che possano essere distribuiti dovunque, sia online che offline. Supporta tutti formati audio, video, 3D e grafici più diffusi e permette agli sviluppa- tori di utilizzare stream video senza limiti di durata, garantendo il supporto QuickTime, RealVideo e AVI. Gli sviluppa- tori possono lanciare Flash direttamente da Director MX e quindi di effettuare rapidi cambiamenti dei contenuti o controllare Flash attraverso l'utilizzo di Lingo. Inoltre, il software si integra con le tecnologie ser- ver di Flash, e in particolare con Flash Remoting MX, permettendo di ottenere una connessione ad elevate prestazioni tra ColdFusion MX e il Player Shockwave e con Flash Communication Server MX per la gestione di giochi multi utente, streaming video Flash e collaborazioni in tempo reale. www.macromedia.com/it/software/director/ WebSphere 5 supporta Soap Uscita la quinta release dell'application server di IBM, con funzioni di autodiagnosi e autoriparazione e un miglior supporto ai web service Rilasciato WebSphere Application Server 5.0, con funzioni per "lautonomic com- puting", un supporto potenziato per i web service. Secondo IBM, WebSphere 5.0 costi- tuirà la piattaforma di base per il software "On Demand" di IBM e sarà integrato con il database Db2, le applicazioni Tivoli per la gestione della rete e il software Lotus per la collaborazione, puntando a definire nuovi standard in termini di prestazioni, scalabi- lità, affidabilità, facilità di manutenzione ed interfaccia d'uso. Inoltre gli strumenti di sviluppo, strettamente integrati nelT am- biente, permettono agli sviluppatori di es- sere più veloci ed efficaci nella creazione di applicazioni J2ee". Dal punto di vista com- merciale, è stata aggiunta una nuova linea di prodotti, la cosiddetta Express che per- mette a IBM di estendere la propria offerta agli Isv e alle aziende che hanno bisogno di un ambiente J2ee, ma anche di un paradig- ma di sviluppo per le applicazioni semplifi- cato. I nuovi tool, infatti, rendono più sem- plice lavorare sui web service, "automatiz- zando" il processo di sviluppo, fino a limi- tare al minimo la scrittura di codice. In più, i wizard di WebSphere Studio danno l'op- portunità di ispezionare il codice prima di eseguirlo. WebSphere 5 è anche una buona piattaforma di integrazione perché ha il pieno supporto per J2ee Connection Archi- tecture 1.0. IBM ha aggiunto il supporto per Web Service Invocation Framework, una tecnologia che permette di richiamare e svi- luppare servizi Web su varie reti e protocol- li di trasporto, e Axis 3.0, un nuovo sistema per i web service ad alta velocità in grado di gestire richieste Soap, il più importante protocollo di trasporto per questi servizi. WebSphere 5.0 include anche Ibm Web Ser- vices Gateway per amministrare e rendere sicuri i Web service; un repository privato Uddi (Universal Description, Discovery and Integration)', una tecnologia per il workflow dei Web service basata su Bpel4Ws (Business Process Execution Language) for Web Services. www.ibm.com/it/ Batosta di Sun a Microsoft Per ordine di un giudice, la Java Virtual Machine di Sun dovrà essere inclusa nel sistema operativo di Windows e nel browser Internet Explorer La corte di giustizia del Maryland ha dato ragione alla Sun in una causa intentata contro la Microsoft per violazioni della legge antitrust e del copyright. L'oggetto della causa era l'implementazione di una versione proprietaria del linguaggio Java. Ora Microsoft dovrà distribuire Java http: //www. itportal.it 2 3 ►►► 7 w IBM WebSphere Payment Manager "genuino". Il contenzioso tra le due aziende risale sin dal Marzo 1996, quando Microsoft strinse un accordo con Sun per distribuire prodotti basati sulla tecnologia Java, allora molto in voga. La natura trasversale della piattaforma Java, che non dipende dal sistema operativo, venne giudicata da Microsoft come un serio rischio alla posizione di predominio di Windows. Microsoft tentò quindi di togliere a Sun il controllo di Java, trasformando Java in un modo per scrivere applicazioni per Windows. Ma Sun replicò con una clausola che obbligava i prodotti Java distribuiti da terzi a superare un "core test" di Sun. Microsoft apportò quindi delle modifiche alla Virtual Machine, creandosi una propria versione (MSJVM). La piattaforma Java originale continuò ad essere distribuita soprattutto grazie a Netscape Navigator. Sun iniziò un'azione legale che portò ad un primo successo il 24 Marzo 1998, corte distrettuale della California del Nord. Nella sentenza, si proibiva a Microsoft di utilizzare la dizione "compatibile Java" nella distribuzione di MSJVM. La corte d'appello annullò però la sentenza, pur senza negare l'incompatibilità di MSJVM con Java. Sun si rifiutò di fornire il sorgente aggiornato a Microsoft. MSJVM, distribuito da Microsoft, divenne presto obsoleto rispetto agli sviluppi di Java. MSJVM è più lento, più soggetto a "crash" e con meno funzioni rispetto alle versioni più recenti di Java. Il 23 Gennaio 2001 Microsoft e Sun trovarono un accordo che permise di mettere fine alla controversia legale in California, ma Microsoft decise di lavorare ad un'alternativa a Java, quello che oggi noi conosciamo come .NET framework. Anche in questo caso si tratta di una piattaforma "trasversale" e che utilizza un linguaggio diverso da Java. Il 13 Febbraio 2002 Microsoft cominciò la distribuzione commerciale del primo prodotto contenente il framework .NET, ossia Visual Studio .NET Nello stesso tempo Microsoft smise di fornire MSJVM, che infatti non è incluso in WindowsXP, anche se è poi stato reinserito nel Service Pack 1, con annuncio che comunque nel prossimo futuro sarà abbandonato. Sun reagì affermando che Microsoft stava sfruttando i vantaggi derivanti dalla precedente attività illegale (rispetto alla legge antitrust) con la quale aveva frammentato la piattaforma Java e distrutto i canali di distribuzione di cui Sun si era avvalsa. Sun intraprese quindi un'altra azione legale, richiedendo che Microsoft distribuisse l'ultima versione, fornitale a costo zero, del binario (il sorgente non essendo più fornito a Microsoft già da un po' di tempo) di Java (Java Runtime Environement: JRE) con tutti i pacchetti Windows e con Internet Explorer; che JRE venisse installato ed abilitato automaticamente; che Microsoft rendesse disponibile JRE tramite il sito Windows Update; e che Microsoft annunciasse la prossima disponibilità di nuove versioni di Windows e di Internet Explorer con almeno 120 giorni di anticipo, e non modificasse in alcun modo il codice binario di JRE. La corte di giustizia del Maryland ha dato ragione a Sun e quindi, se la decisione non sarà annullata in appello (Microsoft ha già annunciato che presenterà ricorso) gli utenti Windows dovrebbero presto avere a disposizione JRE tramite il sito di aggiornamento di Windows. Non solo: i prossimi CD di Windows, nonché le distribuzioni di Internet Explorer, conterranno JRE. http://it.sun.com Intel presenta i nuovi compilatori Disponibile la versione 7.0 dei compilatori Intel C+ + e Intel Fortran per Windows e Linux I nuovi compilatori Intel permettono di sviluppare applicazioni che possano avvalersi della tecnologia Hyper-Threading di Intel, grazie alla quale si possono eseguire più attività contemporaneamente. I compilatori sono stati ottimizzati per i processori Intel Itanium 2, Intel Pentium 4 e Intel Xeon, e consentono di realizzare applicazioni compilate in modo più efficace per velocizzare le operazioni. Tra le varie applicazioni che potranno beneficiare dell'ottimizzazione, i programmi commerciali orientati alle transazioni, le applicazioni finanziarie ad elaborazione intensiva e quelle tecniche e scientifiche, i programmi multimediali digitali, i videogame e gli effetti speciali. I nuovi compilatori supportano funzioni di Compaq Visual Fortran, tra cui la compatibilità a livello di riga di comando, e prevedono l'integrazione con Microsoft Visual Studio. La versione di Linux prevede la compatibilità a livello di GNU con C++ con l'adozione dell'interfaccia binaria delle applicazioni C++. I nuovi compilatori Intel versione 7.0 comprendono inoltre un'opzione di parallelizzazione automatica, tramite la quale, nelle applicazioni, viene automaticamente ricercata la possibilità di creare più thread esecutivi e miglioramenti per il supporto OpenMP, standard di settore che consente l'uso di direttive di alto livello per semplificare la creazione e la gestione di software multithreaded. I compilatori Intel C++ versione 7.0 per Windows e Linux sono già disponibili al prezzo di listino consigliato di 399 dollari USA ciascuno. I compilatori Intel Fortran versione 7.0 per Windows e Linux sono altresì disponibili al prezzo di listino consigliato di 499 e 699 dollari rispettivamente. I compilatori possono essere acquistati tramite download presso Intel e i rivenditori di tutto il mondo, e saranno presto disponibili anche su CD-ROM. www.intel.com/software/products/ 8 ►►► F e b b http: //www. itportal.it w Allarme BSA: giovani pirati in aumento La pirateria informatica tra i ragazzi europei allarma la Business Software Alliance La Business Software Alliance ha espres- so forte preoccupazione per il numero crescente di casi di pirateria software su In- ternet in Europa che vedono coinvolti i mi- nori. Tre casi sottoposti recentemente dalle polizie locali all'attenzione dell' European Internet Investigation Team della BSA, il gruppo di investigatori dedicato alla ricerca di siti internet illegali, hanno visto infatti la partecipazione di minori ad attività com- merciali illegali. In tutte e tre le occasioni le autorità hanno intrapreso opportune azioni, mentre altri casi simili sono attualmente al vaglio degli inquirenti. Nel Regno Unito uno studente sedicenne è stato incriminato per aver venduto software pirata su siti d'aste online, mentre in Svizzera un minore è stato arrestato per aver pubblicizzato e venduto software pirata su un sito Web spe- cializzato in piccoli annunci. Sempre in questo paese, uno studente minorenne è stato incriminato perché vendeva CD conte- nenti software "pirata". In tutti i casi la ma- gistratura ha imposto severe pene pecunia- rie alle quali, in una circostanza, si sono ag- giunte anche restrizioni sull'uso futuro di Internet. Studi recenti dimostrano che alcu- ni genitori giudicano innocuo questo tipo di attività e tendono a chiudere un occhio con il risultato che i minori vengono incriminati e i genitori sono costretti a pagare multe ele- vate. Organizzazioni come la BSA compio- no ogni sforzo per spiegare ai giovani i ri- schi insiti in queste attività, ma senza l'atti- va collaborazione dei genitori si tratta spes- so di sforzi vani. Il Tablet PC diventa un bloc-notes evoluto Alias Wavefront annuncia uno strumento software intuitivo per trasformare il Tablet PC in un bloc notes digitale Alias SketchBook Pro permetterà agli utenti di cambiare i pennelli elettro- nicamente, di fare e rifare velocemente i propri lavori, di manipolare immagini » g i.^M,jÌ ?l| Make your mark. I £'.-■ la ..--t Ci- *~ Si osservi la struttura gerarchica descritta dal docu- mento appena esposto: un tag radice, menu, raggrup- pa T intero insieme di dati. Con ordine, caratteristica fondamentale per la buona riuscita di qualsiasi siste- ma software, vengono raggruppate tutte le voci di me- nu del primo livello sotto il tag elementi, appartenente al tag radice. Al di sotto di elementi, vengono esposti i tag voce, che incapsulando gli attributi testo e uri dan- no vita alle voci di menu di primo livello. Gli elemen- ti voce, come è possibile osservare chiaramente nell'e- sempio proposto, possono contenere essi stessi il tag elementi, che a loro volta possono contenere tag di tipo voce, così da permettere, come si annunciava, la possi- bilità di creare menu a livelli multipli. Nel nostro fine applicativo, continuiamo lo sviluppo del menu e diamo nome al file appena creato Me- nu, xml) una volta salvato il file, lo rendiamo disponibi- le per la lettura da parte di Flash, salvandolo nella stes- sa cartella dove risiederà l'swf che andremo a costrui- re nel resto dell'articolo. Si tenga presente che Flash, se utilizzato all'interno di una pagina html sotto forma di plugin, consente di default l'apertura di file XML che sono residenti solo all'interno dello stesso sottodomi- nio dell' swf caricato: nella maggioranza dei casi ciò si- gnifica che il plugin di Flash non consente di caricare dati da un sito web che non sia quello da cui viene lan- ciato l'swf. A tale limitazione, studiata in effetti per non essere tale ma per offrire un livello di sicurezza maggiore agli utenti del plugin, va incontro anche la funzione LoadVariablesO, così come il nuovo oggetto LoadVars di FlashMx, che altro non è che un wrapper di quest'ultima. li Master" url="http://ivww. edmaster.it" /> testo="Prodotti" url="http://wwiM.edmaster.it/?job = prodotti" /> e testo="ioProgrammo" url="http://wvjvj.edmaster.it/?job = prodotti8!Ìd = 4" /> e testo="Internet Magazine" url="http://wwvj.edmaster.it/?job = prodotti8:id = 3" /> iw.macromedia.c .macromedia.coi (.vj3c.org" /> Fig. 1: Il menu XML appena creato, aperto tramite Internet Explorer. Non soffrono di questo limite, invece, gli swf non in- globati in pagine html ma aperti in maniera diretta tra- mite il player apposito (solitamente utilizzato dai soli sviluppatori Flash). L'INTERFACCIA Come già anticipato, la porzione del nostro applicati- vo che opererà con l'utente utilizzerà unicamente l'in- terfaccia Flash 5 (e l'html necessario per istanziare il plugin swf). Le operazioni che affidiamo a questo mo- dulo sono a livello logico abbastanza semplici; sostan- zialmente utilizzeremo actionScript per caricare la struttura XML appena creata e per visualizzare a video il nostro menu, utilizzando quest'ultima come direttri- ce. Per chiarezza, dividiamo il codice che andremo a sviluppare in tre step: acquisizione dei dati, elabora- zione dei dati e visualizzazione a video. Creiamo quin- di il movie con cui lavoreremo nel resto dell'applica- zione di esempio. Non sono importanti qui le impo- stazioni grafiche, lascio al lettore il piacere di procede- re verso questa direzione come meglio crede. Il codice actionScript che andremo a creare nel resto dell'appli- cazione risiederà pertanto per semplicità nel primo e unico frame del nuovo movie. Con la convinzione che effettuare una buona progettazione ora servirà a facili- tarci le cose in un secondo momento, procediamo a scendere nel dettaglio dell'implementazione degli step di cui si accennava poche righe fa. Non nascondo che la difficoltà si abbatte drasticamente se la base dati è ben predisposta: fortunatamente quest'ultima è stata creata da zero ed ho preferito dare ad essa una strut- tura chiara e facile. Di conseguenza i primi due step - acquisizione dei dati e elaborazione dei dati - non ci creeranno troppi grattacapi. L'acquisizione dei dati av- viene in maniera pressoché automatica; eccone il codi- ce: 1 var xmlMenu = new XML(); XML e Flash XML e Flash L'utile ed il dilettevole si incontrano Dom yift L'insieme di og- —^ getti software (Do- cument Object Model o DOM in breve) che per- mettono l'interazione con dati XML varia nella sua completezza di si- stema in sistema e di produttore in produtto- re, e a seconda natural- mente delle necessità del software che ne farà uso. http://www.itportal.it 3 ►►► 15 3 xmlMenu.ignoreWhite = true; XML e Flash XML e Flash L'utile ed II dilettevole si Incontrano XML e Flash y^ft II supporto XML di ■^s Flash, reso dispo- nibile solo a partire dal- la versione 5.0, non of- fre molte funzionalità e, a prima vista, il suo funzionamento appare davvero criptico, anche (e oserei direi soprat- tutto) per chi già utiliz- za altri DOM XML; i van- taggi offerti, però, dallo studio di questo model- lo ad oggetti e dalla conseguente possibilità di elaborare dati XML tramite Flash sono mol- ti e aprono le porte a progetti software com- pletamente dinamici e di altissimo impatto vi- sivo. 4 xmlMenu.onLoad = MenuLoaded; 5 xmlMenu.load('Menu.xml'); In questa prima porzione di codice avviene la creazio- ne dell'oggetto XML e del caricamento dei dati pre- senti nel menu XML, creato poco fa. Andando nel det- taglio viene effettuata la dichiarazione e la creazione dell'oggetto xmlMenu (1), viene impostata la proprietà ignoreWhite a true (2), associato all'evento onLoad la funzione MenuLoaded che tra poco analizzeremo (3) e infine caricato il file menu.xml (4). Fino a qui non pos- siamo ancora determinare se tale file è stato caricato o meno nell'oggetto xmlMenu. Per poterlo fare è neces- sario entrare nel dettaglio della funzione che viene lan- ciata non appena il caricamento del file XML è termi- nato. 10 function MenuLoaded(bSuccess) ili 12 ifQbSuccess) { 13 trace('Menu non caricato.'); 14 return; 15_J 16 17 trace('Menu caricato.'); 18 19 var ndRoot = xmlMenu.firstChild; 20 var ndElementi = ndRoot.firstChild; 21 22 DisplaySubMenu(ndElementi, 0); 23} Come anticipato, MenuLoaded viene richiamata quan- do il caricamento del file XML termina. È possibile sa- pere se tale caricamento è andato a buon fine analiz- zando il parametro bSuccess, booleano, valorizzato a false in caso di problemi, a true altrimenti. Come è pos- sibile osservare, il codice d'esempio effettua un trace dichiarando lo stato dell'operazione (13 e 17) e in caso questa non sia stata andata a buon fine esce dalla fun- zione (14) annullando di fatto l'intera operazione. Nel- l'ipotesi in cui menuXML contenga la struttura presen- te nel file menu.xml, vengono dichiarate due variabili. La prima, ndRoot (19), è in realtà il primo elemento presente all'interno della struttura XML, ciò che nella totalità dei DOM XML presenti nel mercato viene chia- mato nodo radice) per Flash tale nodo è dunque il pri- mo nodo figlio del documento XML caricato. Vista la struttura del documento XML, è conveniente creare una funzione generica (Display SubMenu), incaricata di elaborare in maniera ricorsiva i dati presenti nel tag elementi. Tale funzione visualizzerà i tag voce presenti nella struttura che verrà passata in parametro e richia- merà sé stessa qualora vi fossero altre strutture ele- menti figlie di quella originale. L'idea della ricorsione è molto utile in casi come questi dove le operazioni da effettuare per un blocco di dati sono identiche per n blocchi di uguale struttura. Data la necessità di elabo- razione del tag elementi, richiediamo al nodo radice la referenza alla struttura di primo livello di questo tipo e associamo alla variabile ndElementi tale referenza. Valorizzata ndElementi, richiamiamo la funzione Di- splay SubMenu di cui ora verrà fatta analisi approfondi- ta. 30 var verticalDeep = 0; 31 32 function DisplaySubMenu(xmlData, deep) Hi 34 var ndVoce = xmlData.firstChild; 35 while(ndVoce) 26_L 37 switch(ndVoce.nodeName) 38 { 39 case voce : 40 DisplayMenuItem(ndVoce, deep); 41 verticalDeep+ + ; 42 break; 43 case 'elementi' 44 DisplaySubMenu(ndVoce, deep + 1); 45 break; 46 } 47 48 ndVoce = ndVoce.nextSibling; 49 } 50} Display SubMenu è la funzione cuore della fase dell'ela- borazione dei dati. Come è possibile osservare nella sua dichiarazione (32), sono previsti due parametri di chiamata. Il primo, xmlData, sarà un nodo XML di tipo elementi. Il secondo parametro, deep, altro non sarà che la profondità orizzontale del submenu, in altre parole il livello del menu che la funzione andrà a creare. Non a caso nella chiamata effettuata in MenuLoaded (22), deep è impostato a zero. Alla stessa maniera viene mantenuta una variabile globale, verticalDeep (30), ini- zializzata a zero che altro non è che la profondità ver- ticale del menu: servirà nella fase di visualizzazione per disegnare le voci di menu una sotto l'altra. La fun- zione crea dapprima la variabile ndVoce e la fa punta- re al primo nodo figlio del nodo elementi ricevuto co- me parametro (34). Viene poi iniziato un ciclo che du- rerà fintanto che ndVoce sarà un nodo valido (35). Suc- cessivamente, se ndVoce è un tag di tipo voce (39) que- sto viene passato alla funzione DisplayMenuItem (40) che si occuperà di visualizzare la voce a video (la ve- dremo in seguito); se invece tale nodo è di tipo elemen- ti (43), viene applicato il concetto di ricorsione già an- ticipato per cui viene chiamata nuovamente Display- SubMenu (44), valorizzando questa volta il parametro deep ad un valore incrementato di uno rispetto al deep originale. Infine ndVoce viene valorizzata con il suo nodo succes- sivo dello stesso livello (48): qualora questo nodo non esistesse (al termine della struttura XML), ndVoce verrà !&►►► F e b b http://www.itportal.it TECNICA impostata automaticamente a nuli, di fatto interrom- pendo il ciclo. Va altresì aggiunto che verticalDeep vie- ne incrementata ad ogni visualizzazione di voce di menu (41), proprio al fine di tenere traccia della profondità verticale nelle fase di presentazione dei da- ti. Al fine di ottimizzare lo sfruttamento delle risorse e migliorare la qualità del nostro lavoro, risulta ora con- veniente prevedere la costruzione di un movieclip da utilizzare come modello di visualizzazione delle voci di menu. Tale movieclip verrà inserito nella library del movie su cui stiamo lavorando e ci permetterà di riu- tilizzarlo per costruire le n voci di menu di cui neces- sitiamo; la funzione di visualizzazione si occuperà dunque di duplicare questo movieclip e di impostar- ne le proprietà necessarie affinchè ogni voce contenga i dati corretti. Creiamo dunque preventivamente il movieclip mvVoce, dotiamolo di un testo dinamico al quale associeremo la variabile Testo. Fatto questo im- portiamolo nella library, ricordando di spuntare nelle proprietà di linkage la checkbox Export for Action- Script: tale impostazione ci permetterà di lavorare col movieclip presente nella libreria anche tramite il codi- ce che andremo a creare. Finiti i preparativi, vediamo dunque nel dettaglio la funzione di visualizzazione delle voci di menu. 60 function DisplayMenuItem(xmlData, deep) §LL 62 attachMovie('mvVoce', 'dmvVoce' + verticalDeep, verticalDeep); 63 var mvCurrent = eval('dmvVoce' + verticalDeep); 64 65 mvCurrent.onRelease = functionQ { 66 getURL(xmlData.attributes.url); 67_i 68 69 mvCurrent. Testo = xmlData.attributes. testo; 70 mvCurrent._x = 20 * deep; 71 mvCurrent. _y = (mvCurrent. _height + 2) * verticalDeep; 72} Come si è già potuto osservare, DisplayMenuItem vie- ne richiamata con il parametro xmlData che punta ad un nodo voce della nostra struttura XML ed il para- metro deep che indica il livello corrente del menu (ma anche il suo scostamento orizzontale, nel nostro caso). Inizialmente la funzione crea un nuovo movieclip (62), duplicando mvVoce, presente nella library. A questo duplicato viene assegnato un nome, creato dinamica- mente, formato dalla stringa dmvVoce (la d sta per di- namico) e dalla profondità verticale che cambia ad ogni chiamata. Eseguita la duplicazione, viene asse- gnata alla variabile mvCurrent la referenza a tale nuo- vo movieclip (63), allo scopo di impostarne le proprietà nelle righe successive. Tramite la referenza appena creata, impostiamo la risposta air evento onRelease del movieclip (65): trattandosi di una voce di menu, non facciamo altro che eseguire un getURL verso il colle- gamento specificato dall'attributo uri del nodo XML voce preso in esame (66). Impostiamo inoltre la varia- bile Testo, associata al testo dinamico creato in prece- denza air interno del movieclip mvVoce: il nuovo valo- re sarà quello dell'attributo testo del nodo voce corrente (69). Non ci resta che impostare le coordinate entro cui il nuovo movieclip sarà disegnato; impostiamo dun- que l'attributo _x ad un valore di scostamento a piace- re (neir esempio si è scelto 20) che ovviamente andrà moltiplicato per la profondità orizzontale del menu (70). Edizioni Master Prodotti • io Programmo * Internet Nagazine ITPortal Macromedia FlashMX World Wide Web Consortium Fig. 2: L'interfaccia Flash, dopo un piccolo ritocco grafico. Infine impostiamo l'attributo __y pari all'altezza del movieclip più un valore di scostamento a piacere (nel- l'esempio 2), moltiplicando il tutto questa volta per la profondità verticale. Il motore di visualizzazione del nostro menu XML è ora pronto e funzionante; sarà sufficiente solo qualche accorgimento grafico per in- globarlo nelle vostre soluzione dinamiche basate su tecnologia Flash. CONCLUSIONI Le possibilità offerte dall'elaborazione dei dati tramite il motore XML fornito da Flash sono notevoli; nono- stante il supporto incompleto che questo prodotto of- fre per la manipolazione dei dati secondo tale model- lo, è comunque facile creare applicazioni completa- mente dinamiche che utilizzano l'interfaccia Flash. L'interesse di Macromedia verso questa tecnologia sembra comunque essere in continuo aumento: se fino alla versione 5 il motore XML era codificato interna- mente tramite actionScript (con l'enorme penale della velocità di esecuzione), con la versione MX di Flash la casa produttrice ha scelto di optare per una compila- zione in C++, inglobando il supporto XML nello stes- so motore del player. Nella visione globale del web, sia le componenti ser- ver che quelle client tendono sempre più verso la tec- nologia XML, facendone di fatto uno standard verso cui guardare con fiducia. Non ci rimane che sfruttare al meglio ciò che il mercato ci propone, seguendo que- sta tendenza che, come abbiamo visto nel corso del- l'articolo, offre ampi spunti di riflessione per mettere all'opera la fantasia. Efran Cobisi XML e Flash XML e Flash L'utile ed il dilettevole si incontrano XSLT r-& Il linguaggio XSLT ^-J {eXtensible Style- sheet Language Transfor- mations), nasce dall'esi- genza di definire uno stru- mento generale che con- senta di trasformare e ma- nipolare un documento XML. Così come per l'XML, anche l'XSLT prende vita dal consorzio W3C, organo che si occupa della sua standardizzazione. La ver- sione corrente di XSLT è la 1.0 (raccomandazione W3C del 16 Novembre 1999), al momento della stesura di questo testo è stata proposta la versione 1.1, attualmente reperibile come working draft http: //www. w3. org/TR/xsltl 1/ http://www.itportal.it 2 3 ►►► 17 :: i;AAAMÌ_ XML e Flash XML e Flash L'utile ed il dilettevole si incontrano XPath y-S II linguaggio XPath --J viene utilizzato per formulare espressioni in grado di valutare ogni nodo di un documento xml e di svolgere deter- minate operazioni su di essi, restituendo a parti- colari valori di tipo strin- ga, numerici o booleani. Il metodo con il quale XPath esplora le diverse strutture di nodi, all'in- terno del documento XML, è denominato per- corso di posizione. Un percorso di posizione di- spone di una sintassi specifica che consente di includere operatori ed espressioni utilizzati nel- l'individuazione di parti di un documento in base al tipo di struttura che rappresentano. L'OGGETTO XML DI ACTIONSCRIPT PRONTUARIO DELLE FUNZIONI PIÙ USATE new XML([sorgente]) È il costruttore dell'oggetto XML, vale a dire il metodo da ri- chiamare per creare un oggetto di questo tipo. Accetta op- zionalmente come unico parametro una stringa di testo XML con cui istanziare il nuovo oggetto. Es: Creazione di un nuovo oggetto XML vuoto var xOggetto = new XML(); Es: Creazione di un nuovo oggetto XML con alcuni dati var xOggetto = new XML('PD Mk/provincia> PROPRIETÀ attributes È un array associativo che contiene gli attributi xml di un particolare nodo. Es: Accesso ad un attributo xml var xOggetto = new XML(''); trace(xOggetto.firstChild.attributes.sigla); // Visualizza in output la stringa PD childNodes È un array che contiene i nodi figli di un particolare no- do xml. Es: Accesso al secondo nodo figlio di una struttura XML var xOggetto = new XML(''); var xFiglio = xOggetto. childNodes[1]; firstChild Proprietà di un qualsiasi oggetto XML che punta al pri- mo nodo figlio di quest'ultimo. È conseguentemente una scorciatoia per oggetto. childNodes[0]. Nel caso in cui l'oggetto XML non abbia nodi figlio, tale proprietà ritorna nuli. lastChild Proprietà di un qualsiasi oggetto XML che punta all'ul- timo nodo figlio di quest'ultimo. Anche qui, nel caso in cui l'oggetto XML non abbia nodi figlio, tale proprietà ritorna nuli. ignoreWhite Proprietà booleana che indica, se impostata a true, di non prendere in considerazione gli spazi e i ritorni di carrello presenti tra un tag ed un altro. È molto utile in tutti quei casi dove la struttura xml che si va a leggere contiene questi tipi di caratteri e ciò è molto comune, specie se il vostro file XML è chiaro ed ordinato. Di default è impostata a false, quindi Flash interpreta in maniera scorretta tali tipi di file. È consigliabile impo- starla di regola sempre a true prima di procedere alla lettura di qualsiasi struttura XML. Ioaded Proprietà booleana che indica il corretto caricamento della struttura XML in cui viene chiamata. Se ritorna true, il documento è stato caricato con successo. Es: Controllo sullo stato di caricamento dell'oggetto XML preventivamente creato trace(xOggetto.loaded); // Se xOggetto è stato caricato correttamente visualizza true in output. nextSibling Ritorna l'oggetto XML successivo nello stesso livello corrente, appartenente allo stesso nodo genitore. Que- sta proprietà è utile quando si intende effettuare la scansione di un intero set di nodi XML appartenenti ad uno stesso nodo genitore. Es: Stampa in output tutti gli oggetti figlio di un parti- colare nodo XML var xOggetto = new XML(' '); var xFiglio = xOggetto.firstChild; while(xFiglio) // Questo ciclo si ferma se xFiglio è nuli { trace(xFiglio. attributes. sigla); // Visualizzo di ogni elemento l'attributo sigla xFiglio = xFiglio.nextSibling; L //Visualizza PD MI RM previousSibling Ritorna l'oggetto XML precedente nello stesso livello corrente, appartenente allo stesso nodo genitore. Que- sta proprietà è utile quando si intende effettuare la scansione di un intero set di nodi XML appartenenti ad uno stesso nodo genitore. La funzionalità è inversa ri- spetto a quella offerta da nextSibling. Es: Stampa in output tutti gli oggetti figlio di un parti- colare nodo XML (scansione all'indietro) var xOggetto = new XML(' '); var xFiglio = xOggetto.lastChild; while(xFiglio) // Questo ciclo si ferma se xFiglio è nuli L trace(xFiglio. attributes. sigla); // Visualizzo di ogni elemento l'attributo sigla xFiglio = xFiglio.previousSibling; 1_ Il Visualizza RM MI PD 18 ►►► F e b b http://www.itportal.it TECNICA PROPRIETÀ nodeName Proprietà che ritorna il nome del tag del nodo XML corrente. Es: Stampa in output del nome del tag del primo nodo pre- sente in una struttura XML var xOggetto = new XML(''); var xFiglio = xOggetto.firstChild; trace(xFiglio.nodeName); // Visualizza provincia nodeType Proprietà che ritorna il valore 1 se il nodo XML corrente è un elemento, il valore 3 se il nodo è un nodo contenente solo te- sto. Si noti che Flash tratta i nodi testuali come unici nodi fi- glio del nodo che li contiene. Es: Stampa in output del tipo di nodo per due nodi XML var xOggetto = new XML(' Si trova in Veneto Si trova in Veneto Si trova in Veneto= 0) Then nStatus = IbStatus = "Posiziona il dito sul lettore di impronte" Else MsgBox "Impossibile aprire periferica di acquisizione impronte digitali" End If Il codice è auto-esplicativo: si fa uso della funzione HTOpen che restituisce un valore Long maggiore o uguale a zero se l'operazione è riuscita; i parametri da passare alla funzione sono l'ID della periferica e una variabile globale (pDevice) in cui verrà memo- rizzato il codice associato alla periferica, da usare in tutte le successive operazioni. Ovviamente in fase di uscita bisognerà chiudere e rilasciare il controllo del lettore di impronte usando HTClose. U acquisizione di impronte ad alto livello è immediata, basta solo chiamare HTEnroll passando i parametri necessari e il gioco è fatto: questa chiamata genera infatti una fi- nestra indipendente, dotata di icone e grafica, in cui è possibile acquisire l'impronta di un qualsiasi dito in modo molto semplice; al programmatore non è ri- chiesto nessuno sforzo. U acquisizione a basso livel- lo è invece più macchinosa e richiede Fuso di un ti- mer col quale temporizzare le diverse fasi dell'ac- quisizione (periferica pronta, contatto del dito sul sensore, rilascio del dito): Dim pulReadStatus As Long Dim Result As Long Dim BufferQ As Byte 'Lettura singola impronta digitale usando Low-Level API 'Parametri da passare: ' periferica da usare (ricavata da HTOpen) ' variabile di tipo HT_RAW_DATA che conterrà l'immagine (raw) dell'impronta letta ' variabile di tipo HT_BIO_DATA (dati biometrici calcolati dall'impronta) ' stato della periferica 'Restituisce : ' boolean (true se l'operazione riesce) Dim pRawData As HT_RAW_DATA Dim pEnRollBioData As HT_BIO_DATA Result = HTRead(pDevice, pRawData, pEnRollBioData, pulReadStatus) If Result = HT_OK Then 'Disegna su un frame immagine VB l'impronta catturata in pRawData Result = HTDrawRawImage(Frame.hWnd, 8, 12, 160, 160, pRawData) Acquisizione Impronta cornp etata Fig. 6: LETTURA DELL'IMPRONTA Come un moderno mago, il sensore biometrico legge la nostra mano, visualizzando le impronte sul form....sarà anche in grado di prevedere il nostro futuro? U impronta digitale, una volta acquisita viene resti- tuita dalla funzione HTRead in due formati diversi: come HT_RAW_DATA (corrisponde all'immagine vera e propria acquisita dal sensore) e come HT_BIO_DATA (è la traduzione dell 7 impronta in forma biometrica, analizzabile dal computer). La memorizzazione su file richiede Y intervento di una piccola conversione (eseguita con la API di sistema CopyMemory), necessaria per consentire a Visual Ba- sic di trattare adeguatamente i dati biometrici: 'Converte i dati biometrici acquisiti dal lettore 'in un array di bytes (necessario per l'uso sotto VB) ReDim Buffer(pEnRollBioData.ulLength - 1) CopyMemory Buffer(O), ByVal pEnRollBioData. pvData, pEnRollBioData .ulLength 'Richiede il nome da associare all'impronta acquisita 'e lo scrive su file nome = InputBox("Nome?", "Assegnazione impronta") 24 ►►► F e b b http://www.itportal.it Open "C:\FINGER" + FP_ _RecTest.ID + ".TXT" For Output As #1 Print #1, nome Close #1 'Scrive su file i dati biometria dell'impronta acquisita Open "C:\FINGER' For Binary > + FP_RecTest.ID + ".DAT" <\s #1 Len = pEnRollBioData.ulLength Put #1, , Buffer Close #1 U impronta di ciascun utente viene infine scritta su un file di nome "C:\FFNGERxx.DAT" dove "xx" rappresenta il valore numerico del contatore salvato in "C:\USERS.DAT" , che viene incrementato ad ogni acquisizione. Ad ogni file che memorizza un'impronta è associato un file analogo del tipo "C:\FINGERxx.TXT" che memorizza invece il nome associato alla persona (è un'alternativa scelta per evitare Fuso di un database). FingerPrint Recognition •CormancJi- ACQUISISCi RICi UTENTI F vel- est ìs\ Createci by Elia Fli Fig. 7: CONOSCERE... E RICONOSCERE Il riconoscimento di un'impronta è immediato (si tratta di pochi secondi) e la percentuale di errore è molto bassa. L'applicazione consente tuttavia all'utente di scegliere un adeguato livello di sicurezza, per evitare riconoscimenti errati o troppo serveri. In fase di riconoscimento bisognerà invece leggere una nuova impronta dal sensore (usando HTCaptu- ré) e confrontarla poi con tutte quelle memorizzate nei diversi file "C:\FINGERxx.DAT" '. Dim pBioData As HT_BIO_DATA Result = HTCapture(FP_RecTest.hWnd, pBioData, 10, 0, 0, 0) If Result = HT_OK Then For I = 1 To n Open "C:\FINGER" + CStr(I) + ".DAT" For Binary As #1 Len = 256 Get #1, , Buffer Close #1 Dim fileBioDatal As HT_BIO_DATA fileBioDatal.pvData = VarPtr(Buffer(Q)) fileBioDatal.ulLength = 256 'Confronta due impronte digitali usando High-Level API 'Parametri da passare: ' improntai (tipo HT_BIO_DATA), ' impronta2 (tipo HT_BIO_DATA), ' livello di sicurezza da usare per il confronto ' variabile long che indica l'esito del confronto 'Restituisce : ' boolean (true se l'operazione riesce) Result = HTMatch(fileBioDatal, pBioData, m_nSecul_evel, IRtnMatch_Frt) La funzione HTMatch è il cuore dell' SDK, perché implementa il confronto fra due impronte passate come argomento (fileBioDatal e pBioData), usando un certo valore di soglia (m_nSecuLevel) e resti- tuendo come esito del confronto lRtnMatch_Frt, che vale HT_SUCCEEDED se le due impronte combaciano. FingerPrint Recognition Test -Comandi Riconoscimento completato. Identità : Elia TU Hdiurn Created by Elia Florio (ioProgratnmo - 2003) Fig. 8: PIÙ' PRECISO DI SHERLOCK HOLMES Da un'impronta riconosciuta diventa facile risalire all'identità di una persona usando un database o un archivio che associa i nomi e i dati anagrafici alle impronte digitali acquisite. Per il codice completo del programma si rimanda al progetto VB incluso nel CD-ROM della rivista. Elia Florio EUTRON La Eutron è una società da anni leader nel settore dell'IT Security, produttrice di di- verse soluzioni per la protezione del soft- ware, del copyright e per l'autenticazione dell'identità mediante token, chiavi hard- ware e ora anche attraverso le impronte di- gitali. Tutte le informazioni sul kit MagicSe- cure distribuito dalla Eutron sono reperibili all'indirizzo http://www.eutron.it/infosecurity /magic.asp il costo dei diversi kit biometrici venduti da Eutron varia tra i 130 e i 180 euro (IVA esclusa) mentre l'SDK necessario per lo sviluppo delle applicazioni viene venduto separatamente. Biometria Password a "portata di mano" MagicSecure SDK /A Il sistema biome- ^--J trico MagicSecure progettato dalla Eutron si basa su un sensore di rilevamento impronte (venduto in versione singola o integrato su un mouse ottico) dotato di un kit di autenticazione per sistemi Windows 2000 e XP. Il software comprende il driver USB necessario per pilotare il sensore e un rimpiazza- rne nto del modulo MSGI- NA.DLL di Windows. http://www.itportal.it 3 ►►► 25 TEORIA TECNICA Sistema File sul CD ^s> \soft\codice\compressione\ Ó Lossy Indica un tipo di compressione che a fronte di una perdita par- ziale di informazioni (es. formato JPEG) permette di ottenere livelli di com- pressione più elevati di quelli ottenibili con algo- ritmi di tipo lossless. Librerie DI COMPRESSIONE OPEN SOURCE In questo articolo vedremo come introdurre nelle nostre applicazioni, in modo semplice e rapido, la possibilità di gestire dati in forma compressa, sia su disco che direttamente in memoria. L'utilizzo di alcune delle tecniche illustrate, oltre a ridurre la quantità di spazio necessario a memorizzare i nostri dati, fornisce una prima forma di cifratura di informazioni che non devono essere facilmente rilevabili analizzando i file che fanno parte delle nostre applicazioni. Chi ha sviluppato progetti di una certa en- tità si sarà probabilmente imbattuto nel problema di dover riorganizzare i propri database, immagini o dati in genere, che per ra- gioni di dimensioni sforano il tetto massimo che ci si è prefissati o che le specifiche impongono. Cosa fare, ad esempio, se le textures del nostro ultimo gioco 3D sono troppe o troppo dettagliate ma non vi vogliamo o non vi possiamo rinunciare? Il pri- mo pensiero corre a famosi programmi di archi- viazione come WinZIP, WinRAR e simili. Certo una soluzione, ma come gestire il tutto diretta- mente dal nostro codice? Basta dare un'occhita in giro per la rete per rendersi conto che in realtà esi- stono ottime soluzioni al problema e per di più to- talmente gratuite: librerie di compressione lossless più o meno efficienti e con API più o meno sem- plici da usare. Le librerie di compressione propo- ste di seguito sono state scritte nella maggior par- te dei casi in puro ANSI C, e dunque facilmente portabili su qualunque sistema operativo che for- nisca un compilatore C che rispetti tali specifiche. Per loro natura, quindi, tali librerie sono da utiliz- zarsi principalmente in C, anche se esistono, in al- cuni casi, librerie DLL per Windows e quindi uti- lizzabili, in linea di principio, da qualunque lin- guaggio/ambiente di programmazione (Delphi, Visual Basic, ecc.). Come vedremo tra breve con esempi pratici di utilizzo, le librerie presentano una interfaccia utente per la compressione /de- compressione dei dati secondo due diverse moda- lità: • Direttamente in memoria • Files su disco La prima delle due possibilità è certamente quella più interessante perché ad esempio ci permette di effettuare una fase di precompressione "off-line" dei dati delle applicazioni, magari usando livelli di compressione molto elevati, e utilizzare i files così ottenuti decomprimendoli "on the fly" in me- moria prima di darli in pasto alla parte di codice che dovrà elaborarli. Tale tecnica, se applicata cor- rettamente, porta dei grossi vantaggi per la ridi- stribuzione delle proprie applicazioni, al costo di un ragionevole impatto sulle prestazioni. La com- pressione dei dati presenta inoltre altri vantaggi e talvolta inaspettati effetti collaterali: • Aumento dello spazio disponibile su floppy, ZIP, cdrom, nastri, hard disk e qualunque altro supporto di memorizzazione. • Una certa probabilità di correzione dei possibi- li errori generati dai supporti di memorizza- zione. • Riduzione dei tempi di trasferimento in appli- cazioni di rete e conseguente ottimizzazione dell'utilizzo della banda. • Un primo semplice livello di protezione dei dati stessi: i files e i flussi di dati in memoria saranno, in senso lato, cifrati. Nel seguito analizzeremo le API fornite da tre li- brerie di compressione dati (LibBzip2, LZO e Zlib) indicando vantaggi e svantaggi di ciascuna, e for- nendo alcuni esempi di utilizzo delle loro primiti- ve. I sistemi operativi presi come riferimento, sia per la compilazione delle distribuzioni delle stesse librerie che per gli esempi proposti, saranno quel- li Unix-like. LIBBZIP2: EFFICIENTE MA NON VELOCISSIMA Questa libreria si basa sull'algoritmo di compres- 26^^ F e b b http://www.itportal.it sione di Burrows-Wheeler, che mediamente si comporta meglio della più famosa famiglia di al- goritmi LZ77/LZ78 anche se non eccelle in quan- to a velocità di compressione. Questa libreria è di- sponibile per una vasta gamma di architetture, tra le quali Linux, Windows 95/98/ME/NT 4/ 2000/XP (in versione statica e DLL), MacOS, Sola- ris Sparc e x86, OS/2, OpenBSD e FreeBSD. L'interfaccia utente fornita da LibBzip2 si presenta suddivisa su tre livelli: funzioni di basso livello, funzioni di alto livello e funzioni di utilità. Al li- vello più basso troviamo le funzioni per compri- mere/decomprimere direttamente in memoria: • int BZ2_bzCompresslnit(bz _stream *stream, int blockSize, int verbosity, int workFactor) • int BZ2_bzCompress(bz _stream *stream, int ac- tion) ti da comprimere. Per quanto riguarda il parame- tro small, se è settato ad un valore diverso da zero forza al libreria ad usare un algoritmo di decom- pressione alternativo che riduce l'utilizzo della memoria. Sebbene il tutto possa sembrare compli- cato, in realtà la sequenza di operazioni da com- piere per comprimere un blocco di dati è piuttosto semplice: 1) Invocare BZ2_bzCompressInit(&stream, 4, 0, 30) specificando i puntatori ai buffer sorgente /de- stinazione nella struttura puntata da &stream 2) Invocare BZ2_bzCompress(&stream, BZ_RUN) 3) Modificare opportunamente i puntatori ai buf- fer sorgente /destinazione e invocare BZ2_bz- Compressi&stream, BZ_FINISH) finché ci sono dati da comprimere Sistema Librerie di compressione Open Source • int BZ2_bzCompresEnd(bz_stream *stream) 4) Invocare BZ2_bzCompresEnd(&stream) • int BZ2_bzDecompresslnit(bz _stream *stream, int verbosity, int small) • int BZ2_bzD ecompr ess(bz_stream *stream) • int BZ2_bzDecompressEnd(77z_s£razra *stream) Definizione del tipo bz_stream in bzlib.h typedef struct { char *next_in; unsigned int avail_in; unsigned int total_in_lo32; unsigned int total_in_hi32; char *next_out; unsigned int avail_out; unsigned int total_oiitJo32; unsigned int total_out_hi32; void *state; void *(*bzalloc)(void *, int, int); void (*bzfree)(void *,void *); void *opaque; _j bz_stream; Un tipo bz_stream, come è possibile vedere, indica una struttura in cui sono mantenute informazioni sui buffer sorgente e destinazione utilizzati duran- te la compressione/decompressione, blockSize- [1, ... ,9] è la dimensione del blocco dati usato per la compressione (9=massima compressione ed eleva- to uso della memoria), verbosity -[0,... ,4] indica il livello di verbosità da usare (0=nessun messag- gio), mentre workFactor=[0,..,250] indica la soglia oltre la quale si passa dall' algoritmo standard a quello di tipo fallback in presenza di blocchi di da- ti ripetitivi, computazionalmente piuttosto pesan- Se il blocco dati da comprimere non è molto gran- de si può invocare una sola volta la BZ2_bzCom- press() specificando direttamente il valore BZ_FI- NISH per il parametro action. Allo stesso modo, per decomprimere un blocco di dati in memoria basta: 1) Invocare BZ2_bzDecompressInit(&stream / 0, 0) specificando i puntatori ai buffer sorgente /de- stinazione 2) Invocare ripetutamente BZ2_bzD ecompressi & stream) aggiustando di volta in volta i puntato- ri ai buffer sorgente /destinazione 3) Invocare BZ2_bzDecompressEnd(&stream) per concludere l'operazione Vediamo un breve esempio di come sia possibile comprimere e decomprimere un blocco di dati in memoria usando queste primitive (esempio pre- sente sul CD: ESEMPIO 1: bzip2_LowLevel.c). L'interfaccia di alto livello fornisce una serie di primitive che permettono di comprimere e decom- primere direttamente i file, che per default sono identificati dal suffisso .bz2: • BZFILE *BZ2_bzReadOpen(m£ *bzerror, FILE *fp, int small, int verbosity) • int BZ2_bzRead(m£ *bzerror, BZFILE *bzfp, void *buf, int len) • void BZ2_bzReadGetUnused(m£ *bzerror, BZ- FILE *bzfp, void **unused, int *nllnused) • void BZ2_bzReadClose(m£ *bzerror, BZFILE Burrows- Wheeler ^a La trasformata di ^---J Burrows-Wheeler è una operazione che per- mette di ottenere, a par- tire da una stringa S di N caratteri, una matrice M le cui righe sono tutti gli shift ciclici a sinistra del- la stringa 5. L'ultima co- lonna BW della matrice M ordinata costituisce la trasformata di Burrows- Wheeler. La stringa BW così ottenuta contiene gli stessi caratteri della stringa in input, ma è più facile da comprimere. http://www.itportal.it 2 3 ►►► 27 Sistema Librerie di compressione Open Source LZ77/LZ78 y-S Sono due varianti di ^-J una serie di algorit- mi detti di Lempel-Ziv. Concettualmente l'algo- ritmo LZ78 è una varian- te di LZ77 che consente di raggiungere una com- pressione di 4:1. Invece di caricare i dati in un buffer e sostituire i byte ripetuti solamente entro il blocco attuale, LZ78 crea un elenco dei byte ripetuti e li sostituisce sull'intero file. *bzfp) • BZFILE *BZ2_bzWriteOpen(m£ *bzerror, FILE *fp, int MockSize, int verbosity, int workFactor) • void BZ2_bzWrite(int *bzerror, BZFILE *bzfp, void *buf, int len) • void BZ2_bzWriteClose(m£ *bzerror, BZFILE *bzfp, int abandon, unsigned int *nbytes_in, unsi- gned int *nbytes_out) Il prossimo esempio chiarisce come utilizzare tali primitive (esempio presente sul CD: ESEMPIO 2: bzip2_HiLevel.c). Le funzioni di utilità, infine, per- mettono di comprimere e decomprimere i dati in memoria attraverso una semplice chiamata alle se- guenti funzioni: • int BZ2_bzBuffToBuffCompress(c/zar *dest, unsigned int *destLen, chat *source, unsigned int sourceLen, int MockSize, int verbosity, int work- Factor) • int BZ2_bzBuffToBuffDecompress(c/zar *dest, unsigned int *destLen, char *source, unsigned int sourceLen, int small, int verbosity) i cui parametri hanno la stessa semantica di quelli visti in precedenza per le funzioni di alto livello (esempio presente sul CD: ESEMPIO 3: bzip2_Uti- lity.c). Per compilare gli esempi precedenti, basta seguire la solita procedura: • Scaricare il tarball bzip2-1.0.2.tar.gz dal sito sources.redhat.com/bzip2 • Compilare il tutto col comando "make" per ot- tenere la libreria \ibbz2.a • Compilare gli esempi da riga di comando: ce -lbz2 L'unico svantaggio di questa libreria risiede nel- l'affermazione fatta dagli autori della stessa, e suf- fragata dall'evidenza dei benchmark, ossia nella non trascurabile quantità di memoria e cicli di CPU richiesti per la compressione e decompres- sione dei dati. Per questo motivo LibBzip2 non è la scelta ottimale nel caso in cui Y interattività risulta essere un fattore chiave dell'applicazione. Ma que- sto probabilmente è da imputare alla complessità computazionale della trasformata di Burrows- Wheeler. LZO (LEMPEL-ZIV-OBERHUMER): DECOMPRESSIONE REAL-TIME Questa libreria di compressione è considerata co- me una delle più efficienti in termini di tempo necessario a comprimere /decomprimere i dati: la velocità permette di operare la decompressio- ne, e spesso anche la compressione, in real-time. Inoltre è molto efficiente in termini di memoria richiesta per le operazioni: sia la compressione che la decompressione possono essere di tipo overlapped, ossia è possibile comprimere o de- comprimere i dati nella stessa area in cui questi sono memorizzati, azzerando di fatto la richiesta di memoria extra. Il rovescio della medaglia è che purtroppo non risulta altrettanto efficiente in termini di rapporto di compressione. Per tale motivo, la migliore strategia di utilizzo consiste nel precomprimere i dati da usare nelle applica- zioni, e decomprimerli al volo a seconda delle necessità, facendo affidamento sulla capacità di decompressione real-time. In tal modo è possibi- le usare algoritmi relativamente lenti nella com- pressione ma molto efficienti. La libreria LZO comprende molti algoritmi, cia- scuno con diversi livelli di compressione: LZOX- N, con X=tipo di algoritmo (1, 1A, 1B, 1C, 1F, IX, 1Y, 1Z, 2A), ed N=livello di compressione (1...9, 99, 999). Naturalmente, quanto più è elevato il valore di N tanto più efficiente e lento sarà il processo di codifica. Nella maggior parte dei ca- si la scelta migliore risulta essere l'algoritmo LZOIX-N. Anche in questo caso le piattaforme supportate sono le più svariate: Dos (16 e 32 bit), Windows 3.x, Windows 95/98/NT 4/2000/XP, Linux, HPUX, Alpha, AIX, MacOS, e così via. Esistono inoltre versioni native in Java, Perl e Python. L'interfaccia utente in C, linguaggio in cui è scrit- ta la libreria, risulta essere molto estesa, per la- sciare al programmatore la più ampia libertà sul- la scelta dell'algoritmo da utilizzare, ma di sem- plice gestione. Rispetto alle altre, tuttavia, non fornisce primitive in grado di creare files con un preciso formato di compressione standard, ma tutto il lavoro è lasciato al programmatore. A ta- le scopo sono fornite alcune primitive di utilità tra cui quelle per il calcolo del checksum (Adler e CRC), lzo_fwrite() ed lzo_fread() (versioni portabi- li delle freadQ /fwriteO standard). Le primitive for- nite permettono unicamente di operare la com- pressione/decompressione di blocchi di dati in memoria. Per operare su uno di questi blocchi basta invocare la funzione lzo_init() e quindi pas- sare alla compressione o decompressione dei da- ti in questione utilizzando le funzioni relative al- la versione dell'algoritmo scelto (ad esempio lzolx_l_compress() e lzolx_l_decompress() nel caso si volesse usare LZOlX-1). Vediamo in dettaglio queste primitive: • int ìzo_init(v oid): inizializzazione della libre- ria da invocare prima dell'utilizzo di qualun- 28^^ F e b b http://www.itportal.it que altra primitiva. int lzolx_l_compress (const Izojbyte *src, Izojuint src_len, Izojbyte *dst, Izojuintp dst_ leti, lzo_voidp wrkmetn): compressione con algoritmo LZOlX-1. Il parametro turk- meni rappresenta il puntatore ad un buffer utilizzato internamente dalla libreria e la cui dimensione varia in base all'algoritmo usato. La dimensione di questo buffer è predefinita da una serie di costanti che ritroviamo nel- Theader file relativo all'algoritmo. Per quan- to riguarda V esempio riportato di seguito, nel file Izolx.h è definita la costante LZ01X_1_ MEM COMPRESS. A questo punto è possibile compilare l'esempio che usa le funzioni relative all'algoritmo LZOIX- 1 con un semplice comando: (esempio presente sul CD: ESEMPIO 4: lzo_compress.c). ce -o lzo.com press lzo.com press. e -llzo Oltre alla versione completa, esiste anche una versione minimale della libreria LZO, chiamata miniLZO, costituita da un sorgente in C e due header files, che occupa solo 6KB dopo la compi- lazione, mentre l'intera distribuzione occupa ap- pena 14 KB. Come dire: nessuna scusa per non incorporare queste funzionalità nella proprie ap- plicazioni. Sistema Librerie di compressione Open Source • int lzolx_decompress(cons£ Izojbyte *src, Izojuint src_len, Izojbyte *dst, Izojuintp dst_ len, Izojvoidp wrkmem): decompressio- ne con algoritmo LZOlX-1. In questo caso il parametro wrkmem non è utilizzato e va po- sto al valore NULL. Esiste, come detto, una vasta scelta di primitive: in genere per ciascun algoritmo esiste una serie di funzioni del tipo lzoALGJSl_compress() ed lzoALG_ N_decompressTYPE(), dove: • ALG=l, la, Ih, le, If, Ix, ly, Iz, la • N=l,2,3,4,5,6,7,8,9,99,999 • TYPE= _safe, _asm, _asm_safe, _asm_fast, _asm_fast_safe. Il suffisso jism indica il fatto che il codice è per lo più scritto in assembly ottimizzato, _safe indica la versione di de- compressore che effettua alcuni test di vali- dità del blocco dati compresso da elaborare, mentre _fast indica una versione ancora più performante in termini di velocità di esecu- zione. Per quello che concerne il buffer in cui i dati ver- ranno decompressi, esistono semplici formule per calcolarne la dimensione: • Per gli algoritmi LZOl* outjbufferjsize = in_buffer_size+(inJbuffer_size/64)+16+3 . • Per gli algoritmi LZ02* outjbufferjsize = in_buffer_size+ (in_buffer_size/8)+l2 8 +3 . La libreria LZO può essere scaricata da www.ober- humer.com/opensource/lzo e compilata con la se- guente procedura: • configure • moke • moke instali ZLIB: UN BUON COMPROMESSO TRA VELOCITÀ ED EFFICIENZA Questa libreria utilizza una variante del ben no- to algoritmo LZ77 chiamata deflation (RFC1951), che affonda le sue radici nell'altrettanto famoso PkZip della PkWare. Anche Zlib, come le precedenti, è assolutamente gratuita, scritta in ANSI C e vanta un enorme nu- mero di sistemi operativi su cui è stata imple- mentata: Windows 9x/NT 4/2000 /XP/CE, Li- nux, MacOS, Solaris, HPUX, Compaq Tru64, Irix 6.x e BeOS, solo per citare i più noti. Il rapporto di compressione può variare da 2:1 a 5:1, anche se, come da specifiche, il formato zlib è capace di un limite teorico di 1030:1. Definizione del tipo z_streamp in zlib.h. typedef struct z_stream_s { Bytef *next_in; /* next input byte */ ulnt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total nb of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ ulnt avail_out;/* remaining free space at next_out */ uLong total_out; /* total nb of bytes output so far */ char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc;/* used to allocate the internai state */ free_func zfree; /* used to free the internai state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: ascii or binary */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ Adler ^a Si tratta di una tec- ^J nica di calcolo del checksum che impiega 2 contatori da 16 bit, si ed s2. Il primo contiene la somma di tutti i byte in input, mentre s2 contie- ne la somma di tutti i va- lori di si. Entrambe le somme sono modulo 65521. Il checksum è da- to da s2*65536 + si in network byte order. CRC ^a II Cyclic Redundan- -J cy Check permette di calcolare il checksum (un valore a 32 bit che riassume il contenuto di un blocco di dati) som- mando blocchi da 2 byte in complemento a 1. http://www.itportal.it 3 ►►► 29 TE } z_stream ; Sistema Librerie di compressione Open Source Lossless ^a E' un termine riferi- J to ad una famiglia di algoritmi di compressio- ne che permettono di ri- durre la dimensione di qualunque tipo di dati senza perdita di alcuna informazione. Sul Web W LibBzip2: http://sources.redhat.com/ bzip2 LZO : http://www.oberhumer.com /opensource/lzo Zlib : http://www.qzip.org/zlib typedef z_stream FAR * z_streamp; U interfaccia verso il programmatore risulta esse- re composta da tre classi, una di base (Basic) che permette un tuning fine di ogni parametro del- l'algoritmo, una di alto livello (Utility) che pre- senta funzioni per la compressione /decompres- sione in memoria e di file in formato standard gzip (RFC1952), ed infine una classe di funzioni avanzate (Advanced) che risulta essere abbastan- za complessa e di utilizzo piuttosto raro. Tali classi risultano essere ben strutturate e pos- sono coprire qualunque necessità possa avere il programmatore: ne è una chiara dimostrazione il fatto che sia stata adottata per i loro prodotti da moltissime compagnie leader nel settore del software per PC. Tra queste ricordiamo anzitutto Microsoft, Apple, Adobe, Macromedia, Cisco, Borland e Netscape. Vediamo in dettaglio i pro- totipi delle funzioni di livello Utility: • int compress(Byfef *dest, uLongf *destLen, con- st Bytef *source, uLong sourceLen) • int uncompress(Byfef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) • gzFile gzopen(const char *path, const char *mode) • int gzread( gzFile file, voidp buf, unsigned len) • int gzwrite( gzFile file, const voidp buf, unsi- gned len) • z_off_t gzseek( gzFile file, z_off_t offset, int whence) • int gzeoi(gzFile file) • int gzclose( gzFile file) I parametri da passare alle funzioni sono piutto- sto intuitivi e l'implementazione del codice che effettua la compressione risulta molto semplice, come si può rapidamente dedurre dall'esempio (esempio presente sul CD: ESEMPIO 5: zlib_Uti- lity.c). Per quanto riguarda la classe Basic, troviamo le seguenti funzioni: • int deflatelnit (z_streamp strm, int level) • int def late (z_streamp strm, int flush) • int deflateEnd (z_streamp strm) • int inflatelnit (z_streamp strm) • int inflate (z_streamp strm, int flush) • int inflateEnd (z_streamp strm) Anche in questo caso, i parametri da passare alle funzioni sono concettualmente identici al caso della libreria LibBzipl. Anzi, come chiaramente si può vedere confrontando i listati 1 e 2, le struttu- re che descrivono lo stream di dati da compri- mere o decomprimere sono pressoché identiche se si trascurano alcuni piccoli particolari. Si veda l'esempio presente sul CD: ESEMPIO 6: zlib_Ba- sic.c). La compilazione della distribuzione in formato sorgente di Zlib avviene con la solita procedura: • configure • make • make instali mentre per compilare gli esempi forniti basta da- re i comandi: • ce -o zlib_Utility zlib_Utility.c -lz per com- pilare l'esempio relativo alle funzioni della classe Utility. • ce -o zlib_Basic zlib_Basic.c -lz per compi- lare l'esempio relativo alle funzioni della classe Basic. CONCLUSIONI Come avrete capito, queste librerie di compres- sione open source permettono di dotare le nostre applicazioni di porzioni di codice, relativamente semplici da implementare, che gestiscono dati compressi con una notevole riduzione di spazio occupato su disco a fronte di un ragionevole overhead in termini di memoria e cicli di CPU. Molte applicazioni che usiamo tutti i giorni fan- no uso di tali librerie e ciò testimonia, più di ogni altra argomentazione, l'indubbia affidabilità e i molti vantaggi che se ne possono trarre. La caratteristica di essere multipiattaforma, inol- tre, permette di avere un impatto praticamente nullo su tutte quelle applicazioni di natura platform-independent. Insomma, una volta capite le modalità di utiliz- zo di queste librerie, la possibilità di maneggiare grosse quantità di dati al costo di una frazione delle loro reali dimensioni diventa pressoché tra- sparente per le nostre applicazioni. Sicuramente un ottimo motivo per aggiungere la compressio- ne la prossima volta che si ha a che fare con quantità titaniche di dati. Giuseppe Palumbo 30 ►►► F e b b http://www.itportal.it TEORIA Fotolab FILTRI FOTOGRAFICI IN VISUAL BASIC In questo articolo, suddiviso in due parti, si vogliono presentare alcuni algoritmi di ritocco fotografico finalizzati a migliorare la qualità di immagini bitmap acquisite con lo scanner o con una macchina fotografica digitale e ad estrarne delle caratteristiche, come ad esempio i contorni delle figure. Per memorizzare questo livello di intensità oc- corrono 8 bit per ciascuno dei colori primari, e quindi in totale 24 bit per pixel. In questo mo- do si hanno 2 A 24 = 16.777.216 possibili colori. Alcuni esempi di colori sono indicati nella Ta- bella 1. Secondo il modello RGB, quando le intensità dei tre colori primari sono identiche si ottengo- no dei livelli di grigio; pertanto si possono ave- re 256 livelli di grigio, graduati dal nero 0, 0, al bianco 255, 255, 255. Visual Basic ^File sul CD \soft\codice\filtri.zip Un qualche ritocco si rende spesso ne- cessario perché l'acquisizione di im- magini comporta inevitabilmente l'in- troduzione di "rumore elettronico", che si ma- nifesta mediante l'alterazione del colore di qualche pixel, ed anche di qualche distorsione geometrica. Gli algoritmi verranno sviluppati in Visual Basic, partendo da un file in formato BMP, limitandosi ad usare le librerie e i con- trolli standard di tale linguaggio. Pertanto ri- sulterà agevole la realizzazione degli stessi in altri linguaggi di programmazione. IL MODELLO RGB Si ricorda brevemente che le immagini bitmap a colori sono costituite da una griglia di punti, ad esempio 800 x 600 punti, detti pixel, per cia- scuno dei quali si memorizza il colore, ottenuto come combinazione dei tre colori primari Red, Green, Blue (Rosso, Verde, Blu). Per ciascuno di questi colori si memorizza il livello di intensità che può variare da = assenza di colore a 255 = massima intensità di colore, e quindi massima luminosità (brightness). R G B Colore 255 rosso 255 verde 255 255 giallo 255 255 240 avorio 255 255 255 bianco 128 128 128 grigio nero IL FORMATO BMP La scelta di questo formato è stata dettata dalla sua ampia diffusione unitamente alla sua gran- de semplicità. Innanzitutto si deve sapere che un file BMP è composto di 4 parti: 1. intestazione del file, 2. intestazione dell'immagine, 3. eventuale tavolozza dei colori, 4. i dati sui pixel. La Tabella 2 rappresenta in dettaglio la struttu- ra di un file BMP, dove in neretto sono eviden- ziati i campi che vengono utilizzati nel pro- gramma FotoLab che svilupperemo. Si fa notare che l'immagine viene memorizzata capovolta rispetto a come essa viene visualizzata a video: ovvero le righe di pixel che costituiscono l'im- magine vengono memorizzate in ordine inver- so, dall'ultima alla prima. Si veda la Fig. 1. immagine uisualizzata immagine memorizzata Tab. 1: Livelli di intensità necessaria a ottenere alcuni colori fondamentali. Fig. 1: Il formato BMP memorizza le immagini capovolte. TRASFORMAZIONI DELL'IMMAGINE Tralasciando tutte le trasformazioni di tipo geo- metrico (quali ad esempio riflessione, rotazio- ne, ingrandimento, riduzione dell'immagine), ci si occupa delle trasformazioni che modifica- La risoluzione dell'immagine r& Il numero di pixel -J che formano l'imma- gine acquisita da una fo- tocamera digitale è fisso, e dipende dalle caratteri- stiche del suo sensore CCD (Charge Coupled De- vice): ad esempio essa potrebbe avere un senso- re da circa 2 milioni di pixel disposti in una gri- glia di 1600 x 1200 pixel. Questo vale anche per lo schermo del computer, che viene solitamente impostato per visualizza- re immagini a 800x600 pixel, oppure 1024x768 pixel. Tuttavia le dimen- sioni in pixel non dicono nulla sulle dimensioni fi- siche in cm dell'immagi- ne: è necessario conside- rare la densità dei pixel, ovvero la risoluzione del- l'immagine. http://www.itportal.it 3 ►►► 31 Visual Basic Fotolab Filtri Fotografici in Visual Basic La dimensione dell'immagine /-& La risoluzione di --^ una immagine è il rapporto tra il numero dei pixel che la costitui- scono e la dimensione fisica della stessa; essa si misura in dpi (dot per inch = punti per polli- ce). Si ricorda l'equiva- lenza 1 pollice = 2.54 cm. Un normale moni- tor per computer ha una risoluzione relati- vamente bassa, di 72 dpi, mentre una stam- pante a getto d'inchio- stro può avere una riso- luzione di 600 dpi. Uno scanner piano per fogli A4 può avere una riso- luzione effettiva di 2400 dpi, uno scanner per pellicole una risolu- zione di 2700 dpi. La stessa immagine può essere riprodotta su carta con risoluzioni e quindi dimensioni di- verse; ad esempio una foto di 1268 x 1012 pixel se stampata ad una risoluzione di 300 dpi avrà dimensioni di 4.2 x 3.4 pollici, ovvero di 10.7 x 8.6 cm (in- fatti 1268 / 300 = 4.2 e 1012 / 300 = 8.6); se invece la si vuole stam- pare con dimensioni raddoppiate su entram- bi i lati, la risoluzione si dimezza a 150 dpi. no il colore dei pixel, con l'obiettivo di miglio- rare la qualità di una immagine affetta da ru- more, oppure troppo sfuocata, o scura, e di tra- sformazioni che cercano di evidenziare la su- perficie oppure i contorni degli oggetti in essa rappresentati. In base alle tecniche utilizzate, le trasformazioni possono essere suddivise in: • trasformazioni puntuali, che agiscono su ciascun pixel singolarmente; • trasformazioni locali che modificano il colo- re di un pixel in funzione dei pixel che gli stanno attorno, • trasformazioni globali che modificano il co- lore di un pixel in funzione di tutti i pixel dell'immagine: si tratta ad esempio della trasformata di Fourier per l'analisi delle fre- quenze spaziali dell'immagine (essa non viene considerata in questo articolo). TRASFORMAZIONI PUNTUALI Dato il generico pixel, indichiamo con x(R), Nome Campo Dimensione Descrizione Intestazione del File totale 14 byte Firma 2 byte = Stringa Contiene i caratteri 'BM' Dimensione File 4 byte = Long Dimensione del file in byte riservato 4 byte non utilizzato (= 0) Offset 4 byte = Long Spiazzamento dei dati relativi ai pixel Intestazione dell'Immagine totale 40 byte Dimensione dell'intestazione 4 byte = Long Dimensione dell'intestazione in byte = 40 W 4 byte = Long Larghezza Immagine H 4 byte = Long Altezza Immagine Numero Piani 2 byte = Integer Numero di piani del disegno (= 1) Bit per Pixel 2 byte = Integer Bit per Pixel 1 = monocromatica. Numero Colori = 1 4 = 4 bit. Numero Colori = 16 8 = 8 bit. Numero Colori = 256 16 = 16 bit. Numero Colori = 65.536 24 = 24 bit RGB. Numero Colori = 16 milioni Compressione 4 byte = Long Tipo di Compressione = RGB nessuna compressione 1 = RLE8 codifica RLE a 8 bit 2 = RLE4 codifica RLE a 4 bit Dimensione dell'Immagine Compressa 4 byte = Long = se Tipo di Compressione = Pixel Per Metro AsseX 4 byte = Long Scala orizzontale Pixel /metro Pixel Per Metro Asse Y 4 byte = Long Scala verticale Pixel /metro Numero Colori 4 Usati byte = Long Numero di colori effettivamente usati Colori Importanti 4 byte = Long Numero di colori importanti = se tutti Tavolozza dei colori totale (4 * Num. Colori Usati) byte E presente solo se Bit Per Pixel <= 8 Blue lbyte Intensità di Blu Green lbyte Intensità di Verde Red lbyte Intensità di Rosso riservato lbyte Non utilizzato (= 0) ... ripetuto per tutti i colori usati Dati sui Pixel totale byte = multiplo4(3 * W) * H Il totale di byte per riga = 3 * W deve essere arrotondato per eccesso ad un multiplo di 4: se ad esempio W = 5 pixel e H = 5 pixel, allora 3 * W = 15 e quindi multiplo4(3 * W) = 16, pertanto il totale sarà 16 * 5 = 80 byte. I byte aggiunti per arrotondare sono settati a Blue lbyte Intensità di Blu Green lbyte Intensità di Verde Red lbyte Intensità di Rosso ... ripetuto per tutti i pixel dell'immagine Tab. 2: Dettaglio della struttura di un file BMP. 32 ►►► F e b b http://www.itportal.it TECNICA x(G), x(B) le intensità, rispettivamente, delle sue componenti rossa, verde e blu. Queste in- tensità costituiscono le tre componenti di colo- re di ciascun pixel. Le trasformazioni puntuali consentono di ottenere delle nuove intensità y(R), y(G) e y(B) in funzione soltanto delle cor- rispondenti componenti x(R), x(G), x(B): 1) La trasformazione che produce il negativo di una immagine consiste nel calcolare il complemento a 255 dell'intensità di ciascu- no dei tre colori di base di tutti i pixel, se- condo la formula y = 255 - x La Fig. 2 evidenzia graficamente l'anda- mento lineare di questa trasformazione. y 255 \ 0^ 255 x Fig. 2: Inversione di una immagine. 2) Una trasformazione che consente di aggiu- stare in modo semplice la luminosità e il contrasto di una immagine è la trasforma- zione lineare delle intensità di colore Per aumentare la luminosità si deve aumen- tare il livello di intensità dei colori dei pixel, mentre per aumentare il contrasto si deve aumentare la differenza tra le intensità dei colori di pixel adiacenti. La formula è la seguente y = a * x + b dove i parametri a e b sono numeri reali che rappresentano, rispettivamente, il fattore di contrasto e la variazione di luminosità. Il y 255 o" b- j a / 255 x r contrasto aumenta per a > 1 e diminuisce per < a < 1, mentre la luminosità aumenta per b > e diminuisce per b < 0. I valori di y ottenuti da tale calcolo vengono ricondot- ti air intervallo .. 255, assegnando il valore agli y negativi e il valore 255 agli y mag- giori di 255. La Fig. 3 illustra il significato grafico dei parametri a e b, i quali rappre- sentano, rispettivamente, la pendenza della retta e la sua staccata rispetto air origine. 3) Quando i tre colori di base hanno uguale in- tensità, si ha una immagine a 256 livelli di grigio. Per separare nettamente le zone chiare dell'im- magine da quelle scure si può applicare la tra- sformazione di sogliatura (thresholding) che riconduce l'immagine a due soli livelli di gri- gio: nero e bianco. Lo scopo di questa trasfor- mazione è quello di segmentare l'immagine, ovvero di evidenziare la superficie degli ogget- ti raffigurati nella stessa. Essa risulta maggior- mente efficace nei casi in cui si ha un oggetto di colore omogeneo che campeggia su uno sfondo anch'esso di colore omogeneo. A causa del ru- more elettronico non ci sarà mai una perfetta distinzione tra l'oggetto e lo sfondo e quindi questa operazione sarà comunque sempre sog- getta ad un qualche errore. La tecnica della tra- sformazione di sogliatura è molto semplice: da- to un valore di soglia s, compreso tra e 255, si fanno diventare neri tutti i pixel il cui livello di grigio è inferiore a s e bianchi tutti gli altri: È y = per x < s ì È y = 255 per x >= s La Fig. 4 evidenzia graficamente tale comporta- mento. y 255 0^ s 255 x Fig. 3: Trasformazione lineare Fig. 4: Sogliatura. La scelta del valore di soglia s si basa sull'ana- lisi dell'istogramma delle frequenze dei livelli di grigio dell'immagine. La Fig. 5 riporta un esempio di tale istogram- ma; esso evidenzia il fatto che nell'immagine ci Visual Basic Fotolab Filtri Fotografici in Visual Basic L'istogramma dei livelli di grigio r& L'istogramma di una ■-J immagine a livelli di grigio è un strumento utile per caratterizzare l'immagine stessa. Esso rappresenta, per ognuno dei 256 livelli di grigio, il numero di pixel dell'im- magine che assumono tale livello; tali valori verranno poi divisi per il numero totale di pixel dell'immagine in modo da ottenere il diagramma delle frequenze relative dei livelli di grigio. http://www.itportal.it 3 ►►► 33 Visual Basic Fotolab Filtri Fotografici in Visual Basic La tecnica dei falsi colori rA Si tratta di una tec- -J nica che consente la segmentazione del- l'immagine, ovvero di evidenziare alcune zo- ne della stessa, carat- terizzate da una omo- geneità cromatica. Data una immagine a li- velli di grigio, si fissano degli intervalli di inten- sità di grigio e si asso- cia arbitrariamente a ciascuno di essi un di- verso colore: si tratta evidentemente di "falsi colori". Essi sono finalizzati a facilitare la lettura del- l'immagine, anche per- chè l'occhio umano non è in grado di distin- guere tutti i 256 livelli di grigio, ma soltanto un sessantina. Per scegliere tali inter- valli si farà uso del- l'istogramma dei livelli di grigio. Ad esempio: i pixel con intensità comprese tra 41 e 60 diventeranno verdi, quelli con inten- sità compresa tra 61 e 80 diventeranno rossi e tutti gli altri manter- ranno l'attuale intensi- tà di grigio. ^ K 20 40 60 80 100 120 140 100 180 200 220 24'J 255 Fig. 5: Istogramma dei livelli di grigio. sono due distinte aree: una più scura, di inten- sità compresa tra 30 e 70 e l'altra più chiara, di intensità compresa tra 110 e 150. Pertanto, in questo caso, la scelta del valore di soglia s = 100 consente di separare nettamente le due zone dell'immagine, facendo diventare neri tutti i pixel con intensità < 100 e bianchi tutti gli altri. Ovviamente qualsiasi valore di soglia venga scelto compreso nell'intervallo tra 70 e 110 darà il medesimo risultato di s = 100. L'esempio fornito illustra una situazione parti- colarmente favorevole, ma spesso non c'è una così netta distinzione tra le intensità di colore delle diverse aree dell'immagine e pertanto la scelta del valore di soglia pur se fatta con ocu- latezza non consentirà una netta evidenziazio- ne delle stesse. ESEMPIO A titolo esemplificativo si mostrano gli effetti delle suddette elaborazioni sull'immagine di Fig. 6a. Fig. 6a: Tre; Fig. 6b: Negativo; Fig. 6c: Rosso; Fig. 6d: Verde. Dell'immagine di partenza ne viene fatto il ne- gativo (Fig. 6b) e ne vengono estratti i colori primari (Fig. 6c, 6d, 6e). Essa viene poi conver- tita in scala di grigi (Fig. 6f), il cui istogramma dei livelli di grigio è proprio quello rappresen- tato in Fig. 5, a questo punto viene applicata la preannunciata operazione di sogliatura, ap- punto con valore di soglia pari a 100 (Fig. 6g). Fig. 6e: Blu; Fig. 6f: Grigio; Fig. 6g: Grigio Soglia 100; Fig. 6h: a 2b = 20. Ripartendo dalla Fig. 6a si applica una trasfor- mazione lineare per aumentarne il contrasto e la luminosità: la Fig. 6h mostra il risultato ap- plicando un fattore di contrasto pari a 2 e una variazione di luminosità pari a +20. CODIFICA IN VB La Fig. 7 mostra la finestra di tipo "Multiple Do- cumenta del programma FotoLab con all'interno due finestre "figlie" contenenti i controlli Pictu- reBox per visualizzare le foto coinvolte nelle elaborazioni. '; e F ì<: 'unti ale ; iltro Locale ^]n\x\ ^MM=^ ■-, (' ) / "3 4j ...... H : \ , '-'-^{~ ^ - ^ "~— D~ •h • — - Fig. 7: FotoLab Il programma applica il filtro prescelto alla foto corrente; per rendere corrente una foto si deve fare un click all'interno della stessa. FotoLab ge- stisce soltanto immagini BMP a 24 bit, ovvero a 16 milioni di colori, e qualora si trasformi una foto a colori in una foto a 256 livelli di grigio, per semplicità, essa viene considerata come un caso particolare di una foto a colori senza adot- tare il più opportuno formato BMP a 8 bit. Inoltre, avendo uno scopo didattico, il pro- gramma non si preoccupa troppo dell'efficien- za degli algoritmi, ne' recupera spazio in me- moria centrale quando si chiude la finestra as- sociata ad una foto. A livello globale viene di- chiarato un array di oggetti "foto", ciascuno dei quali avrà il nome della foto, il riferimento al Form su cui essa viene visualizzata, le dimen- sioni della stessa, l'offset dei pixel e gli array di byte contenenti l'intestazione del file e la ma- trice di pixel. Private Type tipoFoto NomeFoto As String Form Foto As frmFoto w As Long ' ampiezza foto w3 As Long ' ampiezza aggiustata h As Long ' altezza foto inizioPixel As Long ' offset headerQ As Byte ' header del file BMP g() As Byte ' matrice di pixel End Type Dim FotoQ As tipoFoto ' array di foto Dim nFoto As Integer numero di foto caricate nell'array 34+» F e b b http://www.itportal.it TECNICA Dim gCorrente As Integer ' indice della foto corrente La seguente subroutine consente di caricare una foto BMP letta da disco in una nuova com- ponente dell'array di foto. Il parametro Nome- Foto contiene il nome del file e il parametro Ori- gine vale per indicare che si deve leggere la foto da disco. La foto ha dimensione w * h pixel, quindi essa occuperà una matrice di Byte con h righe e w 3 = w * 3 colonne, dove w3 viene arro- tondato al valore multiplo di 4 immediatamen- te successivo, a causa delle specifiche del for- mato BMP. Private Sub CaricaFoto(ByVal NomeFoto As String, ByVal Origine as Integer) Dim w As Long Dim h As Long Dim pixelOffset As Long With Foto(gCorrente) ' gCorrente è l'indice della foto corrente Set .FormFoto = New frmFoto ' crea un nuovo Form per la foto .FormFoto.Caption = NomeFoto . FormFoto. Tag = CStr(gCorrente) ' associa il form alla foto corrente .Form Foto. Show .NomeFoto = NomeFoto If Origine = Then Set . FormFoto. picFoto.Picture = LoadPicture(NomeFoto) Open NomeFoto For Binary As #1 Get #1, 11, pixelOffset Get#l, 19, w ' Width Get #1, 23, h ' Height .w3 = multiplo4(w * 3) ' funzione che calcola il multiplo di 4 .h = h ■ inizioPixel = 1 + pixelOffset acquisizione header del file BMP ReDim .header(l To pixelOffset) acquisizione matrice di pixel ReDim .g(l To .w3, 1 To .h) notare che la matrice risulta trasposta: colonne, righe e che inoltre le righe sono invertite: dall'ultima alla prima Get #1, .inizioPixel, .g Close #1 Else la foto viene letta dall'array, dalla componente di indice Origine .w = Foto(Origine).w .w3 = Foto(Origine).w3 .h = Foto(Origine).h ■ inizioPixel = Foto(Origine).inizioPixel ■ header = Foto(Origine).header .g = Foto(Origine).g End If End With End Sub Per l'applicazione di filtri puntuali si veda il se- guente estratto di subroutine che applica la tra- sformazione in negativo della foto corrente. Si devono modificare tutte le componenti della matrice dei colori dei pixel. Private Sub FiltraFoto(ByVal TipoFiltro As String) Dim i As Long Dim j As Long Dim n As Long With Foto(gCorrente) n = 3 * .w For i = 1 To .h Step 1 1 = 1 Do While j <= n Select Case TipoFiltro Case "Negativo" ' evidentemente si trattano allo stesso modo ' le tre componenti componente blue ■ gli, i) = CByte(255 - .q(i, i)) 1 = 1 + 1 ' componente green ■ gli, i) = CByte(255 - .q(i, i)) j = j + 1 ' componente red ■ gli, i) = CByte(255 - .q(i, i)) 1=1 + 1 Case ... ' per gestire altri filtri End Select Loop Next i ' salvo la foto in un file temporaneo Open "temp.bmp" For Binary As #1 Put #1, , .header Put #1, Close #1 ' visualizzo il risultato Set .FormFoto. picFoto.Picture = LoadPictureCtemp.bmp") End With End Sub CONCLUSIONI Nella seconda parte di questo articolo verranno affrontate le trasformazioni locali dell'immagi- ne, le quali consentiranno di agire sul contrasto della stessa, di attenuarne il rumore elettronico ed anche di estrarre i contorni degli oggetti in essa rappresentati. Roberto Bandiera Visual Basic Fotolab Filtri Fotografici in Visual Basic Riferimenti r& Per approfondimenti -J e una ampia rasse- gna di tecniche di elabo- razione di immagini si visiti il sito del Diparti- mento di Intelligenza Ar- tificiale dell'Università di Edinburgo http://www.dai.ed.ac.uk /HIPR2/hipr top.htm Presso il Dipartimento di Computer Science della stessa università sono reperibili informazioni sui formati grafici http://www.dcs.ed.ac.Uk/h ome/mxr/qfx/2d-hi .ritmi Per una introduzione alla fotografia digitale si consulti il manuale on li- ne "Le Tecniche Fotogra- fiche In Archeologia" di Fausto Gabrielli del Di- partimento di Scienze Archeologiche - Univer- sità di Pisa http://www.arch.unipi.it /Foto Libro/Foto Libro.html http://www.itportal.it 3 ►►► 35 TECNICA Animazione IN JAVA 3D (parte terza) Dopo aver toccato i temi fondamentali per quanto riguarda la creazione dei nostri universi tridimensionali, possiamo ora addentrarci in quegli argomenti, più marginali, che sono utili per approfondire le nostre conoscenza delle librerie Java per lo sviluppo 3D. E quale tema è più interessante trattare se non l'animazione delle scene che abbiamo imparato a generare? Vediamo quindi come procedere, una volta create le scene che intendiamo animare, per fare in modo che esse risultino dotate di una qualche dinamicità. Innanzitutto occorre fare una importante premes- sa, che riguarda il modo in cui possono essere ge- nerate le animazioni. Esse infatti possono avere due diverse origini: una data dallo scorrere del tem- po, (nel qual caso si parla di animazioni), ed una inve- ce dall'interazione dell'utente con la scena, (denomi- nate appunto interazioni). Anche se la logica con la quale questi due differenti metodi funzionano è simi- lare, le classi sulle quali si appoggiano sono differen- ti, rendendo di fatto diverso il modo in cui è necessa- rio procedere per ognuno dei due comportamenti. Ma vediamo appunto come le API 3D ci vengono in aiu- to nella programmazione di scene dinamiche, illu- strando i passi fondamentali da compiere per arriva- re alla creazione di un esempio pienamente funzio- nante. ANIMAZIONE E INTERAZIONE Abbiamo detto che le due differenti forme di dinami- cità possibili, poggiano comunque sulle stesse basi, vediamone quindi i punti in comune. Ogni azione di- namica in Java3D si realizza utilizzando i Comporta- menti (Behaviors). Un Comportamento non è altro che una classe che si occupa di apportare delle modifiche ad un oggetto o alla scena stessa in risposta a partico- lari eventi che si sono scelti di intercettare. Occorre quindi, per poter realizzare una scena dinamica, crea- re dei Comportamenti che possano essere sfruttati per modificare il nostro universo. Come fare per creare un comportamento? I passi necessari sono pochi e sem- plici, infatti si deve: 1. Creare una classe che estenda la classe Behavior. 2. Fornire la classe di un costruttore che riceva l'og- getto da modificare. 3. Specificare una condizione di attivazione del Comportamento . 4. Specificare quali modifiche il comportamento de- ve apportare all'oggetto. 5. Specificare la prossima condizione di attivazione. J2SE 1.4 Fig. 1: Il parallelepipedo, protagonista della nostra scena. Anche se i passaggi non sono molti, è comunque uti- le sapere che vi sono numerose classi predefinite che si occupano di rispondere agli eventi più comuni, e che ci sollevano dal gravoso compito di dover scrive- re un Comportamento per ciascuna differente anima- zione o interazione. Vedremo più in là queste classi e come è possibile utilizzarle per i propri scopi, ma, per ora, dedichiamoci allo studio del funzionamento dei Comportamenti analizzando passo passo tutti i punti necessari per la realizzarne uno personalizzato. A tal scopo analizziamo il listato seguente: public class ComportamentoSemplice extends Behavior{ private Tra nsform Group oggetto; private Transform3D rotazione = new Transform3D(); private doublé angolo = 0.0; public ComportamentoSemplice(TransformGroup oggetto){ File Sul CD soft\codice\allegati3.zip Ó\ Navigazione Per navigare nel- l'universo che ab- biamo realizzato, occor- re utilizzare i seguenti tasti: Freccia su = zoom in; Freccia giù = zoom out; Freccia destra = sposta- mento a destra; Freccia sinistra = spo- stamento a sinistra; Pag. su = spostamento in alto; Pag. giù = spostamen- to in basso; Se non riuscite a muo- vervi nella scena, po- trebbe essere neces- sario fare click sulla fi- nestra dell'applicazione in modo da selezionarla come attiva. http://www.itportal.it 3 ►►► 37 this. oggetto = oggetto;} J2SE 1.4 Animazione IN JAVA 3 D Comportamenti r& Oltre a quelli visti —J nel listato di esem- pio, sono molte le classi che si occupano di fornire dei Comportamenti pre- definiti utili per i casi più semplici. Tra queste sicu- ramente le più utilizzate sono MouseBehavior, che serve per gestire oggetti tramite mouse, e tutte le sottoclassi di Interpola- tor, utili per modificare forma, colore, dimensio- ne e trasparenza di un solido. public void initialize(){ this.wakeupOn( new WakeupOnAWTEvent ( KeyEvent.KEY_PRESSED)); } public void processStimulus( Enumeration parametri ){ angolo += 0.1; rotazione. rotY(angolo); oggetto.setTransform(rotazione); this.wakeupOn( new WakeupOnAWTEvent ( KeyEvent.KEY_PRESSED));> } Come appare chiaro dal listato, implementare Com- portamenti personalizzati non è eccessivamente com- plicato anche se, nel caso di procedure più complesse, il codice può risultare sicuramente più impegnativo. Occorre premettere che questo comportamento fa in modo che un solido ad esso associato, una volta vi- sualizzata la scena, rimanga immobile finché l'utente preme un tasto. Quando un tasto viene premuto, esso inizia a roteare su se stesso intorno all'asse delle Y senza più fermarsi. Analizzando la dichiarazione del- la classe, la prima cosa da notare è che essa deriva dal- la classe Behavior, come specificato al punto 1 del pre- cedente elenco. Seguono poi una serie di proprietà private della classe tra cui distinguiamo oggetto che rappresenta l'oggetto che il comportamento andrà a modificare. Il costruttore della classe, che rappresenta il 2° punto della nostra scaletta, non fa altro che im- postare il valore di questa variabile. Arriviamo final- mente al primo dei metodi ereditati dalla classe Beha- vior, soprascritto per dotare la nostra classe di una qualche nuova funzionalità: initializeQ. Questo meto- do viene richiamato dal sistema in risposta ad un evento che corrisponde all'iniziazione del Comporta- mento, che si verifica quando una scena viene visua- lizzata. Il metodo posto all'interno della funzione, di nome wakeupOn(. . .), si utilizza per dichiarare al Com- portamento di rimanere in attesa della condizione specificata, esaudendo così il punto 3 del nostro elen- co. La condizione utilizzata nel nostro esempio altri non è che l'evento di pressione di un tasto. La funzio- ne successiva, processStimulus( . . .), è anch'essa eredita- ta da Behavior, ed è il metodo che viene richiamato quando si verifica la condizione di attivazione del Comportamento, cioè nel nostro caso la pressione di un tasto. In questo metodo è possibile implementare il punto 5 della nostra scaletta, visto che è proprio questa la funzione che modifica effettivamente le va- rie geometrie del solido associato al Comportamento. Le istruzioni che formano il corpo della funzione, so- no infatti le istruzioni di modifica dell'angolazione sull'asse Y del solido. Per finire, come nel punto 6 del- l'elenco, è necessario reimpostare una condizione di attivazione per fare in modo che il solido richiami questa stessa funzione al verificarsi di un nuovo even- to. Il metodo wakeupOn(. . .), infatti, termina la sua fun- zione dopo la prima volta e se non si imposta nuova- mente il Comportamento in attesa di un evento, esso terminerà di essere utilizzabile. Bene, il nostro com- portamento è pronto per essere utilizzato ma, per far- lo, è necessario definire ancora un paio di cose. In- nanzitutto affinché il Comportamento funzioni è ne- cessario applicargli una sfera di influenza, che corri- sponde alla distanza entro la quale il Comportamen- to viene attivato. In parole semplici, se siamo molto distanti da un oggetto che deve subire delle modifi- che, è probabile che dette modifiche non risultino vi- sibili all'utente ma, ciononostante, esse saranno state eseguite, portando via tempo di calcolo prezioso in un sistema di rendering in tempo reale. Per evitare questo spreco, è stato pensato un sistema di gestione delle animazioni che prende in considerazione soltan- to quelle la cui sfera di influenza racchiude la vista at- tualmente utilizzata dall'osservatore. Un altro impor- tante punto da rispettare affinché le animazioni che abbiamo pensato funzionino a dovere, è quello di im- postare il Trasformatore incaricato di modificare uno o più oggetti. A questo scopo occorre impostare per quest'ultimo la possibilità di essere modificato in uno o più modi. Ma vi sarà tutto più chiaro quando legge- remo il listato della nostra applicazione. IL NOSTRO ESEMPIO Questa volta, visto che ormai dovreste essere in grado di capire molti dei passaggi riportati, eviterò di com- mentare il superfluo, facendovi soffermare esclusiva- mente sui punti che formano l'argomento odierno. Ma prima di tutto analizziamo il codice che forma la nostra applicazione: import java.awt.*; import javax.swing.*; import javax.vecmath.*; import javax.media.j3d.*; import com.sun.j3d.utils. universe.*; import com.sun.j3d.utils.geometry.*; import com.sun.j3d.utils.behaviors.keyboard.*; public class Dinamicità extends JFrame{ public Dinamicita(){ super("Dinamicita"); setSize(800,600); Fig. 2: Visti da vicino. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 38^^ F e b b http://www.itportal.it TECNICA JPanel nuovoPannello = new JPanelQ; nuovoPannello.setLayout(new BorderLayoutQ); Canvas3D tela = new Canvas3D(null); nuovoPannello.add("Center",tela); Simplellniverse universo = new SimpleUniverse(tela); universo. getViewingPlatform() .setNominalViewingTransform(); BranchGroup gruppo = new BranchGroupQ; // Inseriamo qui il codice per costruire la nostra scena 3D /*** ANIMAZIONE ***/ // Iniziamo creando un oggetto che si occupi dello scorrere del tempo Alpha andatura = new Alpha(-1,50Q0); // Poi creiamo un'area entro la quale le animazioni hanno luogo BoundingSphere area = new BoundingSphereQ; // Ora costruiamo tre strutture che identifichino i colori primari... Color3f rosso = new Color3f(l.Qf,0.0f,0.0f); Color3f verde = new Color3f(0. 07,1-07,0 -00; Color3f blu = new Color3f(0.0f y 0.0f y 1.0f); // con i quali costruiamo poi un materiale... Material materiale = new Material(blu, rosso, verde, rosso, O.Of); //modificabile... materiale. setCapability( Material.ALLOW_COMPONENT_READ| Material.ALLOW_COMPONENT_WRITE); // che utilizziamo per costruire un'estetica. .. Appearance estetica = new AppearanceQ; estetica .setMaterial(materiale); // necessaria a creare infine il nostro solido. com.sun.j3d.utils.geometry.Box solido = new com.sun.j3d.utils.geometry.Box(0.2f,0.4f,0.3f,estetica); // Creiamo ora il trasformatore da utilizzare per le nostre animazioni... /*** ILLUMINAZIONE ***/ // Illuminamo adeguatamente la scena per renderla visibile // Ora impostiamo i comandi per rendere il nostro mondo navigabile /*** INTERAZIONE ***/ public static void main(String[] args){ new DinamicitaQ; } ANIMAZIONE Il listato di esempio riportato si occupa di creare un parallelepipedo applicandovi un materiale non pre- definito, nonché di animarlo, facendolo roteare su se stesso, e modificarne i valori del materiale associato in modo che sembri cambiare colore. Inoltre, al punto di vista utilizzato per generare la scena, viene applicato un Comportamento che rende possibile la navigazio- ne della stessa. Sarà quindi possibile, utilizzando dei tasti particolari, muoversi all'interno della scena grafica e gironzolare intorno al nostro parallelepipedo animato. Il codice fi- no al commento "Inseriamo qui il codice per costrui- re la nostra scena 3D" è stato più volte analizzato e serve a generare un'applicazione che contenga il no- stro universo. Subito dopo appare il primo dei blocchi di codice che ci interessa, quello relativo air animazio- ne della scena. La prima istruzione che troviamo crea un oggetto Alpha, che serve per essere associato a dei Comporta- menti che devono verificarsi con lo scorrere del di tempo. In realtà l'oggetto Alpha è molto potente e configurabile, e permette di stabilire esattamente: quando attivare i Comportamenti ad esso associati, quante volte farlo nel corso dell'applicazione e dopo che lasso di tempo. Quest'oggetto è uno dei più importanti per generare animazioni dovute al passaggio del tempo. Nella riga successiva troviamo un altro oggetto che dovremmo aver imparato a conoscere: BoundingSphere. Esso si oc- cupa di fornire un'area entro la quale tutti gli oggetti ad esso associati (e come potete vedere sono molti) compiano le azioni per la quali sono stati program- mati. Come infatti già spiegato, le animazioni e le in- terazioni, vengono calcolate esclusivamente se la loro sfera di influenza (BoundingSphere) risulta contenere anche il punto di vista utilizzato dall'utente per ren- derizzare la scena. Il costruttore di default utilizzato nel nostro esempio compie egregiamente il suo lavo- ro, impostando dei limiti più che sufficienti alle nostre esigenze. Successivamente II listato crea tre strutture dati che identificano i tre colori primari, rosso verde e blu, ne- cessari a creare un oggetto di tipo Materiale. Sin ora non ci siamo mai occupati della realizzazione di soli- di che avessero delle caratteristiche estetiche diverse da quelle predefinite ma, in questo caso, creeremo una figura che ha delle proprietà visive uniche. I pa- rametri passati per la creazione del Materiale, infatti, rappresentano come esso risponde visivamente quan- do interessato dalle sorgenti luminose. Ma a cosa ci serve? Come preannunciato, le animazioni di una scena gra- fica possono andare a modificare numerosi campi di un solido, tra cui anche il suo materiale. Ma per farlo, quest'ultimo deve essere predisposto ad accettare eventuali modifiche. Ed è proprio a questo che serve la riga di codice successiva, che fa in modo che il no- stro materiale risulti modificabile in lettura e scrittura. Una volta generato questo Materiale modificabile es- so viene utilizzato, nelle due linee successive, per creare un oggetto di tipo Appearance che rappresenta l'insieme delle specifiche estetiche di un solido. Oltre al materiale, infatti, esso può avere associate diverse caratteristiche (textures, trasparenze, etc.etc.) che ven- gono immagazzinate in un unico oggetto che funzio- na da contenitore per tutte le possibili caratteristiche estetiche di uno o più solidi. Nel nostro caso, l'unica particolarità dell'oggetto Appearance sarà nel materia- J2SE 1.4 Animazione IN JAVA 3D WAKE_UP_ EVENTS r% Molte sono anche le ^ condizioni che pos- sono essere implemen- tate nei Comportamenti per fare in modo che questi ultimi vengano at- tivati in un particolare momento. Si può appor- tare una modifica alla scena grafica ad esem- pio quando la vista del- l'utente entra nell'area di influenza del Compor- tamento, oppure in se- guito a delle collisioni, o in base ad un'evento ge- nerato dall'utente, etc. etc. http://www.itportal.it 3 ►►► 39 J2SE 1.4 Animazione IN JAVA 3D Bill board /% Esiste anche una tec- ^ nica, chiamata ap- punto Billboarding, che gestisce l'utilizzo di figu- re bidimensionali al posto di veri e propri solidi, per fare in modo di simulare un'universo solo a prima vista tridimensionale. Ja- va 3D supporta anche questa tecnica. le che abbiamo creato. Fatto ciò, non rimane altro da fare, nella successiva li- nea di codice, che generare il nostro solido con asso- ciato l'oggetto Appearance appena realizzato. Ora che abbiamo un oggetto visivo sul quale lavorare, il lista- to prosegue andando a creare un oggetto Transform- Group che ci servirà per compiere le nostre animazio- ni. Come introdotto nel primo articolo di questa serie, infatti, un qualsiasi oggetto da aggiungere alla scena grafica va aggiunto ad un Gruppo principale, da col- legare poi alla locale fornitaci dalla classe SimpleUni- verse che identifica il nostro universo. Ma se si pro- getta di compiere delle trasformazioni su uno o più oggetti grafici, allora è necessario introdurre tra il gruppo ed il solido un altro oggetto di tipo Tran- sformGroup, utile a realizzare la trasformazione. Per fare in modo che questo oggetto sia in grado di ap- portare delle modifiche alla scena anche dopo che questa sia stata compilata, si dono impostare delle ca- pacità al trasformatore così come fatto nella linea di codice successiva alla creazione dello stesso. Dopo questa istruzione il trasformatore sarà in grado di mo- dificare il o i solidi ad esso associati. Proseguendo nella lettura del listato, si notano i due successivi blocchi di codice compiere azioni molto si- mili. Essi altro non sono che dei Comportamenti pre- definiti di Java3D che noi utilizziamo per dichiarare in modo il nostro solido deve venire modificato. La classe Colorlnterpolator si occupa di modificare il materiale associato al nostro oggetto grafico, ed infat- ti in fase di costruzione dello stesso gli vengono pas- sati come parametri il materiale da modificare e l'og- getto Alpha che indica con che ritmo e per quante vol- te apportare la modifica. La classe Rotationlnterpolator, invece serve a genera- re un Comportamento di rotazione per qualsiasi og- getto le si associ. Come è possibile osservare dal codi- ce, l'oggetto passato al costruttore di Rotationlnterpo- lator, insieme al già visto oggetto Alpha anche qui ne- cessario, è il Trasformatore precedentemente creato ed al quale assoceremo successivamente il nostro so- lido. Se inserissimo più solidi nel nostro trasformatore, es- si subirebbero tutti la medesima animazione. Entram- bi i blocchi di codice continuano poi, dopo l'utilizzo del costruttore per generare due oggetti di tipo Com- portamento che compiono animazioni differenti, im- postando i limiti di influenza di quel Comportamen- to per fare in modo che esso risulti attivo soltanto en- tro una certa distanza dall'osservatore. Il passo finale poi consiste neir aggiungere i Comportamenti creati al Trasformatore che abbiamo creato, in modo che es- so possa utilizzarli dinamicamente. I due ultimi passaggi prima del blocco di codice dedi- cato all'illuminazione, poi, altro non fanno che inseri- re tra il Gruppo che rappresenta la nostra scena grafi- ca, ed solido che dobbiamo modificare, il Trasforma- tore, con tutti i vari Comportamenti associati, incari- cato della modifica. INTERAZIONE Prima di arrivare al blocco di codice relativo all'inte- razione, è necessario passare brevemente in rassegna le istruzioni che rendono possibile la corretta illumi- nazione della scena. Come già visto nel capitolo dedi- cato alle luci, le istruzioni in questo blocco creano due delle sorgenti di luce possibili in Java3D, una tenue luce ambientale ed una luce direzionale. Alle luci vie- ne infine assegnata un'area di influenza simile a que- la dei Comportamenti. Finalmente arriviamo al gruppo di istruzioni che ren- dono possibile la navigazione del nostro universo tri- dimensionale. Come potete osservare, il listato è pres- soché identico a quello degli altri due Comportamen- ti utilizzati in precedenza, ad eccezione del fatto che qui non è necessario fornire un oggetto Alpha che si occupi di innescare gli eventi che modifichino gli at- tributi del solido. L' inutilità di questo oggetto è data dal fatto che non più lo scorrere del tempo, quanto in- vece l'input dell'utente, serve da stimolo al Compor- tamento per essere innescato. Ma vediamo nel detta- glio il funzionamento. Innanzitutto, poiché ciò che in- tendiamo modificare è la posizione del punto di vista dell'utente, occorre recuperare questo oggetto rac- chiuso all'interno della classe SimpleUniverse. Di que- sto si occupa la prima linea di codice del blocco dedi- cato all'interazione. Successivamente, altro non serve che procedere come visto prima per gli altri due Com- portamenti, e cioè costruendo un oggetto di tipo Comportamento che compia le azioni che ci interes- sano, impostarne i limiti di influenza ed aggiungen- dolo alla scena. Le uniche differenze qui sono date dall'assenza dell'oggetto Alpha e dall' aver aggiunto il Comportamento non al Trasformatore, poiché esso deve occuparsi solo della modifica del solido, bensì all'oggetto che lo contiene, cioè il Gruppo che andre- mo poi ad aggiungere all'universo. Fatto ciò non resta che terminare il programma come fatto più volte, as- sociando il gruppo all'universo, compilando e mo- strando il tutto. CONCLUSIONI Gli argomenti trattati oggi sono veramente solo una breve introduzione su un argomento vastissimo che meriterebbe un libro intero. E infatti possibile, utiliz- zando animazioni ed interazione, arrivare a risultati incredibili in un prodotto immediato e potente come Java3D. Gli strumenti presenti nelle API permettono infatti profonde manipolazioni, effetti di morphing, sostituzioni di oggetti, textures, etc.etc. Starà a voi, al- la vostra fantasia e voglia di sperimentare, arrivare a generare ambienti tridimensionali animati ed intera- gibili che possano essere utilizzati nelle vostre appli- cazioni. A me non resta che augurarvi di riuscire sem- pre ad orientarvi in un mondo affascinante e com- plesso come quello della programmazione grafica tri- dimensionale. Giuliano Uboldi 40 ►►► F e b b r a i o 2 3 http://www.itportal.it Biblioteca ON LINE Flash MX - Tutto & Oltre DevShed Un sito interamente dedi- cato alla comunità degli sviluppatori. Si distingue per i frequentatissimi fo- rum messi a disposizione (ASP, XML, Perl, Visual Basic, Flash, C++, ecc, ecc). [-■ |De> E reloperShed *fS<-^— wcT" ^"^ , &T"— i ■ui^wjy™ La collana Tutto & oltre di Apogeo, punto di riferimento degli utenti avanzati e professionali, si pregia di un nuovo testo dedicato al linguaggio Flash, vera rivoluzione per il supporto allo sviluppo di pagine Web. Il testo mostra passo passo come sfruttare al meglio il più potente programma di animazione vettoriale che ha rivoluzionato il modo di progettare e creare pagine Web. Ricco di illustrazioni, esempi e dimostrazioni pratiche a cura di esperti del settore, è sicuramente un libro che non può mancare nella biblioteca di chi desidera costruire lavori di qualità. Tra gli argomenti trattati: • Introduzione all'uso di Flash • I nuovi strumenti di disegno • Contenuto caricato dinamicamente nei progetti Web • Utilizzo di Action Script e dei componenti precostituiti • Effetti dinamici, audio MP3, giochi interattivi in Flash http://www.devshed.com/ DevBuzz DevBuzz.COM è un sito dedicato a chi si cimenta giorno per giorno nella programmazione dei Poc- ket PC. Mette a disposi- zione una vasta serie di tool, tecniche di pro- grammazione e soluzioni a svariate tipologie di problemi. .NET 247 Per dirla così come si auto-sponsorizza il sito: il primo sito dedicato al fra- mework .NET, interamen- te indipendente. .NET problem? .NET answer. http://www.dotnet247.com/ 247reference/default.aspx Nel CD-ROM allegato: software in versione trial e shareware, aggiunte, plug-in, modelli e file degli esempi trattati nel testo. Difficoltà: Medio-Alta • Autori: Reinhardt Robert, Dowd Snow • Editore: Apogeo http://www.apoqeonline.com • ISBN: 88-503-2070-1 • Anno di pubblicazione: 2002 Lingua: Italiano • Pagine: 1048 • Prezzo: € 55,00 • Contiene 1 CD-Rom Beginning database with MySQL MySQL rappresenta il prodotto più popolare tra gli RDBMS Open Source, rinomato soprattutto per velocità e potenza di elaborazione. Il testo si propone come guida di riferimento alla scoperta del database, analizzandone, approfonditamente, tutte le funzioni più importanti. Un tour alla scoperta di MySQL, che guida il lettore, passo passo, dall' installazione alla sua configurazione, all'esecuzione di comandi, semplici e complessi, sia per T amministrazione del sistema che per il suo utilizzo pratico. Molti esempi mostrano come integrare la potenza di MySQL all'interno dei più comuni linguaggi di programmazione orientati al Web. Difficoltà: Medio-Alta • Autori: Richard Stones, Neil Matthew • Editore: WROX http://www.qorilla.it • ISBN: 1-861006-92-6 • Anno di pubblicazione: 2002 • Lingua: Inglese Pagine: 608 • Prezzo: $39,99 Building an ASP.NET Intranet e ■&£&! Building An ASP.NET Intranet Il libro giusto per tutti coloro che vogliono apprendere i concetti di base per lo sviluppo di applicazioni Intranet, utilizzando le nuove funzionalità messe a disposizione dalla tecnologia ASP.NET. In questo testo gli autori hanno posto T accento più sull'aspetto pratico che teorico, mostrando, di fatto, un esempio reale di applicazione, nella fattispecie IBuySpy Portai. • Qual è la differenza tra un'applicazione Intranet e un'applicazione Internet? • Quali i requisiti per creare un'applicazione Intranet? • Come gestire la sicurezza di un'applicazione Intranet? Queste alcune delle domande che trovano risposta nel testo. Difficoltà: Medio-Alta • Autori: Vari • Editore: WROX http://www.qorilla.it • ISBN: 1-861007-49-3 Anno di pubblicazione: 2002 • Lingua: Inglese • Pagine: 480 • Prezzo: $49,99 http: //www. itportal.it 3 ►►► 41 Tips&Tricks I trucchi del mestiere La rubrica raccoglie trucchi e piccoli pezzi di codice che solitamente non trovano posto nei manuali, ma sono frutto dell'esperienza di chi programma. Alcuni trucchi sono proposti dalla Redazione, altri provengono da una ricerca sulla Rete delle Reti, altri ancora ci giungono dai lettori. Chi vuole contribuire potrà inviarci i suoi tips&tricks preferiti che, una volta scelti, verranno pubblicati nella rubrica. Il codice completo dei tips lo trovate nel CD allegato nella directory \tips\. ► Visual Basic.NET ► ►►►►►► Come sfruttare l'ereditarierà visuale in Visual Basic .NET Visual Basic ha sempre avuto come punto di forza una estrema semplicità nella costruzione delle interfacce utente, il punto in cui pagava dazio ad altri linguaggi come C++ e Java era lo scarso supporto alle più avanzate funzionalità Object Oriented. Questo gap è stato ampiamente superato con Visual Basic.NET. Nell'esempio che andiamo ad illustrare vedremo proprio come sfruttare l'ereditarietà nelle interfacce utente. Supponiamo di voler distribuire un'ap- plicazione in cui tutte le form presen- tino il marchietto dell'azienda com- mittente in alto a destra. Grazie alla eredita- rietà potremo creare una volta per tutte la struttura base della form ed ereditarla in tutte le altre. Inoltre, se ci troveremo a di- stribuire la stessa applicazione per un'altra azienda, potremo cambiare il marchio stes- so o la sua posizione, ottenendo che al mo- difica si ripercuota a cascata in tutte le form. CREIAMO LA CLASSE BASE Ricordando che anche una form altro non è che una classe, apriamo un nuovo progetto in Visual Studio .NET e creiamo una nuova libreria di classi: avere qui la nostra forma Categorie - 1 El.iN.n" •->.! i-'.'jj.i ' •_ L».jfc si Interface ■m Codice _| Dati mm m § arni Controllo Form per applicano Nome: J ioForrn.vb base ci consentirà di aggiungerla successi- vamente in qualsiasi applicazione sviluppa- ta in seguito. Chiameremo questa libreria LibreriaForm. In questa libreria andremo ad aggiungere una nuova form, chiamiamola ioForm (Fig. 1). 1 Y-'Ìbogrammo a Fig. 1: Aggiunta di un nuovo elemento. Fig. 2: Interfaccia dell'applicazione. Per aggiungere il logo utilizziamo una Pic- tureBox, andando a indicare il percorso in cui si trova l'immagine relativa. Posizionia- mo la PictureBox in alto a destra e andiamo a individuare fra le proprietà quella relativa all'ancoraggio. Per default, l'ancoraggio è fissato in alto a sinistra, noi lo sposteremo in altro a destra. Questo accorgimento consen- tirà di tenerlo nell'angolo, alla corretta di- stanza dai bordi, qualsiasi sia la dimensione della finestra (Fig. 2). Quando in seguito de- riveremo una form dalla form base, tutti i M ROGRAMMO : ìriaForm" (p i - /p LibreriaForm +■■ |S| References 3 Assemblylnfo.vb S» Esplora soluzioni | ^ Cerca | Fig. 3: Modifier controlli che abbiamo sistemato nella form di libreria saranno presenti anche nella form derivata. Le proprietà di questi con- trolli non potranno essere modificate se i corrispondenti controlli nella form base hanno la proprietà Modifier impostata a Pri- vate o Friend. Per lasciare alle classi derivate la possibilità di modificare le caratteristiche dei controlli è dunque necessario impostare la proprietà Modifier dei controlli a Public oppure Protected. Andiamo ad operare in questo modo sul controllo relativo al logo. x msmummamna^m KE1 \*\ ifii e :. ili 1 [Hill I H il II -fi-I Ufi 11 II-! Il fi- snHJuasa m Rigenera _J Aggiungi nuovo elemento. . . Aggiungi gemente esistei ite .. ' i Nuova cartella y m X jferirnento... rimento Web... Aggiungi Windows Form , . . tome progetto -^ Aggiungi form ereditato.,. . Jd] Aggiungi controllo utente,.. ^ jj] Aggiungi controllo ereditato. , . estLibreria Aggiungi so jsione e controllo de codice sorgente . Aggiungi componente. , . Aggiungi modulo,.. %$ Aggiungi classe.., Incolla Rimuovi Rinomina Proprietà Fig. 4: AggiungiFormDerivata CREIAMO LA FORM DERIVATA Prima di poter sfruttare la libreria a cui stia- mo lavorando, è necessario compilare il pro- getto. E' bene tenere presente che, al fine di rendere effettive le modifiche, è necessario ricompilare il progetto ogni volta che andia- mo a modificare la form base. Per provare la libreria appena creata, imposteremo un nuo- vo progetto ma, ovviamente, la nostra Libre- 42»*> F e b b http: //www. itportal.it R I riaForm può essere utilizzata da qualsiasi progetto .NET, anche già esistente. Il nuovo progetto lo chiameremo TestLibreria e lo creiamo come applicazione VB.NET. Andia- mo dunque ad aggiungere una form eredi- tata, cliccando con il tasto destro sul nodo TestLibreria di Esplora Soluzioni. Se la form da cui vogliamo derivare si trova in un proget- to diverso da quello corrente, dovremo spe- cificare il percorso in cui si trova la DLL re- lativa alla classe base: nel nostro caso XLibre- riaForm\bin. Così facendo avremo nel nostro proget- to una form del Fig. 5: Notate la tutto identica a piccola freccia in alto a sinistra. q uella presente in 1M ROGRAMMO libreria. Se guardate con attenzione il con- trollo relativo al logo, potete notare una pic- cola freccia in alto a sinistra. La freccia sta a indicare che il controllo è stato ereditato. MODIFICHE Grazie al fatto che abbiamo dichiarato pu- blic la proprietà Modifier della classe base, possiamo aggiornare l'aspetto ed il compor- tamento della classe derivata. Proviamo, ad esempio, a cambiare la posizione del logo, spostandolo neir angolo in alto a sinistra e modificando la relativa proprietà di anco- raggio con 'Top, Left". A questo punto pos- siamo anche aggiornare il logo con uno più recente, modificando la proprietà Image... □ ^^^^^^™fc D È :::::::::::::::::::::::::::::::::::::::::::::::::::::::: | Fig. 6: Risultato finale. Nella figura trovate il risultato dei nostri sforzi: l'ereditarietà visuale è servita! Sul CD potete trovare i progetti in Visual Studio .NET relativi all'esempio pre- sentato. ► C + + ► ►►►►►► ► ► ► ► stamento ed il ridimensionamento delle finestre. « Come risolvere equazioni di secondo grado Una piccola applicazione che si occupa di risolvere equazioni qua- dratiche del tipo Ax 2 + Bx + C = e che implementa due funzioni specifiche: numRoots(a, b, e) e quadSolve(a, b, e); la prima determina il numero di radici, mentre la seconda si occupa di trovare le radici. Come leggere un file riga per riga Utilizzando le Standard Template Library, questo piccolo codice con- sente di accedere ad un file e leggerlo riga per riga. Per immagazzi- nare i dati così ottenuti viene utilizzato un vettore: ogni riga è me- morizzata in una cella di questo vettore. Come calcolare il numero di nepero Utilizzando le serie di Taylor, questo piccolo pezzo di codice si occu- pa di calcolare il numero di Nepero. Non particolarmente veloce, ha nella semplicità la sua forza. Come ridurre lo sfarfallio durante il ridimensionamento delle finestre Come tutti sanno, il messaggio WM_PAINT è inviato ad una finestra ogni qual volta si rende necessario ridisegnarla. Dunque, general- mente, scriviamo tutte le istruzioni relative al layout della finestra nell'handler di WM_PAINT, incluse le istruzione relative allo sfondo. Quello che accade in realtà è che prima di WM_PAINT è inviato il messaggio WM_ERASEBKGND, la cui implementazione di default cancella l'area corrispondente alla finestra con un rettangolo bianco. Per migliorare l'efficienza può essere utile individuare gli oggetti che non cambiano sullo schermo, il codice che si occupa di disegnare questi oggetti può essere dunque spostato dall'handler di WM_PAINT a quello di WM_ERASEBKGND, avendo l'accortezza di ritornare un valore diverso da zero per indicare che non è più ne- cessaria la cancellazione riferita a quell'oggetto. Con questa soluzione si riduce la complessità delle operazioni di di- segno di una finestra e si elimina l'effetto di sfarfallio durante lo spo- ► Java ► ► ► ► ► ► ►►►►►► Come assegnare la dimensione di un array a run-time Grazie al fatto che in Java un array non è altro che un oggetto, la sua dimensione è trattata come una proprietà dell'oggetto stesso e può dunque essere assegnata a run time. Questa caratteristica diviene di fondamentale importanza in tutti i casi in cui abbiamo bisogno di un array per immagazzinare dei dati ma non possiamo conoscere la di- mensione esatta prima di eseguire l'applicazione. Come implementare una funzione di search&replace Una classe semplice ed utile che consente di effettuare la classica funzione di "trova e sostituisci" all'interno di una stringa. Il metodo riportato sul CD accetta tre parametri: la stringa su cui effettuare la ricerca, la stringa di caratteri da cercare e la stringa di caratteri che andrà a sostituire quella originale. Come semplificare l'utilizzo degli switch I programmatori C++ sono abituati ad analizzare gli argomenti pas- sati dalla linea di comando attraverso i parametri "ini arge" e "char *argv[]" della funzione maini). Un utilizzo del tutto simile dei para- metri è possibile attuarlo in Java per impostare le opzioni di avvio di un'applicazione. La classe presente sul CD rappresenta un utile fra- mework su cui basare una propria implementazione degli switch. Come caricare una classe da un file Jar Una semplice, benché completa, implementazione di un class loader per recuperare delle classi java da un file JAR durante l'esecuzione di un'applicazione. Il codice allegato, ampiamente commentato, può essere facilmente personalizzato al fine di essere integrato nei nostri progetti. http: //www. itportal.it 3 ►►► 43 T I ► V s u a I B a s ► ► ► ► &s Come sfumare lo sfondo Con il codice qui presentato avremo a disposizione un metodo sem- plice ed efficace per ottenere il classico effetto di sfumatura dello sfondo tipico, ad esempio, di molte applicazioni di installazione. La sfumatura andrà a schiarirsi dal basso verso l'alto, mentre la scelta del colore base si effettua attraverso la combinazione dei tra para- metri booleani accettati dalla funzione (rosso, verde e blu). Ad esem- pio, per avere la sfumatura blu come sfondo è sufficiente scrivere il seguente codice nella procedura Form_Load: FadeForm Me, False, False, True Come mostrare i font durante la selezione La prima cosa da fare è popolare la ComboBox con i nomi dei font disponibili nel sistema, per fare ciò è sufficiente aggiungere il se- guente codice a Form_Load() For I = To Screen.FontCount - 1 ' Put each font into list box. cboFont.Addltem Screen.Fonts(I) Next I Non resta che gestire l'evento Click aggiungendo il codice che impo- sta il font del ComboBox: Private Sub cboFont_Click() 'Set the FontName of the combo box 'to the font that was selected. cboFont. FontName = cboFont.Text End Sub Come salvare la dimensione e la posizione di una Form Molti di voi avranno notato che, all'avvio, alcune applicazioni "ri- cordano" la posizione e la dimensione in cui si trovavano al mo- mento dell' ultimo utilizzo. In questo tip spieghiamo come realizza- re lo stesso effetto in un'applicazione Visual Basic. In un modulo del- l'applicazione specifichiamo una costante di questo tipo: Public Const ApplicationName = "Nome Applicazione" Questo nome serve a distinguere l'applicazione nel registro di Win- dows. Nell'evento Form_Load richiamiamo la funzione di recupero delle impostazione, passando come argomento il riferimento alla form corrente: Cali LoadFormDisplaySettings(Me) Mentre nell'evento Form_Unload richiamiamo la funzione di salva- taggio delle impostazioni: Cali SaveFormDisplaySettings(Me) Da notare che come effetto indesiderato di questo metodo si ha che nel registro di Windows restano le impostazioni che abbiamo defini- to anche dopo che l'applicazione viene disinstallata. Se si vuole in- tervenire su questo aspetto, le informazioni si trovano in: HKEY_CURRENT_USER\Software\VB and VBA Program Settings\My Application Name\. Nel CD trovate il codice delle due funzioni di salvataggio e ripristi- no delle impostazioni, codice che va inserito in un modulo dell'ap- plicazione. Come controllare l'input Capita spesso di utilizzare dei textbox per effettuare l'input di dati di tipo numerico. Per accertarsi che l'utente non digiti caratteri alfabe- tici è possibile effettuare un controllo a posteriori rispetto all'immis- sione, anche se sarebbe preferibile impedire direttamente in fase di digitazione l'immissione di caratteri diversi da quelli numerici: pro- prio quello che consente il codice qui presentato. ► D e I p h ► ►►►►►► Come nascondere l'applicazione nella task bar Per alcune applicazioni può essere utile inibire la visualizzazione del pulsante relativo sulla task bar. Per ottenere questo è sufficiente im- postare la proprietà ShowMainForm di Application a False. In Win- dows 2000 e in Windows XP l'applicazione continuerà a essere visi- bile tra i processi presenti nel Task Manager: proprio dal task mana- ger sarà possibile fermare l'applicazione, fare in modo che l'applica- zione giri in modalità nascosta, la rende assimilabile ad un servizio. Come utilizzare tipi enumerateci come variabili di controllo in un loop Una caratteristica interessante de cicli for. ..do è quella di poter usare tipi enumerated al posto dei più comuni interi. Utilizzando questa proprietà possiamo incrementare notevolmente il livello di leggibi- lità del nostro codice, così com'è possibile notare nella porzione di codice riportata sul CD. Come visualizzare informazioni relative alla occupazione di memoria Il box "About" standard ingloba alcune interessanti informazioni sta- tistiche sulla memoria. In particolare, attraverso il metodo Physica- lAvMemLbl è possibile conoscere la quantità di memoria complessi- vamente disponibile, mentre con MemorylnUseLbl si può ottenere la percentuale di memoria fisica occupata. Come impostare le proprietà comuni a più controlli Capita spesso di dover settare la medesima proprietà su più control- li: un caso tipico è la proprietà visible o anche enabled nelle interfac- ce. La procedura allegata al CD si occupa di impostate la proprietà visible di un gruppo di oggetti TControl. La stessa procedura può es- sere facilmente modificata ed estesa per accedere ad altre proprierà degli oggetti: SetVisibleProp([NewBtn,OpenBtn,SaveBtn],UserState <> usNotAuthorized); 44 ►►► F e b b http: //www. itportal.it Torneo C R o b o t s Il Torneo DI CROBOTS 2K2 E dodici. Tanti sono gli anni che ci separano dal primo torneo di crobots, tenutosi nell'orma! lontano 1991, quando la potenza dei computer si misurava in Mhz e l'interfaccia grafica sui P.C. era un lusso che pochi potevano permettersi. Eppure, a dispetto del tempo trascorso, l'attività di assemblare mostriciattoli in Ansi C non smette di attirare nuovi adepti... "onostante il numero dei robot inviati annualmente si mantenga sempre più o meno costante, la scomposizio- ne per autore del parco combattenti mostra caratteristi- che sorprendenti: accanto a quei sei o sette veterani, che non saltano una competizione da tempi immemorabili, troviamo, infatti, di volta in volta nomi nuovi che pren- dono il posto di quanti, per motivi di tempo o di calo di interesse non partecipano più alla manifestazione. Nu- merosi e graditissimi sono anche i ritorni: autori che, dopo una pausa di riflessione, si ripresentano ai nastri di partenza per sfidare nuovamente la sorte, sia per ani- mati propositi di vittoria, sia per dire, semplicemente, 'eccomi, sono tornato'. Spulciando l'elenco degli autori dei 54 programmi che quest'anno si sono confrontati (per la verità sarebbero di più, ma alcuni sono stati in- viati a manifestazione oramai conclusa, un vero pecca- to), cercando di aggiudicarsi il premio messo in palio da ioProgrammo, ritroviamo con piacere Marco e Luca Pranzo, Tommaso de Prà e Fabio Luciano (confesso di non sapere quale sia il nome), un concorrente che non si vedeva dal lontano 1994. Tra le defezioni segnalo, per l'importanza dell'autore, quella di Dario Serino. A cau- sa degli esami universitari non ha potuto essere dei no- stri, ma ha promesso un ritorno in grande stile per il prossimo anno. Con lui in campo, forse, le cose sareb- bero andate diversamente. Assai gradita, giunge poi la conferma di Franco Cartieri che, con Copter2 e Kyash_2 ribadisce in tutte le competizioni gli ottimi piazzamen- ti della scorsa stagione. Numerose le matricole, tra le quali si segnala senza dubbio la notevole performance di Antonio Barone che, con il robot TheSlayer, caccia molti veterani, tra i quali il sottoscritto, dalle posizioni nobili della classifica: a una prima analisi il suo pro- gramma appare lineare ed efficiente. Senza dubbio fornirà nuovi spunti per la prossima edi- zione. I MICROROBOT Anche quest'anno, come già era accaduto per il prece- dente evento, la maggior parte dei partecipanti ha, de- ciso di iscrivere un combattente solo a questa categoria: ben 23 dei 54 sfidanti, infatti, erano al di sotto delle 500 istruzioni. Il girone ha salutato la vittoria di regis.r (Da- niele Nuzzo) nella modalità flf e di tigre.r (Alessandro Carlin) per la specialità 4vs4. Il calcolo combinato delle efficienze che, lo ricordo, attribuisce un rapporto di 5 a 1 ai pesi delle due competizioni, ha decretato l'afferma- zione finale del piccolo appartenente alla scuderia di Daniele. IL TORNEO 'CLASSICO' A questo punto, i microbi, uniti ai 16 mini-robot perve- nuti, si sono affrontati per decidere il vincitore nella ca- tegoria del crobots 'storico'. Per dovere di cronaca va detto che questi classicbots poco hanno a che spartire con i combattenti scritti nell'epoca delle 1000 istruzioni effetti- ve. Molti di loro, anzi, so- no tranquillamente in gra- do di battere persino i macro robot del torneo 2001. Qui la superiorità del microcampione è stata addirittura imbarazzante: bruenor.r ha dominato il primo girone, mentre regis.r si è comportato in maniera superlati- va anche in presenza di avversari più cor- pulenti, andando a vincere in solitudine il proprio round. La fi- nale è stata a senso uni- co, con Bruenor in grado di infliggere un distacco di ben 5 punti percentuali a Enigma.r, di Ales- sandro Carlin (ancora lui!). Torneo CRobots 2k2 http://www.itportal.it 2 3 ►►► 45 Torneo CRobots 2k2 Ó Spunti tecnici Archiviata la pra- tica del 2002 è iniziato il confronto semi-ufficiale teso a trovare i 32 robot più forti mai scritti, contro i quali allenare i propri sfidanti alla prossima competizione. Mentre scrivo il torneo è anco- ra in corso di svolgi- mento: potrete trova- re i risultati sul sito ww, itportal . it/crobots e su go.to/crobots Il 2002 termina con un verdetto meno con- traddittorio di quello fornito dalla passata edizione: si assiste, in- fatti, alla convergenza delle tattiche vincenti tra microbi, classici e macrorobot. IL TORNEO 2K2 Finalmente si fa sul serio: i primi tre giorni di combat- timenti sono stati poco più che un prologo alla manife- stazione principale. La domanda aleggia nell'aria: riu- scirà qualcuno ad opporsi allo strapotere Nuzziano? La fase eliminatoria incorona vincitori Drizzt.r (Danie- le Nuzzo), Yerba.r e MoveOn.r (di Michelangelo Messi- na). Quest'ultimo si è così confermato il dominatore delle qualifiche: oramai da anni non perde un colpo! Fermo restando che i primi otto qualificati di ogni round hanno passato direttamente il turno, i piazzati dal nono al sedicesimo posto hanno poi dato vita al ri- pescaggio, che ha fornito i nomi degli 8 robot mancan- ti alla lista dei 32 finalisti. Finalmente, può prendere il via lo scontro decisivo. Scontata l'affermazione neir/2/ di Wulfgar, con uno stratosferico 84%. I migliori tra gli altri, Zorn.r, Drizzt.r e Obelix.r si fermano intorno al 74%. Inizia l'ultima battaglia. Drizzi si porta subito al comando e, durante il primo 10% del torneo è abbon- dantemente avanti a tutti, arrivando ad accumulare un vantaggio intorno al 2% su Zorn. È praticamente cam- pione. Ma a questo punto succede l'incredibile: Zorn inizia una lenta ma costante risalita. Al 25% è dietro so- Pos. Nome del Crobot Eff . 4vs4 Eff. f2f Eff. Totale 1 Zorn 43,79 73,16 48,69 2 Drizzt 41,50 74,48 47,00 3 Rudolf_7 41,51 64,65 45,37 4 Wulfgar 38,15 80,57 45,22 5 Bruenor 36,93 67,23 41,98 6 Yerba 36,35 64,58 41,06 7 MoveOn 35,27 61,50 39,64 8 Regis 31,52 56,80 35,73 9 Enigma 29,46 60,34 34,61 10 TheSlayer 29,35 53,71 33,41 11 Obelix 24,35 70,08 31,97 12 Todos 25,77 51,10 29,99 13 Asterix 24,06 59,45 29,96 14 MedioMan 24,19 50,23 28,53 15 Doom2099 22,09 58,58 28,17 16 Tomahawk 23,41 48,83 27,65 17 Pippo2a 25,36 35,20 27,00 18 Jedi5 21,03 53,26 26,40 19 Padawan 15,84 53,35 22,09 20 Tigre 19,28 34,91 21,89 21 Mind 16,05 49,36 21,60 22 Remus 16,09 48,35 21,47 23 Colosseum 19,37 30,87 21,29 24 Idefix 19,10 28,91 20,74 25 Ska 15,06 39,10 19,07 26 Supernov 19,30 17,38 18,98 27 Stanlio 16,20 24,77 17,63 28 Mazinga 14,12 27,33 16,32 29 Kyash_2 7,56 58,16 15,99 30 Harpo 14,14 22,63 15,56 31 Romulus 11,13 32,71 14,73 32 Dynamite 7,00 23,69 9,78 Tab. 1: La classifica del torneo 2K2. lo di un quarto di punto. Al 30% è davanti, seppur di poco. Dal 40% in avanti il vantaggio si stabilizza intor- no al 2%. E siamo, infine al 100%. Il calcolo combinato delle efficienze decreta che il campione sia, ancora una volta, e per la seconda volta consecutiva, Alessandro Carlin (che si aggiudica in questo modo la microcame- ra messa in palio dalla Logitech). Secondo complessivo è Drizzt, che però, nel solo 4vs4, subisce l'onta di essere superato anche da Rudolf?, en- nesimo esponente della numerosa famiglia di Alessan- dro. Anche quest'anno impressionanti sono state le performance mostrate dai microrobot nella competi- zione generale. Daniele trova una parziale consolazio- ne piazzando regis.r all'ottavo posto complessivo, mi- gliore tra gli under 500, e bruenor. r al quinto, primo tra gli under 1000. Ma lasciamo ora la parola al campione 2002, che, per inciso, strappa a Daniele anche un altro primato: da quest'anno, infatti, i bicampioni sono ben due. "Innanzitutto devo dire che sono molto soddisfatto per il successo che ancora una volta ha avuto il torneo e per la sorprendente partecipazione di un congruo numero di matricole che si è unito ai tanti veterani e ad alcuni gra- diti ritorni. Un torneo (seguito in diretta su internet gra- zie all' eff icenza di Simone) che anche quest'anno si è ri- velato com-battutissimo e incerto e nel quale non sono mancate le sorprese. Ad esempio sono rimasto stupito per la notevole per- formance della matricola Theslayer che, all'esordio, si piazza nella top ten con un codice molto originale e sem- plice, e dalla straordinaria efficienza di molti robots di piccola taglia capaci di dimostrarsi competitivi con av- versari di taglia superiore (primo su tutti Regis), a di- mostrazione del fatto che i margini di miglioramento ga- rantiti dalle 2000 istruzioni sono ancora in gran parte da sfruttare. Venendo a Zorn direi che il suo movimento ba- se si può descrivere come una fortunata fusione di Fizban e Rudolf _6 che ottimamente si erano comportati l'anno scorso. Infatti il movimento a "quadrato" del primo è stato trasformato in un rettangolo che, di volta in volta, porta Zorn ad avvicinarsi, e quindi ad attaccare, uno dei robots più vicini. Per quanto riguarda gli scontri 1 con- tro 1, si tratta di un upgrade della stessa routine di Ru- dolf _6 con un movimento oscillatorio al centro dell'are- na, che in Zorn è però unito ad un movimento rettilineo Nord-Sud. Questa scelta è stata dettata dalla complementarietà del- le due soluzioni: a seconda della situazione Zorn utilizza una routine o l'altra cercando di adattarsi all'avversario che ha di fronte. Un importante aiuto mi è stato dato dal debugger, novità del 2002, che permette di ottimizzare le temporizzazioni del robot. Sono così riuscito a evitare i tempi morti dovuti alla ricarica del cannone e a mante- nere sempre una velocità elevata nei cambi di direzione. Non mi rimane che dare appuntamento a tutti al torneo 2003 ma prima ancora in MailingList per discutere e se- guire il torneo 91-2k2 tuttora in svolgimento/' Simone Ascheri 46^^ F e b b http://www.itportal.it <<<<<<<<<<<<<<<<<<<<<<<< Sistema Ottimizzare PROGRAMMI DELPHI Ambienti di sviluppo sempre più complessi e computer di ultima generazione dalle prestazioni strabilianti sembrano aver reso inutile l'ottimizzazione del codice. Vediamo come, quando e perché non abbassare la guardia. Sono in molti a sostenere, non a sproposito, che T ottimizzazione sia un'arte. In effetti scrivere del "buon codice" significa anche sapere amalgamare capacità tecniche e creatività, senza di- menticare una generosa dose di passione. Il know-how necessario per ottimizzare, in modo adeguato, un programma risulta spesso fuori dalla portata del programmatore alle prime armi. E' ne- cessario infatti conoscere a fondo: le caratteristiche dell' architettura hardware, del sistema operativo, del compilatore e del linguaggio di programmazio- ne. In questo articolo, rivolto principalmente a pro- grammatori che si avvicinano per la prima volta al- l'argomento, analizzeremo metodi, strumenti e trucchi utilizzabili per aumentare Y efficienza del codice prodotto ed evitare gli errori più grossolani. CONTRO E PRO Può sembrare strano, ma non è facile giustificare Y ot- timizzazione, in quanto aumentare le prestazioni di un programma costa in termini di: • Tempo: cercare soluzioni migliori significa impie- gare del tempo prezioso, è perciò molto improba- bile che il codice ottimale si concili con il time-to- market; • Stabilità: i trucchi usati per raggiungere certe pre- stazioni possono minare il corretto funzionamen- to del programma; • Portabilità e manutenibilità: Y impiego di alcune ottimizzazioni lega il codice ad un numero relati- vamente ristretto di architetture e/o compilatori. Inoltre, in assenza di commenti si rischia di otte- nere "spaghetti code", ossia sorgenti intricati, po- co comprensibili e difficilmente mantenibili. A volte però il conseguimento di prestazioni eleva- te rientra tra i requisiti del progetto ed in ogni caso T efficienza è una discriminante non indifferente: uno dei fattori chiave nella scelta di un programma rispetto ad un altro similare. Considerate poi che nei dispositivi mobili, di strategica importanza per il futuro, le risorse sono limitate e la razionalizza- zione del software diventa una necessità imprescin- dibile. La "qualità del codice" è direttamente proporziona- le air esperienza del programmatore, la conoscenza delle tecniche di ottimizzazione rappresenta dun- que un influente biglietto da visita. Nel prosieguo dell' articolo verranno illustrate le re- gole più semplici e generali per ridurre la dimen- sione dell'eseguibile, dello spazio occupato in me- moria e dei tempi di calcolo. DA DOVE PARTIRE In generale per ottimizzare un programma è necessa- rio operare (almeno) a due livelli di astrazione: • Livello algoritmo/strutture dati: lo studio della complessità computazionale fornisce una misura oggettiva della "bontà" di un algoritmo e aiuta il programmatore nella scelta del metodo più ido- neo alla risoluzione di un problema. E' bene non dimenticare che ogni struttura dati ha delle pecu- liarità che potrebbero viziare in maniera determi- nante le prestazioni; • Livello implementazione: solo dopo aver esami- nato in modo esaustivo il livello precedente si può pensare di "mettere mano al codice" e cerca- re i costrutti, le funzioni di libreria ed i trucchi del mestiere che garantiscono la maggiore efficienza. Entrambi i livelli prevedono una casistica stermina- ta e come spesso accade i punti di vista sono i più disparati, in alcuni casi nettamente contrapposti, mi limiterò dunque a fornire tecniche applicabili in ge- nerale. Per motivi di spazio trascureremo argomenti im- portanti come la programmazione parallela (multi- threading, presenza di GPU, ...) e Fuso delle pro- prietà dei dispositivi hardware (branch prediction, pipelining, vincoli di accesso alla memoria cache, ...). Spero che una volta letto l'articolo termini qua- li profiler e memory leak risultino meno astrusi. Sistema File sul CD \soft\codice \Ottimizzazione Delphi\ Eseguibili r& Per ottenere ese- F e b b http: //www. itportal.it m Complessità L'efficienza di un programma può essere caratte- rizzata dai tempi di esecuzione (efficienza tempo- rale) e/o dalla memoria impiegata (efficienza spaziale). È possibile ottenere una misura dell'ef- ficienza indipendente dalle architetture hardware e software, analizzando la complessità temporale e quella spaziale di un algoritmo. Esistono molte- plici notazioni ideate per determinare se un algo- ritmo è più veloce/lento di un altro, tra le più co- muni possiamo annoverare le notazioni asintoti- che: O, 0, Q. O GRANDE: Scrivendo f(n) EO(g(n)) si intende che f(n) è de- finitivamente maggiorata da g(n). In altre parole esistono un valore k dell'ingresso ed una costan- te positiva e tali che: k. È importante notare che per valori di n mino- ri di k potrebbe accadere che f(n) sia mag- giore di cg (n), a partire da k risulta però cg (n)zf(n)! Nella figura a lato k vale 1. OMEGA GRANDE: Scrivendo f(n) E Q (g(n)) si intende che g (n) è definitivamen- te maggiorata da f(n), ovve- ro: 3c f k > ta- li che £ cg(n) £ f(n) per ogni n > k. 20 / 10 cg| ny fi n) 2 3 4 f(nl cfl(n) 5i 4 c :2g(r y / f(n) / ^ d g (n) 2 4 8 10 THETA GRANDE: Scrivendo f(n) E0(g(n)) si intende che, a partire da n>k, f(n) è compresa tra ci g(n) e c2 g(n) dove ci e c2 sono due costanti molti- plicative positi- ve, cioè: 3 k,cl ,c2 tali che clg (n) * f(n) * c2g(n) per ogni n > k. Di solito si valuta la complessità computazionale di un algoritmo facendo riferimento alla notazione O grande. Vediamo un piccolo esempio: vogliamo ordi- nare 1000 elementi (n=1000) e dobbiamo scegliere tra un algoritmo Bubble-Sort ed uno Quick-Sort. Do- po "ore" spese in considerazioni formali determinia- mo che il primo ha, in generale, una complessità 0(n*2) quindi risulta O(l.OOO.OOO), il secondo ha un caso medio che "costa" solo 0(n log n) e dunque O(10.000). Un bel risparmio di tempo! Se vi è venuta voglia di approfondire l'argomento vi consiglio di consultare un qualsiasi testo accademico di informa- tica di base. mente necessarie. Le altre verranno create e di- strutte via codice; • Rinunciamo ai fronzoli e agli abbellimenti grafici eccessivi concentrando Y attenzione sulle caratte- ristiche fondamentali; • Impieghiamo al meglio la gestione delle eccezio- ni, in particolare i blocchi try. . .finally. L'ultimo passo consiste nel verificare la presenza dei temuti memory leaks, ovvero di sprechi di me- moria dovuti a errori di allocazione e deallocazio- ne. Per fortuna esistono delle suite di strumenti, più o meno sofisticate e costose, che svolgono gran parte del lavoro al posto nostro. Memproof è un tool gratuito che ci permette di scovare i bug più sub- doli: gli sprechi di memoria. Gli eseguibili proces- sati da Memproof devono essere compilati con le informazioni di debug, si faccia riferimento alla se- zione Linker delle opzioni di progetto oppure allo switch -v del compilatore da linea di comando. Non creiamo automaticamente tutte le form pre- viste dall' applicazione, ma solo quelle stretta- Fig. 5: MemProof segnala eventuali sprechi di memoria (ed altri errori comuni). Dopo aver caricato l'eseguibile da testare in Mem- Proof si avvia l'analisi con il tasto ¥9, ad analisi ter- minata si ha un resoconto approfondito con tanto di descrizione del problema (Fig. 5). CONCLUSIONI Nello spazio che mi è concesso ho tentato di mette- re molta carne sul fuoco in modo da stuzzicare la vostra curiosità. L'ottimizzazione è un argomento interessante ma al tempo stesso tra i più complessi, spero di avervi invogliato a testare i vostri pro- grammi con gli strumenti descritti, potreste avere delle sorprese! La programmazione parallela, l'otti- mizzazione degli applicativi database e client/ ser- ver, il nuovo Delphi 7 ed il futuro Delphi .NET me- riterebbero intere serie di articoli... È per questo che vi consiglio di non perdere i pros- simi numeri! Salvatore Meschini Sistema Ottimizzare Programmi Delphi Complessità j-& Nella tabella di ^J fianco troviamo una breve disamina sulla complessità e sul- la misura dell'efficien- za. http://www.itportal.it 3 ►►► 51 <<<<<<<<<<<<<<<<<<<<<<<< Sistema Ottimizzare PROGRAMMI DELPHI Ambienti di sviluppo sempre più complessi e computer di ultima generazione dalle prestazioni strabilianti sembrano aver reso inutile l'ottimizzazione del codice. Vediamo come, quando e perché non abbassare la guardia. Sono in molti a sostenere, non a sproposito, che T ottimizzazione sia un'arte. In effetti scrivere del "buon codice" significa anche sapere amalgamare capacità tecniche e creatività, senza di- menticare una generosa dose di passione. Il know-how necessario per ottimizzare, in modo adeguato, un programma risulta spesso fuori dalla portata del programmatore alle prime armi. E' ne- cessario infatti conoscere a fondo: le caratteristiche dell' architettura hardware, del sistema operativo, del compilatore e del linguaggio di programmazio- ne. In questo articolo, rivolto principalmente a pro- grammatori che si avvicinano per la prima volta al- l'argomento, analizzeremo metodi, strumenti e trucchi utilizzabili per aumentare Y efficienza del codice prodotto ed evitare gli errori più grossolani. CONTRO E PRO Può sembrare strano, ma non è facile giustificare Y ot- timizzazione, in quanto aumentare le prestazioni di un programma costa in termini di: • Tempo: cercare soluzioni migliori significa impie- gare del tempo prezioso, è perciò molto improba- bile che il codice ottimale si concili con il time-to- market; • Stabilità: i trucchi usati per raggiungere certe pre- stazioni possono minare il corretto funzionamen- to del programma; • Portabilità e manutenibilità: Y impiego di alcune ottimizzazioni lega il codice ad un numero relati- vamente ristretto di architetture e/o compilatori. Inoltre, in assenza di commenti si rischia di otte- nere "spaghetti code", ossia sorgenti intricati, po- co comprensibili e difficilmente mantenibili. A volte però il conseguimento di prestazioni eleva- te rientra tra i requisiti del progetto ed in ogni caso T efficienza è una discriminante non indifferente: uno dei fattori chiave nella scelta di un programma rispetto ad un altro similare. Considerate poi che nei dispositivi mobili, di strategica importanza per il futuro, le risorse sono limitate e la razionalizza- zione del software diventa una necessità imprescin- dibile. La "qualità del codice" è direttamente proporziona- le air esperienza del programmatore, la conoscenza delle tecniche di ottimizzazione rappresenta dun- que un influente biglietto da visita. Nel prosieguo dell' articolo verranno illustrate le re- gole più semplici e generali per ridurre la dimen- sione dell'eseguibile, dello spazio occupato in me- moria e dei tempi di calcolo. DA DOVE PARTIRE In generale per ottimizzare un programma è necessa- rio operare (almeno) a due livelli di astrazione: • Livello algoritmo/strutture dati: lo studio della complessità computazionale fornisce una misura oggettiva della "bontà" di un algoritmo e aiuta il programmatore nella scelta del metodo più ido- neo alla risoluzione di un problema. E' bene non dimenticare che ogni struttura dati ha delle pecu- liarità che potrebbero viziare in maniera determi- nante le prestazioni; • Livello implementazione: solo dopo aver esami- nato in modo esaustivo il livello precedente si può pensare di "mettere mano al codice" e cerca- re i costrutti, le funzioni di libreria ed i trucchi del mestiere che garantiscono la maggiore efficienza. Entrambi i livelli prevedono una casistica stermina- ta e come spesso accade i punti di vista sono i più disparati, in alcuni casi nettamente contrapposti, mi limiterò dunque a fornire tecniche applicabili in ge- nerale. Per motivi di spazio trascureremo argomenti im- portanti come la programmazione parallela (multi- threading, presenza di GPU, ...) e Fuso delle pro- prietà dei dispositivi hardware (branch prediction, pipelining, vincoli di accesso alla memoria cache, ...). Spero che una volta letto l'articolo termini qua- li profiler e memory leak risultino meno astrusi. Sistema File sul CD \soft\codice \Ottimizzazione Delphi\ Eseguibili r& Per ottenere ese- F e b b http: //www. itportal.it m Complessità L'efficienza di un programma può essere caratte- rizzata dai tempi di esecuzione (efficienza tempo- rale) e/o dalla memoria impiegata (efficienza spaziale). È possibile ottenere una misura dell'ef- ficienza indipendente dalle architetture hardware e software, analizzando la complessità temporale e quella spaziale di un algoritmo. Esistono molte- plici notazioni ideate per determinare se un algo- ritmo è più veloce/lento di un altro, tra le più co- muni possiamo annoverare le notazioni asintoti- che: O, 0, Q. O GRANDE: Scrivendo f(n) EO(g(n)) si intende che f(n) è de- finitivamente maggiorata da g(n). In altre parole esistono un valore k dell'ingresso ed una costan- te positiva e tali che: k. È importante notare che per valori di n mino- ri di k potrebbe accadere che f(n) sia mag- giore di cg (n), a partire da k risulta però cg (n)zf(n)! Nella figura a lato k vale 1. OMEGA GRANDE: Scrivendo f(n) E Q (g(n)) si intende che g (n) è definitivamen- te maggiorata da f(n), ovve- ro: 3c f k > ta- li che £ cg(n) £ f(n) per ogni n > k. 20 / 10 cg| ny fi n) 2 3 4 f(nl cfl(n) 5i 4 c :2g(r y / f(n) / ^ d g (n) 2 4 8 10 THETA GRANDE: Scrivendo f(n) E0(g(n)) si intende che, a partire da n>k, f(n) è compresa tra ci g(n) e c2 g(n) dove ci e c2 sono due costanti molti- plicative positi- ve, cioè: 3 k,cl ,c2 tali che clg (n) * f(n) * c2g(n) per ogni n > k. Di solito si valuta la complessità computazionale di un algoritmo facendo riferimento alla notazione O grande. Vediamo un piccolo esempio: vogliamo ordi- nare 1000 elementi (n=1000) e dobbiamo scegliere tra un algoritmo Bubble-Sort ed uno Quick-Sort. Do- po "ore" spese in considerazioni formali determinia- mo che il primo ha, in generale, una complessità 0(n*2) quindi risulta O(l.OOO.OOO), il secondo ha un caso medio che "costa" solo 0(n log n) e dunque O(10.000). Un bel risparmio di tempo! Se vi è venuta voglia di approfondire l'argomento vi consiglio di consultare un qualsiasi testo accademico di informa- tica di base. mente necessarie. Le altre verranno create e di- strutte via codice; • Rinunciamo ai fronzoli e agli abbellimenti grafici eccessivi concentrando Y attenzione sulle caratte- ristiche fondamentali; • Impieghiamo al meglio la gestione delle eccezio- ni, in particolare i blocchi try. . .finally. L'ultimo passo consiste nel verificare la presenza dei temuti memory leaks, ovvero di sprechi di me- moria dovuti a errori di allocazione e deallocazio- ne. Per fortuna esistono delle suite di strumenti, più o meno sofisticate e costose, che svolgono gran parte del lavoro al posto nostro. Memproof è un tool gratuito che ci permette di scovare i bug più sub- doli: gli sprechi di memoria. Gli eseguibili proces- sati da Memproof devono essere compilati con le informazioni di debug, si faccia riferimento alla se- zione Linker delle opzioni di progetto oppure allo switch -v del compilatore da linea di comando. Non creiamo automaticamente tutte le form pre- viste dall' applicazione, ma solo quelle stretta- Fig. 5: MemProof segnala eventuali sprechi di memoria (ed altri errori comuni). Dopo aver caricato l'eseguibile da testare in Mem- Proof si avvia l'analisi con il tasto ¥9, ad analisi ter- minata si ha un resoconto approfondito con tanto di descrizione del problema (Fig. 5). CONCLUSIONI Nello spazio che mi è concesso ho tentato di mette- re molta carne sul fuoco in modo da stuzzicare la vostra curiosità. L'ottimizzazione è un argomento interessante ma al tempo stesso tra i più complessi, spero di avervi invogliato a testare i vostri pro- grammi con gli strumenti descritti, potreste avere delle sorprese! La programmazione parallela, l'otti- mizzazione degli applicativi database e client/ ser- ver, il nuovo Delphi 7 ed il futuro Delphi .NET me- riterebbero intere serie di articoli... È per questo che vi consiglio di non perdere i pros- simi numeri! Salvatore Meschini Sistema Ottimizzare Programmi Delphi Complessità j-& Nella tabella di ^J fianco troviamo una breve disamina sulla complessità e sul- la misura dell'efficien- za. http://www.itportal.it 3 ►►► 51 LEGO Sul Web ET Sito ufficiale LEGO http://mindstorms.lego. com/enq/default.asp Lego Shop http://shop.lego.com Dave Baum's NQC http: //www, baumfamily. org/nqc/ LEGO MINDSTORMS ROBOTICS INVENTION SYSTEM 2.0 Potrebbe, a prima vista, sembra un classico gioco Lego, in realtà nasconde una vera e propria meraviglia tecnologica, che unisce con estrema semplicità robotica, cibernetica e informatica. Il kit Lego Mindstorms consente di creare robot dotati di articolazioni, sensori ottici e tattili, ecc. utilizzando i mattoncini Lego e l'RCX, un micro- processore programmabile mediante un kit fornito dalla Lego, o tramite appositi SDK per Visual Basic, C++, ecc. Utilizzando il software incluso nel kit si può' scri- vere un programma in grado di far muovere i pic- coli robot in modo che possano evitare gli ostacoli, seguire un percorso o una fonte luminosa. Il kit (Fig.l) si compone di ben 718 pezzi. Tra questi troviamo: • Microcomputer RCX; • CD-ROM con software RCXCode (sviluppo vi- suale sul PC); • Constructopedia: una raccolta di istruzioni per costruire robot di esempio; • 3 Guided Challenges; • 6 Pro Challenges; 1 1 ■ ■ 1 1 • Dispositivo di trasmissione ad infrarossi per la comunicazione tra PC e microcontroller RCX • 718 pezzi LEGO, (2 motori, 2 sensori di contatto, 1 sensore di luminosità). In aggiunta, è possibile acquistare separatamente altri sensori e motori passo passo. In particolar mo- do sono disponibili sensori di temperatura, di pros- sima, di angolazione. Il progetto lo si deve ad un matematico del MIT (Massachusetts Institute of Technology), promotore,tra T altro, del LOGO, linguaggio di programmazione diffuso nelle scuole per far apprendere i concetti ba- se della programmazione ai più piccoli. IL CUORE DEL LEGO MINDSTORMS Il Lego Mindstorms funziona grazie alla presenza di una sorta di microcomputer capace di coordina- re i diversi pezzi che l'utente assembla. RCX, questo il nome del microcontroller a 8 bit, ap- positamente realizzato dalla Hitachi, che rappre- senta il cervello delle invenzioni di Lego MindStor- ms. Tale microcontroller può essere programmato gra- zie all'interfacciamento ad un comune PC; la con- nessione avviene meditante un'apposita porta ad infrarosso compresa nella confezione. Nel kit è altresì presente un CD-Rom contenente un linguaggio visuale per creare gli schemi di funzio- namento dell' RCX, il programma, molto semplice ed intuitivo da utilizzare, consente di creare com- Fig. 1: La scatola contenente i 718 pezzi del Lego Mindstorms. Fig. 2: Il cuore del Lego Mindstorms: l'RCX 52 ►►► Febbraio 2 3 http://www.itportal.it plessi schemi di funzionamento, gestibile anche da un ragazzino di 12 anni poco pratico dei linguaggi di sviluppo. Tuttavia, esistono diversi SDK e kit di sviluppo (Le- go Mindstorms SDK, Bricx, NQC - Not Quite C) che permettono allo sviluppatore, più avanzato, di creare vere e proprie applicazioni in linguaggio C, Visual Basic; esistono, in giro per la Rete, anche kit di sviluppo Java, Delphi, C#. Requisiti tecnici minimi • Sistema operativo: Windows 98; • CPU: Pentium II 233 MHz; • RAM: 32 MB; • Spazio disco disponibile su hard disk: 115 MB; • Mouse: Windows compatibile; • Scheda: Sound Blaster 16 Windows compatibile; • CD-ROM: 8X; • Video display: 800 X 600 SVGA con 4 MB RAM, 16 bit di colore. _} else if (EYE>=tooBright) J Off(RIGHT); On(LEFT); _} else J On(LEFT+RIGHT); } Santo Serra IL LINGUAGGIO C PER LO SVILUPPO Il listato che segue (NQC) mostra come realizzare una semplice applicazione in linguaggio; C si trat- ta di un'applicazione che consente, al robot mo- strato in Fig. 3, di seguire il tracciato di una riga ne- ra segnata su uno sfondo chiaro. #define EYE SENSOR_2 #define LEFT OUT_A #define RIGHT OUT_C int tooDark; int tooBright; void SetupQ { tooBright=EYE; PlaySound (SOUNDJJP); Wait (300); tooDark=EYE; int delta = (tooBright-tooDark)/3; tooDark+=delta; tooBright-=delta; } task main() { SetSensor (EYE,SENSOR_LIGHT); SetupQ; On (LEFT+RIGHT); while(true) { if (EYE<=tooDark) { Off(LEFT); On(RIGHT); LEGO Lego Mindstorms Robot ics Invention System 2.0 §ÌD Biblioteca • DEFINITIVE GUIDE TO LEGO MINDSTORMS II Edition Dave Baum's Fig. 3: Un semplice robot, dotato di sensore di luminosità. Notate come il robot segue, automaticamente, il tracciato nero. (Apress) ISBN: 1-59050-063-5 Prezzo: US $29.99 Si tratta di un testo, in lingua inglese, che con- sente di seguire passo passo la realizzazione di ben 14 robot. Scritto dal creatore del lin- guaggio NQC, da la possibilità, a chiunque ne abbia voglia, di rea- lizzare il proprio Robot, analizzandone ogni aspetto, finanche le leggi fisiche che ne re- golano il comportamen to. Ogni progetto rea- lizzato è ben illustrato e documentato. Per ognuno dei robot presentati, viene rea- lizzato il codice di fun- zionamento, sia in lin- guaggio C, sia median- te l'apposito kit visuale fornito insieme al Lego Mindstorms. Un testo che non può mancare nella bibliote- ca di ogni appassionato dei Lego Mindstorms. http: //www. itportal.it 3 ►►► 53 Un Software DI CONTROLLO PER MOTORI PASSO- PASSO Nello scorso numero è stato proposto uno schema elettronico adatto alla gestione di motori passo passo unipolari: in queste pagine proponiamo il relativo software di controllo, scritto con il linguaggio di programmazione Delphi. Realizzando la semplice scheda hardware proposta in precedenza ed utilizzando il programma riportato in questa sede, si ottiene un sistema semplice ed affidabile per la gestione di motori passo passo. precedenza non è sufficiente al controllo del motore, senza un idoneo programma che ne gestisca le caratte- ristiche. Abbiamo visto inoltre che questo tipo di mo- tori possono essere azionati in diversi modi, utilizzan- do diverse sequenze di commutazione delle linee, ot- tenendo prestazioni diverse che si adattano a differen- ti applicazioni. In queste pagine vogliamo proporre un programma che sia in grado di gestire un motore pas- so passo, mediante lo schema elettrico proposto in pre- cedenza, dando modo al lettore di potere variare senso di rotazione, velocità e modo di funzionamento di questo importante componente elettromeccanico. Si assume che venga utilizzato un sistema dotato di WIN 9X oppure Millennium e che venga utilizzata una por- ta parallela con impostazioni standard LPT1 ( Indiriz- zi 0378h, 0379h 037 Ah ). Motori passo-passo Si potrebbe dire che l'uomo, nel trascorrere della sua storia, abbia sempre cercato un modo per fa- cilitare la propria esistenza. In epoca moderna, costruendo macchinari che svolgono il lavoro in modo autonomo, Tessere umano ha avuto bisogno di svaria- ti sistemi di propulsione ed azionamento delle proprie apparecchiature che sono divenute sempre più com- plesse con il passare del tempo. La semplice propul- sione non è sufficiente però senza una qualche forma di controllo: inoltre quanto più T algoritmo di gestione è Intelligente' e maggiore è la flessibilità delT apparec- chiatura. Ritornando ai nostri motori passo passo, pos- siamo sicuramente affermare che il circuito proposto in ■ -ln|x| 5'eppe; "*■ r Direction ff FWD r REW r Continuous Mo itoring 7 then PresentStep :=0; W:=StepperMotorSteps[PresentStep]; 58^^ F e b b http: //www. itportal.it t t WritePort(MyPortAddress,W); End; procedure TSpuntoStepperMotorForm.StepREW; // Step REW Var W,MyPortAddress:Word; Begin MYPortAddress: = FormPortaParallela .StepperMotorPortaParallela.SpuntoPortDataAddress; PresentStep : = PresentStep- 1 ; If PresentStep<0 then PresentStep:=7; W:=StepperMotorSteps[PresentStep]; WritePort(MyPortAddress,W); End; La scrittura del valore numerico sulla porta parallela avviene per mezzo della procedura 'WritePorf. In que- sta occasione il linguaggio Assembler ci viene in aiuto, facilitando l'accesso alle risorse hardware del nostro di- spositivo parallelo. A causa dell'accesso diretto alle ri- sorse di sistema, è conveniente utilizzare sistemi ope- rativi WIN 9X, oppure Millennium, dal momento che con WIN2000, oppure XP è possibile che il sistema ri- fiuti T esecuzione di questa parte di codice, innalzando un'eccezione di 'Privileged Instruction . procedure TSpuntoStepperMotorForm.WritePort( PortAddress, PortData:word); // Write PortData over PortAddress if Port Writing is enabled begin PortData := (PortData*256)+PortData; asm Mov ax, PortData Mov dx, PortAddress Out dx,ax end; Efes PaiaIN Manager Aboul |7 Stepper Motor [-Direction unning Data Pori Control Pori Status Pori Hex:0378 Hex:037A Hex:0379 % DO Ji\ % CO jj % SO • DijJ • crjj • a 01 • D2^J # C2^J * D2 • D3^J # C3_^J Q ss « D4^J « C4^J « S4 D3 D5 JjJ « C5 _5j # S5 GK • D6 Jl| • C6 jj % SO « D7jj « C7jJ O S7 >T\_ tepper MotOE Communications cambiando nel campo Port il numero di porta seriale. Dal menù Compile quindi selezioniamo la voce Reset Target/ Compile BIOS per richiedere un nuovo tentativo di connessione tra l'SDK ed il modulo Rabbit. :pk m feljSa X fl^l a ^ S.&] Edi, | Compiisi As Semb | Regs | Stacfc j _f] | COMI: 57600 Fig. 5: L'ambiente di sviluppo. UN PRIMO PROGRAMMA DI TEST Sulla scheda demoboard SX01 sono presenti due led, uno rosso, l'altro verde,comunemente chiamati led di test. In questa prima applicazione, giusto per verifica- re il funzionamento dell'intera struttura, scriviamo una semplice applicazione di test, che si limiterà a far lampeggiare alternativamente i due diodi led di test. Selezioniamo dal menù File la voce New per creare un nuovo programma da trasferire al Rabbit. Si aprirà una nuova finestra di editing che consentirà di scrivere i nostri programmi in linguaggio C. Digitiamo quindi il seguente sorgente: nodebug void msDelay(unsigned int ams) { auto unsigned long tO; for( tO = MS_TIMER; MS_TIMER< ams+tO; ) ; > // Inizio programma main () { // Imposta la porta D come linea di ingresso/uscita WrPortI(PDFR, &PDFRShadow, 0x0); // Imposta la porta PD3 come uscita BitWrPortI(PDDDR, &PDDDRShadow, 1,3); // Esegue un loop infinito while(l) j // Attende 5Q0mS msDelay(5Q0); // Accende il led verde TESTI BitWrPortI(PDDR, &PDDRShadow, 1, 3); // Spegne il led verde TEST2 BitWrPortI(PBDR, &PBDRShadow, 0, 7); // Attende altri 500mS msDelay(5Q0); // Spegne il led verde TESTI BitWrPortI(PDDR, &PDDRShadow, 0, 3); // Accende il led verde TEST2 BitWrPortI(PBDR, &PBDRShadow, 1, 7); _} } Clicchiamo il pulsante Compile presente sulla barra de- gli strumenti. Selezioniamo dal menu Run la voce Run. Sulla scheda SX01 dovremo vedere i diodi led TESTI e TESTI, lam- peggiare alternativamente con una frequenza di mez- zo secondo. Per bloccare l'esecuzione del programma selezioniamo la voce Stop dal menu Run. CONCLUSIONI Si conclude qui questa prima panoramica sul modulo Rabbit, nei prossimi appuntamenti approfondiremo il discorso, mostrando come realizzare un'applicazione, e relativa scheda di interfaccia al modulo, per coman- dare tramite un'applicazione Flash sul Web, una serie di led posti come interfaccia al modulo RCM2200. Santo Serra # Micro Controller Un server WEB nel taschino della giacca & Sul Web Tutto il materiale de- scritto in questo artico- lo è reperibile presso il sito: http://www.areasx.com Dal sito è possibile al- tresì acquistare il mo- dulo RCM2200, la demo board SX01 e il develop- ment kit, quest'ultimo per lo sviluppo di appli- cazioni in linguaggio C. http: //www. itportal.it 3 ►►► 63 Gli Exploit della progra azione ► ► ► ► ► ► ► Non c'è PASSWORD CHE TENGA! L'exploit di questo mese riguarda un problema di sicurezza che affligge alcuni modelli di router ADSL della Telindus (www.telindus.com) e della Arescom (www.arescom.com), che sembrano gestire le password di accesso con troppa leggerezza. Premettiamo che non si tratta di un vero e proprio "exploit" di program- mazione (non c'è un codice da com- pilare), ma è comunque interessante studia- re questa lacuna di sicurezza, per approfon- dire le conoscenze sui pacchetti di rete e per esercitare un po' di crittoanalisi. I prodotti affetti dal problema sono la serie Telindus 112x e Arescom NetDSL 1000: si tratta di rou- ter per connessioni ADSL, dotati di un hub/switch interno, accessibili e configura- bili via TCP /IP mediante una utility di amministrazione remota proprietaria. L'ex- ploit non è realizzabile da remoto, cioè non può essere usato da un intruso esterno per violare la sicurezza del router, ciò nono- stante ritorna molto utile usato dall'interno della propria LAN. L'OCCORRENTE L'exploit richiede Fuso dei seguenti softwa- 1) Packet Sniffer (prelevabile da Internet). 2) Telindus 9100 Maintenance Application (fornita col router o prelevabile da In- ternet). Dove e come reperirli? Un packet sniffer è un programma capace di intercettare i pac- chetti in transito su una interfaccia di rete; è un software molto utile per la diagnostica, ma, se usato in maniera diversa, è una delle armi più potenti in mano agli hacker. Due ottimi sniffer per Windows 2000 /XP sono Sniff-em (www.sniff-em.com) oppure Sniffer XP {www.ufasoft.com), che sono sufficiente- mente attrezzati per i nostri scopi. L'utility di amministrazione Telindus 9100 MA. è invece un programma capace di identifica- re un router Telindus presente su una LAN e connettersi ad esso per gestirne, mediante interfaccia grafica, la configurazione. Que- sto programma è in genere fornito col rou- ter stesso, ma è anche prelevabile (in via non-ufficiale) al link http://www.weethet.nl /downloads /Telindus _Local.rar. Una volta pre- disposto l'occorrente, può iniziare l'attacco: per prima cosa bisogna eseguire lo sniffer e metterlo in ascolto sull'interfaccia di rete della nostra LAN, quindi lanciare l'applica- zione Telindus 9100 MA. e catturare i pac- chetti in transito sulla rete. L'indirizzo IP di default assegnato al router è in genere quel- lo standard 192.168.1.1. ANALISI DI UN PACCHETTO UDP L'exploit prende spunto da queste conside- razioni, rilevabili dall'analisi dei pacchetti catturati dallo sniffer (Fig. 1): appare evi- dente che l'utility di amministrazione comunica col router usando il protocollo UDP sulla porta 9833; l'utility però non conosce a priori l'indirizzo IP assegnato al dispositivo; l'unica certezza è che router e computer su cui l'utility è in esecuzione si □ NetBSUl ' QTofcenRjno □ KM* Bn- Bue* Packeb £?$tn4tai W-5frK-X-9e« Ptetowi CKWQ • ]P tare? 192- 164.1-2 ttetintfci 192.168. 1-2 Pretowl IJ ■ Ut*» tare? Pere 98» F7 Sav* (7 Al mz _E*_J Fig. 1: Lo sniffer in azione, non perde neanche un pacchetto! trovano sulla stessa subnet e hanno quindi due indirizzi della forma 192.168.1. x. Come fa quindi l'applicazione Telindus 9100 MA. a scovare il router? Ricorre ad un semplice algoritmo di flooding, che prevede l'invio di un pacchetto UDP (discovery packet) in broadcast a tutti gli indirizzi IP della rete (192.168.1.255); l'unico dispositivo in grado di reagire a questo pacchetto sarà il router (se presente), che prontamente risponderà al mittente, segnalando la sua presenza. Questa prima sessione di identificazione dura pochi attimi ma è completamente rile- vabile seguendo i pacchetti intercettati dallo sniffer, che evidenziano, a questo punto, l'esistenza di un canale di comuni- cazione tra il router (192.168.1.1) e il com- puter che sta eseguendo l'utility di ammini- strazione remota (ad esempio 192.168.1.2). UN PROTOCOLLO NON PROPRIO SICURO Superata questa prima fase si noterà un fatto strano nel momento in cui l'utility di amministrazione attende l'immissione della password di accesso al router. Seguendo l'istinto, la prima cosa che ci viene in mente è quella di provare ad entra- re con qualche password comune, speran- do nell'aiuto della dea bendata, ecco quindi un primo tentativo (a vuoto), un secondo. . . ma al terzo tentativo notiamo qualcosa di strano! Nessun pacchetto, diretto al router, viene catturato dallo sniffer durante i tenta- tivi di accesso, questo significa una sola cosa: l'utility di amministrazione conosce la password ed esegue la verifica in memoria e non via rete, come ci saremmo aspettati. Quando si tenta l'accesso con una pas- sword sbagliata, l'utility di amministrazio- ne dovrebbe trasmettere via rete la pas- 64»*> F e http://www.itportal.it G I sword al router, attendere la verifica (fatta dal dispositivo) e ricevere la comunicazio- ne dell'esito; tutto ciò in realtà non avviene, perché il programma Telindus 9100 M.A. non trasmette niente al router, quindi cono- sce a priori la password, che evidentemen- te gli è stata trasmessa dal router nella ses- sione iniziale descritta prima. Basta infatti controllare la cronologia dei pacchetti cat- turati per scoprirne uno insolito, più lungo di tutti gli altri (>200 bytes), che una volta analizzato rivela parecchie sorprese. . . UN SEMPLICE ATTACCO CRITTOANALITICO Il pacchetto catturato contiene infatti tutti i dati del router (vedi Fig. 2): nome del dispositivo (evidenziato in verde), serial number, versione del firmware e perfino la password di accesso (evidenziata in rosso)! Una banalità. . . a cui nessuno aveva pensa- to, ma che fornisce a chiunque la possibilità di entrare nel router! CYPHER-TEXT 0100 00 03 02 00 08 00 00 A2 A3 2B 63 4B 73 23 AB 99 0110 0120 SB 7B AB 9B 28 ! > 9 89 91 Bl 82 0130 42 29 i L Bl 71 81 71 0140 5)1 t"! DA A3 AB 19 02 53 AB 61 01 95 SI 01 39 ■: : ! i 98 00 10 01 0160 oc- caso ...... ' „• „j 1. __> U _'_ J „„ 2j „j U 0U DI 80 30 0190 10 00 10 0A 10 00 00 05 2Ù 00 10 00 00 10 50 00 01AÙ LO 00 00 0A 30 00 LO 00 00 ÙA A3 00 20 00 00 00 i 00 0A 88 00 02 10 28 00 02 11 10 00 00 20 40 00 .."sa. "{«£+□...- k{<»C...'r ""*,'+ B)jiJbla. a +'.±qni Fig. 2: I vecchi firmware del router scambiamo pacchetti con la password in chiaro. Ce da dire che la Telindus tuttavia, messa a conoscenza del problema, ha subitoappor- tato alcune modifiche al firmware, che nelle nuove versioni (6.x) usa un algoritmo crit- tografico per cifrare il pacchetto "incrimi- nato" e per impedire la lettura della pas- sword in chiaro rafforzando la sicurezza del router. Nel pacchetto crittografato la password non è più leggibile. Si può fare qualcosa? Si può solo tentare un attacco critto-analitico al cifrario, sperando che non usi funzioni complesse e confidando nella buona sorte. Confrontiamo il payload di un pacchetto in chiaro (plain-text) e di uno cifrato (cypher - text) per cercare di capire come sono strut- turati (Tab. 1). I due pacchetti sembrano portare la stessa intestazione (i primi 2+3+2 byte), dopo i quali inizia la sezione informativa: il byte 05 (evidenziato in rosso) nel pacchetto in chiaro indica la lunghezza del campo ed è seguito infatti dalla stringa "DSL00" . Seguono 3 byte (termina- la Telindus 91 TO Ualntsnance Application rdindus 9100 Maintenanee Application Tiiìdus 9100 Mairtsrufws Àpofcfttion òtìstìsd 1 T*frn*js ADSL RotóHrtrWlgefcl You e*n s*ted one «outartindgs ti Ihe ntecfan tatto dnd efcfc Erta I» fjairpjtt tì*t «outMrtsndja If yvu ww* Co òetstt ibi mtwfa'viQt on yow locai FKtwafc «art. cfck Dtìrcl RoutmrtrkteNaiw | IP Adira | hUC Adira CQ60SC12A51F | Sania Sufawt EftiM [ Dattd [ Eri [ Help [ Fig. 3: Ecco come appare l'utility di amministrazione del router. zione?) e quindi il byte di lunghezza della password (OD, sempre in rosso) con la pas- sword in chiaro ("1111111111111"). Un attac- co di crittoanalisi si basa sulle debolezze del cifrario e sulla conoscenza di porzioni del testo in chiaro: nel nostro caso Y attacco è possibile, perché si conosce il nome del router, visto che viene mostrato (Fig. 4) dal- l'utility di amministrazione. Nel pacchetto di esempio catturato, il nome del router è "Telindus ADSL router", lungo 20 byte; a partire dal byte Al (lunghezza cifrata) ini- zia il campo . Non resta che scrivere la tavola crittoanalitica, confron- tando i byte criptati con i codici ASCII reali (Tab. 2). Da una prima analisi appare evi- dente che si tratta di un alfabeto di sostitu- zioni, un cifrario non difficile da risolvere, in cui ogni lettera è associata ad un codice (si evince osservando le corrispondenze delle lettere "u", "e", "t"); tale codice è uni- voco in qualsiasi punto del testo cifrato. Si osserva inoltre che tutti i codici nelF alfabe- to cifrato, terminano con "B" o con "3" , ad eccezione delle lettere finali di parola. Abbiamo abbastanza materiale per passare System Security Type user nanne and pa Maintenance Application Telindus ADSL Router Router Password: p Fig. 4: Il nome del router viene mostrato dall'utility di manutenzione: è questo l'anello debole della sicurezza. minatori non sono cifrati), ma bisogna ancora scoprire le altre lettere. Costruendo l'alfabeto del cifrario, notiamo che "r"=93, s="9B", "t"=A3, "u"=AB e così via, con in- crementi di 8, facendo eccezione per le let- tere di fine parola. È facile scoprire il resto della password, ipotizzando 6B="m" e 9B="s"; l'ultimo carattere, essendo di fine parola, avrà un codice cifrato diverso da quello standard, ma è comunque facile capire che si tratta di una "e" e che la paro- la chiave è "mouse". La riuscita dell'attacco è legata comunque alla lunghezza del campo , che deve essere suffi- cientemente grande da consentire l'attacco crittoanalitico. Elia Florio Sul Web Advidsory sulle insicurezze dei router Telindus/ Arescom http://www.securiteam.com/security- news/5DP0A2K7GY.html Tutorial sull'hackeraggio del router Telindus/ Arescom http://www.weethet.nl/enqlish /adsl arescomhacked.asp PLAIN-TEXT DSL00... .1111111111111.. tf£ + cKs#"™ .."_a. "{"£ + _...+ 0100 00 03 00 01 01 00 00 H-44 53 4C 30 30 01 01 00 0110 H31 31 31 31 31 31 31-31 31 31 31 31 31 01 AB 02 99 2B CYPHER-TEXT ■ A3 2B 63 7B AB A3 2B 4B 90 73 23 0100 00 03 02 00 08 00 00 0110 02 0A22 9A 61 02 93 08 08 00 Tab. 1: Confronto fra il payload di un pacchetto in chiaro e di uno cifrato (alcuni byte sono marcatori). T e 1 i n d u s A D S L R o u t e r ■ 2B 63 4B 73 23 ■ 99 02 0A 22 9A 61 02 93 7B |A3 2B 90 crypt 54 65 6C 69 6E 64 75 73 20 41 44 53 4C 20 52 6F 75 74 65 72 plain (*) (*) (*) (*) (*) (*) Tab. 2: Tavola crittoanalitica per lo studio del cifrario. Si tratta di un alfabeto di sostituzione. all'attacco della password, che inizia subito dopo il terminatore 08 08 00 (Tab. 3). Conosciamo due caratteri della password ("o", "u"), sappiamo che è di 5 byte (i ter- Lung. passwd ? 2B 6B o u ? ? 7B AB 9B 28 Term. 08 10 01 Tab. 3: Conoscendo qualche carattere della password, è facile decifrarla tutta. http://www.itportal.it b b 3 ►►► 65 C o r s Base***'*-***'*'*'*'*'*' JSP File sul CD \soft\codice \codici_jspl3.zip Dispense Web del corso I r& I Diversi approfon- \-J | di menti legati alla lezione odierna e alle precedenti sono reperi- bili in Internet, tra le dispense Web del cor- so. L'URLdi riferimento è http://www.sauronsoftwa- re.it/dispenseweb/jsp/. Procedure MEMORIZZATE CON JDBC Nelle applicazioni Web progettate per lavorare da interfaccia verso un database si fa spesso uso di procedure memorizzate. Questa lezione illustra gli scopi ed i funzionamenti delle procedure memorizzate, dimostrando diversi esempi pratici di codice completo. Le procedure memorizzate sono delle query precompilate. Il loro impiego ha due risvolti positivi: il miglioramento delle prestazioni e la semplificazione del codice. Non tutti i DBMS supportano le procedure me- morizzate. Con JDBC è possibile impiegare le procedure memorizzate, purché siano imple- mentate dal gestore di database sfruttato. L'ESEMPIO GUIDA Ricollegandoci a quanto già fatto nel corso del- la precedente lezione, impiegheremo un databa- se di Access come esempio guida. Create un da- tabase inizialmente vuoto, quindi inserite al suo interno una tabella, nominata Utenti. La struttu- ra è riportata di seguito: • ID, di tipo Contatore, da impiegare come chiave primaria. • Nome, di tipo Testo. • Cognome, di tipo Testo. • Email, di tipo Testo. • AnnoNascita, di tipo Numerico. ID Nome Cognome Email Anno Nascita 1 Mario Rossi rossi@tin.it 1958 2 Luigi Bianchi bianchi@libero.it 1972 3 Antonio Verdi verdi@tiscali.it 1967 4 Franco Neri (NULL) 1948 5 Vittorio Grigi (NULL) 1979 6 Massimo Gialli (NULL) 1981 Tab. 1: Tabella popolata con dati arbitrari. Popolate la tabella con qualche record arbitrario Tab: 1. Come al solito, registriamo un DSN di si- stema associato alla base di dati. Scegliamo il nome MioDatabase. PREPAREDSTATEMENT L'interfaccia PreparedStatement, del package java .sql, estende la già esaminata Statement. Richia- miamo alla memoria le operazioni necessarie per interrogare un DBMS, sfruttando l'interfac- cia Statement. Per prima cosa, è necessario recu- perare un oggetto che implementi tale interfac- cia di programmazione. La connessione (un og- getto Connection aperto) può fornircelo: Statement statement = connection. createStatement(); Ottenuto un oggetto Statement, è davvero sem- plice interagire con il DBMS, facendo fruttare le regole sintattiche di SQL: ResultSet resultset = statement. executeQuery (QUERY_SQL); L'impiego di PreparedStatement è simile, ma non del tutto identico. Una PreparedStatement è una query SQL precompilata. Al momento della creazione dell'oggetto, è già necessario specifi- care il testo dell'interrogazione che si intende sottomettere al DBMS: PreparedStatement statement = connection. prepareStatement(QUERY_SQL); Giacché il testo della query è fornito nel mo- mento stesso in cui l'oggetto PreparedStatement è richiesto al gestore della connessione, non è più necessario passare un'istruzione SQL al metodo executeQuery (). Per ottenere i risultati ricercati, 66^^ F e http: //www. itportal.it quindi, sarà sufficiente digitare: ResultSet resultset = statement. executeQuery(); Impiegando il database di Access allestito in precedenza, verifichiamo le affermazioni con l'esempio completo (lezione 13_01_01.jsp) che trovate nello zip presente nel CD. Quali vantaggi può avere un approccio del ge- nere rispetto a quello esaminato nel corso della lezione precedente? Come anticipato in apertu- ra, è possibile riscontrare due aspetti significati- 1. L'incremento delle prestazioni. 2. La maggiore semplicità del codice. Con l'esempio lezione 13_01.jsp, ad ogni modo, non è possibile riconoscere immediatamente questi due aspetti. Le procedure memorizzate forniscono reali vantaggi, infatti, quando se ne fa uso frequente e ripetuto, nel corso della stes- sa pagina JSP. Il metodo executeQueryO di una PreparedStatement può essere richiamato più di una volta nel medesimo documento JSP. Se una query deve essere eseguita più di una volta, quindi, l'interfaccia PreparedStatement permette di non dover digitare due volte la stessa stringa (= maggiore semplicità del codice). Non solo: le query SQL sottoposte al DBMS me- diante l'interfaccia PreparedStatement sono pre- compilate al momento della loro creazione (= incremento delle prestazioni). Nonostante que- sto, è improbabile che una stessa query debba essere eseguita più di una volta all'interno dello stesso frammento di codice. È più frequente che si debbano eseguire più query simili, ma non del tutto identiche. Ad esempio, se volessimo prima un elenco di tutti gli utenti nati dopo il 1970 e poi una lista di quelli nati dopo il 1980, dovremmo escogitare ed eseguire due istruzioni assai simili, eppure significativamente differenti: SELECT * FROM Utenti WHERE AnnoNascita >= 1970 SELECT * FROM Utenti WHERE AnnoNascita >= 1980 Con JDBC, è possibile eseguire ambo le query attraverso una sola PreparedStatement parame- trizzata: PreparedStatement statement = connection. prepareStatement( "SELECT * FROM Utenti WHERE AnnoNascita >= ?"); Osservando questa sintassi, il parametro di se- lezione collegato all'anno di nascita dell'utente non viene specificato nel momento della pre- compilazione dell'interrogazione. Sarà necessa- rio aggiungerlo in seguito, prima di eseguire la query, sfruttando i metodi del tipo setTipoDatoO. Ad esempio, nel nostro caso, potremo agire co- me segue: statement.setlnt(l, 1970); ResultSet resultset = statement.executeQueryQ; //■■■ statement.setlnt(l, 1980); resultset = statement. executeQueryO; Passiamo in rassegna un esempio completo, che soddisfa la richiesta ipotizzata in precedenza, si tratta del file lezione 13_02.jsp. Specificare i singoli parametri di una Prepared- Statement è molto semplice. I metodi a disposi- zione sono tutti del tipo: statement. setTipoDato(numeroParametro, valoreParametro); Tra i metodi disponibili, è importante annovera- re i seguenti: • setBooleandnt n, boolean p) • setBytednt n, byte p) • setDatednt n, Date p) • setDoublednt n, doublé p) • setFloatdnt n, float p) • setlntdnt n, int p) • setLongdnt n, long p) • setShortdnt n, short p) • setStringdnt n, String p) Nella documentazione ufficiale di Java potrete trovare maggiori informazioni su questa fami- glia di metodi. Più parametri possono essere usati simultaneamente, in una stessa Prepared- Statement. Ad esempio: PreparedStatement statement = connection. prepareStatement("SELECT * FROM Utenti WHERE AnnoNascita BETWEEN ? AND ?"); Per ottenere la lista degli utenti nati tra il 1970 ed il 1980, a questo punto, bisognerà usare la se- quenza di istruzioni: nfnl^ 1 Im Utenti Email Q < [>] Campo: Tabella: Ordinamento: Criteri: Oppure: - ID :■";•:' ,. ■ .-■, ... Utenti Utenti Is Not Nuli "); Maggiori informazioni nella documentazione ufficiale ed in letture apposite. // Imposto il primo parametro. statement.setlnt(l, 1970); // Imposto il secondo parametro. statement.setlnt(2, 1980); // Eseguo la query con i parametri correnti. ResultSet resultset = statement. executeQuery(); Il meccanismo è semplice e di facile compren- sione. CALLABLESTATEMENT Con PreparedStatement, come abbiamo appurato, è possibile sfruttare delle istruzioni SQL pre- compilate, che garantiscono migliori prestazioni e maggiore chiarezza. Le query desiderate sono date in pasto al DBMS al momento della crea- zione di un oggetto PreparedStatement, e posso- no poi essere ripetutamente sfruttate quante volte si desidera. rS rifalla 1 Utenti Nome Cognome Email v < a Tabella: Ordinamento: Criteri: ID Nome Email AnnoNascita " Utenti Utenti Utenti li'-;'.".-. ' -J v l Fig. 2: La procedura memorizzata AnnoNascita. Tuttavia, JDBC ed il DBMS devono ricompilare la medesima istruzione SQL ogni volta che il do- cumento JSP è richiesto da un utente. Ad ogni richiesta, infatti, corrisponde la creazione di un nuovo oggetto PreparedStatement, con tutte le conseguenze del caso. Si può fare di meglio, se il DBMS lo permette, ottenendo incrementi an- cora più significativi sia nelle prestazioni sia nella semplicità del codice. L'interfaccia Calla- bleStatement estende PreparedStatement, e per- mette di richiamare delle procedure memorizza- te all'interno del database. Una query, con CallableStatement, non deve esse- re specificata in linea all'interno del codice della pagina JSP, ma può essere memorizzata peren- nemente all'interno della base di dati, pronta ad essere sfruttata più e più volte, in quante pagine si desidera. Ogni DBMS impiega tecniche diffe- renti per la preparazione di procedure memo- rizzate. Sotto Access, ad esempio, è possibile servirsi di una comoda e semplice interfaccia utente. Riprendiamo il database sfruttato per gli esem- pi precedenti e dotiamolo di una procedura me- morizzata: 1. Aprendo il database con Access, attiviamo la linguetta laterale nominata "Query". 2. Avviamo la procedura "Crea una query in vi- sualizzazione struttura ". 3. Selezioniamo la tabella Utenti come sorgente di dati. 4. Nell'elenco disponibile, facciamo in modo che tutti i campi della tabella siano selezio- nati e restituiti (se non siete pratici di Access, fate riferimento alla Fig. 1). 5. Sotto la colonna associata al campo Email, al- la voce "Criteri " , introduciamo la limitazione "IsNotNull". 6. Salviamo la query generata con il nome Not- NullMail. Tramite questi passi, è stata memorizzata una query del tipo: SELECT IP, Nome, Cognome, Email, AnnoNascita FROM Utenti WHERE Email IS NOT NULL il cui scopo è restituire l'elenco di tutti gli uten- ti di cui è noto l'indirizzo e-mail. Adesso è pos- sibile richiamare la query NotNullMail diretta- mente da JSP: // Ottengo un oggetto CallableStatement che richiama // la procedura memorizzata NotNullMail. CallableStatement statement = connection. prepareCall( "{cali NotNullMail}" ); // Eseguo la query. ResultSet resultset = statement. executeQuery(); Desumerne un esempio funzionante è semplice, come potrete verificare visualizzando il file: le- zionel3_03.jsp. Il codice HTML prodotto in out- put mostrerà il risultato di Tab. 2. Nome Cognome Email AnnoNascita Mario Rossi rossi@tin.it 1958 Luigi Bianchi bianchi@libero . it 1972 Antonio Verdi verdi@tiscali.it 1967 Tab. 2: Risultato dell'esecuzione del codice "lezione 13_03.jsp". Anche una CallableStatement può sfruttare uno o più parametri. Andiamo a dimostrare un esem- pio di questo tipo. Tornando ad Access, realizziamo una nuova procedura memorizzata, chiamata AnnoNascita (Fig. 2). 68^^ F e http: //www. itportal.it Come nel caso precedente, selezioniamo la ta- bella Utenti e trasportiamo tutti i campi dispo- nibili. Il criterio di restrizione, questa volta, lo appli- chiamo al campo AnnoN uscita, con il codice: Between [min] And [max] Questa query, sostanzialmente, corrisponde alla PreparedStatement già esaminata in precedenza: SELECT * FROM Utenti WHERE AnnoNascita BETWEEN ? AND ? Potrà essere richiamata alla seguente maniera: CallableStatement statement = connection. prepareCall( "{cali AnnoNascita(?, ?)}"); I parametri potranno essere impostati come di consueto: statement.setlnt(l, estremolnferiore); statement. setlnt(2, estremoSuperiore); L'intero codice che se ne desume è riportato di seguito: <%@ page import="java.sql.*" %> <%! // Nome del driver. String DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver"; // Indirizzo del database. String DBJJRL = "jdbc:odbc:MioDatabase"; %> Corso di JSP, Lezione 13, Esempio 4 <% // Carico il driver. Class.forName(DRIVER); // Preparo il riferimento alla connessione. Connection connection = nuli; try { // Apro la connesione verso il database. connection = DriverManager.getConnection(DB_URL); // Ottengo un oggetto CallableStatement che richiama // la procedura memorizzata AnnoNascita. CallableStatement statement = connection.prepareCall( "{cali AnnoNascita(?, ?)}" ); // Specifico i parametri. statement.setlnt(l, 1970); statement.setlnt(2, 1980); // Eseguo la query. ResultSet resultset = statement.executeQueryQ; %> Nome <% // Scorro e mostro i risultati. while (resultset.nextQ) { String nome = resultset.getString("Nome"); String cognome = resultset. getString("Cogno- me"); String email = resultset.getString("Email"); boolean emailIsNull = resultset.wasNullQ; int anno = resultset.get!nt("AnnoNascita"); %> <% } %>
Cognome E-mail AnnoNascita
<%= nome %x/td> <% if (lemailIsNull) { %> <%= email %> <% } else { %> N.D. <% } %>
<% } catch (SQLException e) { // In caso di errore. .. %xb>Eccezione: <%= e.toStringQ %><% } finally { if (connection != nuli) connection.closeQ; _} %> L'output restituito comprenderà la Tab. 3. Nome Cognome Email AnnoNascita Luigi Bianchi bianchi@libero.it 1972 Vittorio Grigi (NULL) 1979 Tab. 3: Output restituito dal codice "lezione 13_04.jsp". CONCLUSIONI Con questa lezione si conclude la panoramica che questo corso dedica all'impiego di JDBC. Eventuali approfondimenti possono essere svolti sfruttando la documentazione ufficiale di Java o dei manuali specifici per JDBC e SQL. Carlo Pelliccia Bibliografia • JSP, LA GUIDA COM- PLETA Phil Hanna (McGraw-Hill) ISBN 88-386-4207-9 2001 • IMPARARE JAVASER- VER PAGES IN 24 ORE Jose Annunziato, Stephanie Fesler Kaminaris (Tecniche Nuove) ISBN 88-481-1306-0 2001 • JAVA, LA GUIDA COMPLETA Patrick Naughton & Herbert Schiidt (McGraw-Hill) ISBN 88-386-0416-9 1997 • JAVA 2, TUTTO & OLTRE Jamie Jaworski (Apogeo) ISBN 88-7303-466-7 1999 http://www.itportal.it b b 2 3 ►►► 69 Le finestre Visual Basic DI DIALOGO NET Le proprietà di OpenFileDialog j% ADDEXTENSION, ^s\ indica se viene ag- giunto automaticamen- te un'estensione ad un nome di file quando l'u- tente non la inserisce. CHECKFILEEXISTS, in- dica se nella finestra di dialogo viene visualiz- zato un avviso quando l'utente specifica un nome di file inesistente. CHECKPATHEXISTS, in- dica se nella finestra di dialogo viene visualiz- zato un avviso quando l'utente specifica un percorso inesistente. DEFAULTEXT, indica l'e- stensione del file prede- finita. DEREFERENCELINKS, indica se la finestra di dialogo restituisce la posizione del file a cui fa riferimento il collega- mento o se restituisce la posizione del collega- mento. FILENAME, contiene il nome del file seleziona- to nella finestra di dialo- go. È una proprietà a so- la lettura. FILENAMES, contiene i nomi di tutti i file sele- zionati nella finestra di dialogo. È una proprietà a sola lettura. FILTER, indica la stringa filtro del nome file cor- rente, che stabilisce le opzioni visualizzate nel- le casella relative al tipo di file nella finestra di dialogo. In questo nuovo appuntamento parleremo delle piccole finestre che appaiono sullo schermo per interagire con l'utente e recuperare informazioni. Le finestre di dialogo forniscono la consueta inter- faccia utente presente nelle applicazioni Win- dows utilizzata per mostrare dei messaggi, op- pure per richiedere all'utente dati necessari all'applica- zione. VB.NET fornisce molte finestre di dialogo stan- dard che è possibile adattare alle applicazioni, ad esempio la finestra Apri, rappresentata dal controllo OpenFileDialog, oppure la finestra di Stampa rappresen- tata dal controllo PrintDialog e così via. In VB.NET è possibile, inoltre, progettare finestre di dialogo com- pletamente personalizzate in base ad esigenze partico- lari. Una finestra di dialogo, in pratica, non è altro che una form modale con un insieme di controlli (tipica- mente Label, Textbox e Button), la cui proprietà Form- BorderStyle è impostata su FixedDialog. CREARE UNA FINESTRA DI DIALOGO DA ZERO Per descrivere come realizzare una finestra di dialogo personalizzata, realizziamo una semplice applicazione d'esempio per il calcolo dell'area di un rettangolo (rileggetevi l'articolo di Ottobre per notare le differen- ze). Come di consueto creiamo un nuovo progetto, dal nome Calcolo Area e modifichiamo il nome di Formi in Form Area. Nella finestra Form Area inseriamo un Button dal nome ButtonCalcola. Quando l'utente clicca sul pul- sante verrà mostrata una prima finestra di dialogo in cui verrà chiesto di inserire il valore della base del ret- tangolo, una seconda finestra di dialogo in cui verrà chiesto di inserire il valore dell'altezza del rettangolo, ed infine una terza finestra di dialogo che mostrerà il risultato del calcolo. Per creare la prima finestra di dia- logo si deve aggiungere un form al progetto e modifi- carne alcune proprietà, i passi da seguire saranno: • Selezionare la voce: Progetto /Aggiungi Windows Form. • Digitare il nome della finestra (FormDialogoBase) nella casella di testo Nome. • Cliccare sul pulsante Apri. • Visualizzare la finestra delle proprietà della form in cui modificare le seguenti proprietà per personaliz- zarne l'aspetto: 70»> F e b b • FormBorderStyle in FixedDialog. • MinimizeBox e MaximizeBox su false, poiché le finestre di dialogo in genere non includono pulsanti di riduzione ad icona e di ingrandi- mento. Le finestre di dialogo vengono visualizzate come fine- stre modali, e cioè, come finestre a scelta obbligatoria, che impediscono all'utente di eseguire operazioni al di fuori della finestra di dialogo. Per visualizzare una fi- nestra come modale, si deve utilizzare il metodo Show- Dialog (descritto nei numeri precedenti) pertanto si de- ve scrivere il codice seguente: Private Sub ButtonCalcola_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles ButtonCalcola.Click Dim FBase As New FormBaseQ FBase.ShowDialogQ End Sub La finestra FormBase viene utilizzata per richiedere all'utente di inserire i dati relativi alla base del rettan- golo, quindi è importante sapere in che modo viene chiusa tale finestra, in altre parole se ha prodotto un risultato. Se ad esempio l'utente chiude la finestra dalla crocetta in alto a destra, i dati inseriti non vengono memorizzati ma eliminati. Per verificare come viene chiusa una finestra di dialogo si può utilizzare la pro- prietà DialogResult. In fase di progettazione è possibile impostare la proprietà DialogResult per tutti i controlli Button presenti nella finestra di dialogo. Nella finestra FormBase inseriamo una Label, un TextBox e due pul- santi. Visualizziamo la finestra delle proprietà e varia- mo le proprietà dei controlli appena disegnati: • Selezionando Labell variamo la proprietà Text in Base. • Selezionando TextBoxl variamo la proprietà Name in TextBoxBase e la proprietà Text nella stringa vuota. • Selezionando Buttonl variamo la proprietà Name in ButtonOk, la proprietà Text in Ok e la proprietà DialogResult in OK. http://www.itportal.it • Selezionando Buttonl variamo la proprietà Nume in ButtonCancella, la proprietà Text in Cancella e la pro- prietà DialogResult in Cancel. Dalla finestra (nel nostro caso FormCalcola) che visua- lizza la finestra di dialogo, chiamata anche form padre della finestra di dialogo, è possibile utilizzare il valore della proprietà DialogResult per stabilire se è stato scel- to OK o Annulla. In base alla proprietà DialogResult restituita si può decidere se è necessario recuperare o meno le informa- zioni della finestra di dialogo. If FBase.DialogResult = DialogResult.OK Then base = CDbl(FBase.TextBoxBase.Text) Else base = End If In maniera analoga disegniamo la finestra Form Altezza. Il risultato dell'operazione di calcolo dell'area del ret- tangolo, sarà infine visualizzato in una finestra di dia- logo predefinita, utilizzando MessageBox. Private Sub ButtonCalcola_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles ButtonCalcola.Click Dim base As Doublé Dim altezza As Doublé Dim risultato As Doublé Dim FBase As New FormBaseQ FBase. ShowDialogQ If FBase.DialogResult = DialogResult.OK Then base = CDbl(FBase.TextBoxBase.Text) Else base = End If Dim FAItezza As New FormAltezzaQ FAItezza.ShowDialogQ If FAItezza.DialogResult = DialogResult.OK Then altezza = CDbl(FAItezza.TextBoxAltezza.Text) Else altezza = End If risultato = base * altezza MessageBox.Show(risultato) End Sub LA FINESTRA DI DIALOGO MESSAGEBOX La finestra di dialogo MessageBox è senza dubbio quel- la che vi troverete ad utilizzare più spesso. Questa fine- stra permette di mostrare messaggi personalizzati all'utente, ed è in grado di accettare scelte tramite uno o più pulsanti. Normalmente è composta da quattro parti: • La barra del titolo che identifica lo scopo della fine- stra di dialogo, ad esempio "Richiesta Conferma' . • Il messaggio che appare nella finestra, ad esempio "Sei sicuro di voler continuare?" • Un'icona in grado di attirare l'attenzione, ad esem- pio l'icona con il punto di domanda. • Uno o più pulsanti di comando, ad esempio i pul- santi Si e No. Per visualizzare la finestra di messaggio, si deve utiliz- zare il metodo Show. Il metodo Show può essere richia- mato in più modi, riportiamo di seguito la sintassi di quelli più comuni: MessageBox.Show(testo) MessageBox.Show(testo, etichetta) MessageBox.Show(testo, etichetta, bottone, icona) MessageBox. Show(testo, etichetta, bottone, icona, bottoneDiDefault) In cui: • testo - rappresenta il messaggio che verrà visualiz- zato air interno della finestra di dialogo. Può essere una qualsiasi stringa ed è l'unico parametro obbli- gatorio. • etichetta - rappresenta la stringa visualizzata nella barra del titolo della finestra. Se viene omesso, nella barra del titolo non apparirà nessun testo. • bottone - permette di visualizzare uno o più pul- santi, tra quelli disponibili, nella finestra di dialogo. Se viene omesso, verrà visualizzato il bottone OK. • icona - permette di visualizzare un'icona, tra quel- le disponibili, nella finestra di dialogo. Se viene omesso, non verrà visualizzata nessun'icona. • BottoneDiDefault - permette di specificare quale pulsante, tra quelli visualizzati, verrà impostato come predefinito. L'uso di questo parametro, con- sente all'utente di leggere il messaggio e di preme- re il tasto Invio per indicare l'azione del pulsante predefinito. Analizziamo ora i possibili valori per bottone, icona e BottoneDiDefault. Le icone che si possono visualizzare nella finestra MessageBox sono: • Asterisk, Information - consentono di visualizzare un'icona contenente un simbolo formato da una lettera i minuscola racchiusa da un fumetto. • Error, Hand, Stop - consentono di visualizzare un'icona contenente un simbolo formato da una X bianca racchiusa da un cerchio su sfondo rosso. • Exclamation, Warning - consentono di visualizza- re un'icona contenente un simbolo formato da un punto esclamativo racchiuso da un triangolo su sfondo giallo. • Question - consente di visualizzare un'icona con- tenente un simbolo formato da un punto interroga- tivo racchiuso da un fumetto. Visual Basic .NET Altre proprietà OpenFileDialog a: I FILTERINDEX, in- | dica l'indice del filtro attualmente sele- zionato nella finestra di dialogo. INITIALDIRECTORY, Indica la directory ini- ziale visualizzata dalla finestra di dialogo. MULTISELECT, ottiene o imposta un valore che indica se la finestra di dialogo consente la se- lezione multipla di file. READONLYCHECKED, ottiene o imposta un valore che indica se è selezionata la casella di controllo di sola lettura. RESTOREDIRECTORY, indica se la finestra di dialogo ripristina la di- rectory corrente prima della chiusura. SHOWHELP, indica se viene visualizzato il pul- sante di help nella fine- stra di dialogo. SHOWREADONLY, indi- ca se la finestra di dia- logo contiene una ca- sella di controllo di sola lettura. TITLE, indica il titolo della finestra di dialogo che viene visualizzato nella barra del titolo. http://www.itportal.it 3 ►►► 71 C o r s B a s e ► ► ► ► Visual Basic .NET Alcune proprietà di ColorDialog VyW\ ALLOWFULLOPEN \-J\ indica se l'uten- te può utilizzare la fi- nestra di dialogo per definire colori perso- nalizzati. AIMYCOLOR, indica se nella finestra di dialo- go sono visualizzati tutti i colori disponi- bili nel set di colori di base. COLOR CONSENTE, di ottenere o impostare il colore selezionato dall'utente. CUSTOMCOLORS, indica il gruppo di co- lori personalizzati vi- sualizzato nella fine- stra di dialogo. FULLOPEN, indica se i controlli utilizzati per creare colori perso- nalizzati sono visua- lizzati all'apertura della finestra di dia- logo. SOLIDCOLORONLY, indica se nella fine- stra di dialogo la scelta sarà consenti- ta soltanto ai soli co- lori in tinta unita Le combinazioni di pulsanti che si possono visualizza- re nella finestra MessageBox sono: • AbortRetrylgnore - indica che la finestra di mes- saggio contiene i pulsanti Interrompi, Riprova ed Ignora. • OK - indica che la finestra di messaggio contiene il pulsante OK. • OKCancel - indica che la finestra di messaggio contiene i pulsanti OK ed Annulla. • RetryCancel - indica che la finestra di messaggio contiene i pulsanti Riprova ed Annulla. • YesNo - indica che la finestra di messaggio contie- ne i pulsanti Sì e No. • YesNoCancel - indica che la finestra di messaggio contiene i pulsanti Sì, No ed Annulla. Quando l'utente preme uno dei tasti riportati nella fi- nestra di dialogo, viene valorizzata la proprietà Dialog- Result corrispondente. I valori ammessi, per indicare che un pulsante di una finestra MessageBox è quello predefinito, sono: • DefaultButtonl - indica che il primo pulsante nella finestra messaggio deve essere il pulsante predefinito. • DefaultButton2 - indica che il secondo pulsante nella finestra messaggio deve essere il pulsante predefinito. • Def aultButton3 - indica che il terzo pulsante nella finestra messaggio deve essere il pulsante predefi- nito. Il pulsante predefinito prescelto viene valorizzato in ordine da sinistra verso destra. Se avete visualizzato i pulsanti AbortRetrylgnore ed impostate il valore di Bot- toneDiDefault su DefaultButton3, allora il pulsante pre- definito sarà il pulsante Ignora. Supponiamo ora di aver scritto un'applicazione che salva alcuni dati su database, se l'utente distratto chiude il programma prima di aver salvato effettiva- mente i dati è bene avvisarlo. In questo caso possiamo scrivere il codice seguente che visualizza il messaggio di avviso riportato in figu- ra: If MessageBox. Show("Vuoi salvare i dati prima di uscire ?", "Richiesta Conferma", MessageBoxButtons. YesNo- Cancel, MessageBoxIcon.Question, MessageBoxDefault- Button.Button3) = DialogResult.No Then Exit Sub I CONTROLLI FINESTRE DI DIALOGO VB.NET mette a disposizione alcuni controlli che per- mettono di visualizzare finestre di dialogo standard, per utilizzarli all'interno della vostra applicazione si deve selezionare il controllo prescelto nella casella degli strumenti e trascinarlo sulla form (al solito essen- do un controllo invisibile in fase di esecuzione verrà mostrato nella barra delle componenti). In particolare descriveremo i seguenti controlli finestre di dialogo standard: • OpenFileDialog - consente di aprire un file. • SaveFileDialog - consente di selezionare un file da salvare e la posizione in cui deve essere salvato. • ColorDialog - consente di selezionare o aggiunge- re un colore da una tavolozza predefinita. • FontDialog - consente di selezionare un tipo di carattere tra quelli installati nel sistema. • PrintDialog - consente di selezionare una stam- pante e le pagine da stampare. • PageSetupDialog - consente di impostare i detta- gli di una pagina per la stampa. Per visualizzare una finestra di dialogo standard, si de- ve utilizzare il metodo ShowDialog, così come si è visto in precedenza. Ad esempio nel caso di un controllo OpenFileDialog (dal nome OpenFileDialog!) si deve scrivere: OpenFileDialog l.ShowDialog() Anche le finestre di dialogo standard restituiscono la proprietà DialogResult. Ad esempio nel caso di un con- trollo OpenFileDialog, la proprietà DialogResult restitui- sce i valori Ok o Cancel che corrispondono, rispettiva- mente, ai pulsanti Apri e Annulla della finestra di dia- logo. L'utilizzo di questi controlli evita la necessità di scrive- re codice per implementare il disegno dell'interfaccia, ma resta sempre a carico del programmatore la scrittu- ra del codice. Il controllo OpenFileDialog La finestra di dialogo Apri permette agli utenti di navi- gare tra le cartelle del proprio computer locale o di qualsiasi computer in rete e di selezionare un file da aprire. La finestra di dialogo restituisce il percorso completo ed il nome del file selezionato nella finestra. Il controllo non apre e legge un file, ma è semplice- mente un'interfaccia che permette ad un utente di indi- viduare e specificare il file che l'applicazione deve aprire. È possibile utilizzare la proprietà Filterlndex per impostare l'estensione dei file che sarà possibile visua- lizzare, ad esempio soltanto i file testo con estensione .TXT. La stringa che definisce il filtro è composta da due elementi separati da una barra verticale (per inten- derci il simbolo che si trova di solito di fianco al tasto "1"), l'etichetta che appare nella casella di riepilogo Tipo File ed il filtro stesso. Se ad esempio si vuole permettere la visualizzazione dei file di tipo testo con estensione *.txt Text files (*.txt)|*.txt Si possono anche definire dei filtri multipli, in questo 72^^^ F e b b http://www.itportal.it caso ogni filtro deve essere separato dalla barra verti- cale: Files di testo (*.txt)|*.txt| Tutti i files (*.*)|*.* In caso di filtri multipli, per definire il filtro visualizza- to di default si deve impostare la proprietà Filterlndex ponendola uguale all'indice numerico del filtro desi- derato, nel caso precedente si deve impostare Filter- lndex pari ad 1 oppure a 2. Per stabilire quale file è stato selezionato dall'utente si deve utilizzare la proprietà FileName, che memorizza il nome del file preceduto dal percorso completo in cui si trova. Se l'utente clicca sul tasto Annulla la proprietà FileName sarà pari alla strin- ga vuota "" . Il controllo SaveFileDialog La finestra di dialogo Salva con Nome assomiglia molto alla finestra Apri. Le uniche differenze risiedono nelle etichette e nella barra del titolo, dove al posto del testo apri viene visualizzato il testo Salva. Anche per questo controllo è possibile definire dei filtri sull'estensione del file da salvare con le stesse modalità del controllo OpenFileDialog. Le uniche differenze stanno nella pre- senza di altre proprietà come ad esempio la proprietà CreatePrompt o OverwritePrompt che poste a True visua- lizzano un messaggio nel caso venga creato un nuovo file o si sovrascriva un file esistente. Naturalmente il controllo non salva il file in automatico. Il controllo ColorDialog Il controllo ColorDialog permette di visualizzare la fine- stra di dialogo Colore che consente all'utente di selezio- nare un colore o di crearne di personalizzati. Quando l'utente chiude la casella di dialogo, il colore seleziona- to viene memorizzato nella proprietà Color. .olore Minta r unita Luminosità: OK | Annulla | Aggiungi ai colori personalizzati Fig. 1: Finestra di dialogo per la selezione di un colore. Ad esempio si può assegnare il colore selezionato alla proprietà ForeColor o BackColor di un altro controllo (ad esempio un TextBox) TextBoxl. ForeColor = ColorDialogl. Color La matrice CustomColor contiene tutti i colori perso- nalizzati definiti dall'utente. Il controllo FontDialog Il controllo FontDialog permette di visualizzare la fine- stra di dialogo Carattere che consente all'utente di sele- zionare diversi caratteri, stili e dimensioni. Ogni volta che l'utente seleziona un'opzione, questa finestra vi- sualizza una casella di anteprima con il carattere cor- rispondente alle scelte effettuate. Per recuperare le scelte effettuate dall'utente il con- trollo FontDialog espone le proprietà: • MinSize - ottiene o imposta la dimensione mini- ma selezionabile da un utente, espressa in punti. • MaxSize - ottiene o imposta la dimensione mini- ma selezionabile da un utente, espressa in punti. • FontMustExist - indica se nella finestra di dialogo viene descritta una condizione di errore quando l'utente cerca di selezionare un tipo di carattere o uno stile inesistente. • ShowEffects - ottiene o imposta un valore che indica se nella finestra di dialogo sono inclusi con- trolli che consentono all'utente di specificare opzioni di testo quali il barrato, la sottolineatura e il colore. • AllowVerticalFonts - ottiene o imposta un valore che indica se nella finestra di dialogo sono visua- lizzati sia tipi di carattere verticali sia orizzontali oppure soltanto orizzontali. • AllowVectorFonts - ottiene o imposta un valore che indica se la finestra di dialogo consente di selezionare tipi di carattere vettoriali. Ed infine la proprietà Font, che imposta o restituisce il font selezionato. Il controllo PrintDialog Il controllo PrintDialog permette di visualizzare la finestra di dialogo Stampa che consente all'utente di selezionare la stampante, il numero di copie e le pagi- ne da stampare. Prima di utilizzare il controllo Print- Dialog, è necessario inserire nella form il controllo PrintDocument ed associarlo al controllo PrintDialog tramite la proprietà Document (cliccando sulla freccia rivolta verso il basso accanto alla proprietà apparirà l'elenco degli oggetti PrintDocument selezionabili). L'oggetto PrintDocument espone la proprietà Docu- mentarne che "informa" la finestra di dialogo Stampa quale documento deve essere stampato. Il controllo PageSetupDialog Il controllo PageSetupDialog permette di visualizzare la finestra di dialogo Imposta pagina che consente all'u- tente di stabilire l'orientamento della pagina (verticale o orizzontale), le dimensioni del foglio e le dimensio- ni per i quattro margini della pagina. Anche in questo caso, prima di utilizzare il controllo PageSetupDialog, è necessario inserire nella form il controllo PrintDocu- ment ed associarlo al controllo PageSetupDialog trami- te la proprietà Document. Ing. Luigi Buono Visual Basic .NET MSG Box Ó Un metodo alter- nativo per visua- lizzare le finestre di messaggio è l'utilizzo del comando MsgBox. Il comando MsgBox era il comando utiliz- zato in VB6 che è stato conservato an- che in VB.NET. Le fi- nestre messaggio pro- dotte utilizzando Msg- Box appaiono identi- che a quelle scritte con MessageBox, la differenza è tutta nel- la scrittura del codice. La sintassi è la se- guente: MsgBox(testo, setDibottoni, etichetta) I parametri testo ed etichetta visualizzano il messaggio ed il tito- lo, così come in Mes- sageBox, il parametro set Di Bottoni ammette, invece, una combina- zione dei parametri: bottone, icona e bot- toneDiDefault separati dal segno più (+). Per visualizzare il messag- gio di esempio ripor- tato nell'articolo, uti- lizzando MsgBox si de- ve scrivere: MsgBox("Vuoi salvare i dati prima di uscire ?", MsgBoxStyle.YesNoCancel + MsgBoxStyle.Question + MsgBoxStyle .DefaultButton3, "Richiesta Conferma") http://www.itportal.it 3 ►►► 73 Passaggio DI ARGOMENTI, TRE CASI PARTICOLARI Il passaggio di argomenti ad un metodo, insieme con la ricezione dei valori di ritorno, costituisce uno dei capisaldi della programmazione orientata agli oggetti, tanto con C# quanto con altri linguaggi di programmazione. In questa lezione esamineremo tre interessanti tecniche messe a disposizione da C#, utili per il passaggio di argomenti in situazioni particolari. File sul CD \soft\codice \codici_csharp.zip Già abbiamo parlato di metodi e di pas- saggio di argomenti, in maniera ele- mentare. In questa lezione andremo ad approfondire l'argomento, esaminando alcune tecniche utili per fornire argomenti in situazio- ni particolari. Saranno esaminate le tre parole chiave ref, out e params. PASSAGGIO DI ARGOMENTI CON REF C#, come è stato già detto nel corso delle pre- cedenti lezioni, distingue tra due macro-insie- mi di dati: quelli gestiti per valore e quelli am- ministrati per riferimento. Il tipo di gestione at- tuato dal CLR di .NET costringe a delle rifles- sioni, soprattutto nel momento in cui un valore è trattato come argomento di ingresso di un qualsiasi metodo. Riassumendo, i tipi valore sono sempre copiati e duplicati, mentre i tipi riferimento utilizzano alias differenti per indicare una medesima area della memoria. Quando un tipo valore, ad esempio un int, è usato come argomento di ingresso, il CLR ne esegue automaticamente una copia da affidare al codice contenuto nel corpo del metodo che lo riceve. Modificando la copia, l'originale rimane inalte- rato. E' possibile variare il comportamento pre- definito che l'esecutore assume nei confronti dei tipi valore, servendosi della parola chiave ref. Prendiamo in esame il seguente esempio: class ScambiaVariabili { public static void scambiaSenzaRef(int a, int b) A int aux = a; a = b; b = aux; _} public static void scambiaConRef(ref int a, ref int b) A int aux = a; a = b; b = aux; _} public static void mostraValori(int il, int i2) A System.Console.WriteLine("il vale " + il); System.Console.WriteLine("i2 vale " + i2); _} public static void MainQ A int il = 5; int i2 = 3; mostraValori(il, i2); scambiaSenzaRef(il, i2); System. Console. Writel_ine(" Dopo scambiaSenzaRef"); mostraValori(il, i2); scambiaConRef(ref il, ref i2); 74 ►►► F e b b http://www.itportal.it System .Console. WriteLine("Dopo scambiaConRef"); mostra Valori(il, i2); La classe ScambiaVariabili comprende due me- todi dal corpo identico. Il primo, s cambia Senza- RefO, è formulato in maniera classica: public static void scambiaSenzaRef(int a, int b) { int aux = a; a = b; b = = aux; } All'interno del corpo del metodo avviene uno scambio tra i valori conservati in a ed in b. Tut- tavia, questa operazione non ha alcun riscontro pratico. Gli interi a e b, infatti, sono sempre del- le copie dei valori originariamente forniti al metodo. Lo dimostra la prima parte dell'output restitui- to dal programma: il vale 5 il vale 3 Dopo scambiaSenzaRef il vale 5 il vale 3 Le variabili il e il dichiarate all'interno del me- todo Maini), come è possibile osservare, non hanno subito modifiche. È più che lecito che sia così: il e il sono passate per valore, ogni modi- fica sulle copie non si riflette sugli originali. Di- versamente avviene con il metodo scambiaCon- RefO, benché il corpo impiegato sia il medesi- mo: public static void scambiaConRef(ref int a, ref int b) { int aux = a; a = b; b = aux; } L'unica differenza riscontrabile è nella lista de- gli argomenti di ingresso. Alla dichiarazione dei due interi a e b, infatti, è stato fatto prece- dere lo specificatore ref. La stessa parola chiave deve essere nuovamente digitata nel momento in cui il metodo viene richiamato: scambiaConRef (ref il, ref i2); guirà delle copie. Pertanto, una modifica sul contenuto di a si rifletterà su il, così come va- riando b si cambierà anche il. Lo dimostra l'output prodotto dal programma. Dopo scambiaConRef il vale 3 il vale 5 I valori conservati nelle variabili, in questo ca- so, sono stati invertiti. Il metodo, così formula- to, ha finalmente un senso pratico, che altri- menti non avrebbe. PASSAGGIO DI ARGOMENTI CON OUT Gli argomenti forniti ad un metodo devono sempre essere inizializzati prima di essere for- niti ad un metodo, indipendentemente dal loro tipo. In generale, vale sempre la regola "prima dichiari, poi inizializzi e quindi utilizzi". Il pas- saggio di argomenti, in effetti, è un vero e pro- prio utilizzo delle variabili. Senza l'inizializzazione, una variabile non può essere sfruttata, almeno in situazioni normali. Prendiamo in considerazione il seguente codi- ce: public static void MainQ { int a = 5; int b = 3; int e; somma(a, b, ref e); System.Console.WriteLine("c vale " + e); } Non è possibile compilare questa classe, poiché l'intero e non è stato inizializzato prima di es- sere passato, per riferimento, al metodo som- mai). L'errore che se ne ricava è mostrato di seguito: error CS0165: Utilizzo della variabile locale "e" non assegnata. Argomenti class OutTestl 1 r&\ Gli argomenti \-J\ forniti ad un metodo devono sem- { public static void somma(int a, int b, ref int e) pre essere inizializ- zati prima di essere { forniti ad un metodo, indipendentemente dal loro tipo. e = a + b; } In questo caso, gli interi il e il vengono trasfe- riti al metodo per riferimento. Il CLR non ese- Nonostante questo, esistono dei casi in cui si può desiderare di fare uso di una variabile non http: //www. itportal.it 3 ►►► 75 Dispense Web [7£] Tra le dispense r-J\ Web del corso troverete appunti, ag- giunte, approfondi- menti, risposte a do- mande frequenti e al- tre appendici legate alla lezione odierna. L'indirizzo di riferi- mento è http://www.sauronsoft - ware.it/dispenseweb/ csharp/ inizializzata. La seguente variante del prece- dente esempio ci introduce all'utilizzo della pa- rola chiave out: class OutTest2 { public static void somma(int a, int b, out int e) _i e = a + b; _} public static void MainQ _i int a = 5; int b = 3; int e; somma(a, b, out e); System.Console.WriteLine("c vale " + e); > > OutTestl è stata ottenuta sostituendo la parola out alle clausole ref già presenti in OutTestl. Di- venta ora possibile compilare ed eseguire il co- dice. Sostanzialmente, il passaggio di parame- tri attraverso la parola chiave out porta a due conseguenze: 1. La variabile fornita viene trasferita sempre e comunque per riferimento, anche se è un tipo valore. 2. Non è richiesto che la variabile fornita sia inizializzata, giacché out costituisce una tec- nica che si affianca a return nella restituzio- ne di valori al codice chiamante. Ad ogni modo, perché si dovrebbe utilizzare out per la restituzione di valori, quando si ha già a disposizione il ben più comodo return 7 . Semplicemente, potrebbe capitare che return non sia sufficiente. Con return è possibile resti- tuire un solo dato alla volta, mentre con out è possibile avere quanti valori di ritorno si desi- derano. Il vantaggio di out, dunque, non è evi- dente con il metodo sommai), giacché questo poteva più abilmente essere scritto nella forma: { public static int somma(int a, int b) { return a + b; } public static void perimetroAndArea(int I, out int per, out int area) { per =1*4; area = 1*1; _} public static void MainQ i int I = 5; int p, a; perimetroAndArea(l, out p, out a); System.Console.WriteLine("Perimetro: " + p); System-Console. WriteLine("Area: " + a); } Prendiamo ora in considerazione questa nuova classe: class OutTest3 } Il metodo perimetro And AreaO calcola il perime- tro e l'area di un quadrato di lato 1, in un colpo solo. Impiegando return, non sarebbe stato pos- sibile compiere entrambi i calcoli con una sin- gola funzione: si sarebbe potuto restituire sola- mente uno dei due valori calcolati, l'altro sa- rebbe andato perso. Ad ogni modo, return e out non si escludono a vicenda: in particolari situazioni, è possibile realizzare metodi che restituiscano un valore tramite return ed altri tramite out, simultanea- mente e senza conflitti. PASSAGGIO DI ARGOMENTI CON PARAMS In tutti i casi esaminati sinora, il numero di ar- gomenti forniti ad un metodo era sempre noto a priori. Ad esempio, il metodo sommai) com- preso in OutTestl accetta sempre tre argomenti: i due addendi e la variabile per la restituzione della loro somma. Ovviamente, sommai) non può sommare più di due addendi alla volta. Può essere interessante, invece, gestire un nu- mero di argomenti non noto a priori, ad esem- pio per sommare n addendi: class ParamsTest { public static int somma(params int[] addendi) _J int totale = 0; for (int i = 0; i < addendi.Length; i++) { totale += addendi[i]; } return totale; _} public static void MainQ { 76+» F e b b http://www.itportal.it int ti = somma(5, 3); System .Console. WriteLine(tl); int t2 = somma(2, 5, 1, 10, 9); System .Console. WriteLine(t2); int t3 = somma(6, 3, 8); System .Console. WriteLine(t3); La clausola params permette di gestire un nu- mero variabile di argomenti. Le tre chiamate: sono tutte valide e riferite allo stesso metodo, benché differiscano nel numero degli argomen- ti specificati. Con la dicitura: params int[] addendi abbiamo fatto in modo che il metodo sommai) dell'esempio corrente possa gestire un numero imprecisato di argomenti di tipo int. Di fatto, quello che viene fornito al corpo del metodo è un array di valori int. Questo corso ancora non ha esaminato nel dettaglio gli array. Per il mo- mento, è sufficiente sapere che un array è una collezione di valori del medesimo tipo. Con la proprietà: nomeArray.Length è possibile sapere quanti elementi ci sono nel- l'insieme. Ad ogni elemento di un array corri- sponde un indice numerico, da zero in poi. Il primo elemento di un array ha indice 0, il se- condo 1, il terzo 2 e così via. L'ultimo elemento ha sempre indice Length -2.1 singoli elementi della collezione possono essere selezionati at- traverso una coppia di parentesi quadre: nomeArray[0] nomeArray[l] nomeArray[2] nomeArray [nomeArray.Length - 1] In seguito, analizzeremo più nel dettaglio le collezioni e gli array, giacché questi costituisco- no una categoria di oggetti molto importante per la programmazione di applicazioni raffina- te. CONCLUSIONI C# è un linguaggio molto ricco, benché sempli- ce. Esistono numerosi stratagemmi per abbre- viare le operazioni di stesura del codice. Le tre caratteristiche esaminate in questa lezione, ad esempio, non sono indispensabili in un lin- guaggio orientato agli oggetti di derivazione Java, giacché possono essere rimpiazzate da tecniche alternative più complesse, ma lo stes- so efficaci. Proprio il linguaggio Java, ad esem- pio, non comprende nessuna delle tre parole chiave esaminate in questa sede, poiché i pro- gettisti di Sun le hanno giudicate ridondanti (e confusionarie). Più in generale, quindi, C# deve molto all'e- sperienza di Java, ma trae anche spunto dalle abitudini di C e C++, che gli sviluppatori han- no imparato ad apprezzare nel corso degli an- ni. Carlo Pelliccia C# Guida per lo sviluppatore ISBN: 88-203-2962-X Autore: Simon Robinson e altri Editore: Hoepli Pagine: 1188 Costo: € 55,78 Target: programmatori abbastanza esperti Con l'avvento di .NET, gli scaffali delle li- brerie cominciano a popolarsi delle prime edizioni dei manuali dedicati alla nuova piattaforma Microsoft. C#, ovviamente, è uno degli argomenti più discussi e richiesti dalla comunità degli sviluppatori. È comprensibile: conoscere C#, di fatto, si- gnifica avere la padronanza di tutti i mecca- nismi del nuovo framework, giacché al suo interno sono contenute le strutture che me- glio astraggono le funzionalità offerte da .NET. E poi, non dimentichiamolo, C# è un linguaggio nuovo (almeno nel nome ed in alcune caratteristiche), che incuriosisce e desta l'interesse dell'intera comunità degli sviluppatori Windows. Il manuale in questione si rivolge ai pro- grammatori mediamente esperti che già vantino nel loro curriculum esperienze con linguaggi analoghi a C#, soprattutto Java e C+ + . Il volume, come è lecito attendersi, presenta nei primi capitoli un'introduzione ai concetti di base del nuovo framework. Si parla quindi di .NET, di linguaggio interno, di codice gestito, di assembly e di altro an- cora. Viene poi introdotto il linguaggio C#. In tutti i capitoli più espressamente dedica- ti alle strutture del linguaggio, gli autori si sforzano di illustrare la programmazione orientata agli oggetti anche a chi proviene da un background differente, come i pro- grammatori Visual Basic. BSD Bibliografia • GUIDA A C# Herbert Schildt (McGraw-Hill) ISBN 88-386-4264-8 2002 • INTRODUZIONE A C# Eric Gunnerson (Mondadori Informati- ca) ISBN 88-8331-185-X 2001 • C# GUIDA PER LO SVILUPPATORE Simon Robinson e altri (Hoepli) ISBN 88-203-2962-X 2001 http: //www. itportal.it 3 ►►► 77 Risoluzione DI AMBIGUITÀ ED EREDITARIETÀ MULTIPLA Abbiamo visto una panoramica del concetto di ereditarietà, che rappresenta una delle differenze sostanziali tra programmazione procedurale e programmazione Object-Oriented, nonché una delle caratteristiche più importanti degli oggetti. Adesso scenderemo un pò 1 più nel dettaglio con nuovi concetti. File sul CD \soft\codice\C+ + .zip Nel precedente appuntamento si è esaminato come si possa ridefinire nella classe derivata ognuna delle funzioni particolari, tipiche di una classe (cioè il costruttore, il distruttore e l'operato- re di assegnazione), facendo eventualmente uso di quelle della classe base. Adesso, siccome siamo curio- si, ci chiediamo: possiamo ridefinire, nella classe deri- vata, una qualunque delle funzioni della classe base? Sebbene apparentemente sembri un problema banale, in cui la risposta affermativa ci sorge spontanea (quan- tomeno per trasporto emotivo), tuttavia l'apparenza, al solito, inganna: la soluzione praticamente banale è però molto insidiosa, e solo il bravo programmatore C++ sa come evitarne i pericoli. Supponiamo di avere le (solite) classi Scheda e Scheda Avanzata, e supponiamo che la derivazione tra le due sia di tipo public, mentre i campi nome e telefono siano stati dichiarati protected in Scheda: queste ultime ipotesi ci servono solo per sem- plificare il discorso, non sono necessarie. Il risultato delle nostre precondizioni, che supporremo verificate d'ora in avanti, è che i campi nome e telefono della clas- se Scheda sono a questo punto ereditati come protetti nella classe SchedaAvanzata (se necessario, ricontrollate lo schema introdotto la scorsa puntata). A questo punto, ignorando la possibilità di ridefinire l'operatore «, decidiamo di aggiungere un metodo al- la classe Scheda che si occupi di stampare, con oppor- tuna formattazione, il suo oggetto di invocazione. Una possibile definizione potrebbe essere: //nel file scheda.cpp void Scheda: :StampaFormattata() { cout << "+ +\n"; cout << "| " << nome << " |\n"; cout << "+ +\n"; cout << " telefono: " << telefono << "\n"; In questa funzione, abbiamo trascurato la lunghezza della stringa nome, supponendo che sia lunga il giusto per apparire centrata nell'etichetta: in realtà dovrem- mo gestire da noi la cosa, ricavandoci la lunghezza di tale stringa e completando la stampa, con l'inserimen- to di opportuni caratteri di spaziatura oppure allun- gando l'etichetta del necessario. Potrebbe essere un uti- le esercizio per il lettore volenteroso. Il metodo appena scritto (supposto public nella classe Scheda) viene ades- so ereditato dalla classe SchedaAvanzata, rendendo così possibili operazioni del tipo: Scheda pienno("Pienno","61010610rimarrai"); SchedaAvanzata piero("Piero Pierrot", "0123456789", "pierrot@maschere.it"); pierino.StampaFormattataQ; piero.StampaFormattata(); Si presenta tuttavia adesso un problema. Quando ab- biamo deciso di creare la classe SchedaAvanzata, non avevamo l'intenzione di creare una nuova classe svin- colata dalla precedente: la nostra intenzione era di estendere la classe Scheda, usandola come base per la nuova classe. L'intenzione era poter inserire in un og- getto SchedaAvanzata ulteriori informazioni oltre quel- le inseribili in un analogo oggetto Scheda. Se adesso usiamo per la stampa formattata il metodo appena de- finito, il contenuto informativo che ha in più un ogget- to SchedaAvanzata rispetto ad un analogo oggetto Sche- da è come se non esistesse: in altre parole Scheda e Sche- daAvanzata dal punto di vista di questo metodo risul- tano indistinguibili, e il contenuto di un oggetto Sche- daAvanzata non è visualizzato in maniera completa (in quanto manca la stampa del valore del campo email). Non possiamo usare il campo email nel nostro metodo, perché esso fa parte della classe derivata (SchedaAvan- 78^^ F e b b http: //www. itportal.it zata) e quindi nella classe base (Scheda) non esiste; non possiamo semplicemente spostare il metodo, opportu- namente modificato per la stampa del campo mancan- te, nella classe derivata, poiché si perderebbe la fun- zionalità, rappresentata dal metodo, nella classe base (in altre parole, potremmo stampare con questo meto- do solo oggetti di tipo Scheda Avanzata, ma non esiste- rebbe un analogo metodo per gli oggetti Scheda). Po- tremmo scrivere un metodo apposito per la classe de- rivata, ma (sebbene corretto) si costringerebbe l'utente della classe derivata e della classe base a ricordare la particolarità: è uno sforzo mnemonico superfluo. Pos- siamo però fare la cosa giusta, oltre che auspicabile ed intuitiva: ridefiniamo nella classe derivata lo stesso metodo della classe base. UN ESEMPIO DI RIDEFINIZIONE La ridefinizione si usa ogni volta che si vuole far corri- spondere una funzione della classe derivata ad una funzione della classe base, della quale l'omonima ap- partenente alla classe derivata costituisce una specia- lizzazione. Quando ridefiniamo una funzione nella classe derivata, essenzialmente basta che ci comportia- mo come se non esistesse un analogo metodo nella classe base. Scriviamo così il nostro "nuovo" metodo nella classe derivata: A questo punto, il codice di prova scritto nel paragrafo precedente funziona in maniera corretta, stampando il contenuto del campo email per (e solo per) gli oggetti di tipo Scheda Avanzata. Si deve, però, prestare attenzione ad un aspetto relativamente a questo meccanismo di ri- definizione: quando ridefiniamo un metodo nella clas- se derivata, essa maschera nella classe derivata tutte le funzioni ad essa omonime presenti nella classe base (cioè, il mascheramento avviene solo guardando il no- me della funzione, senza guardare le liste dei parame- tri: in altre parole, non è un overload!). Ad esempio, avendo una generica classe X con la seguente dichiara- zione: class X { public: F(int); F(char); }; e definendo una classe derivata Y nel seguente modo: class Y: public X { public: F(); }; si avrebbe che il metodo Y::F() maschera entrambi i metodi Xr.F(int) ed X::F(char), sebbene abbiano tra loro liste di parametri diverse. Quindi il seguente codice darebbe un errore: Y dummy; dummy.F(lO); //sbagliato: il metodo invocato non esiste! e questo perché il metodo F(int), sebbene presente nel- la classe base, è diventato invisibile nella classe deriva- ta in quanto mascherato dal metodo Y::F(), senza para- metri. I metodi della classe base non sono, però, per- duti per sempre: per accedervi basta usare l'operatore di scope resolution, "::". Tale operatore, incontrato già parlando delle definizioni delle funzioni di una classe, chiarisce al compilatore quale sia la classe di apparte- nenza di un metodo chiamato: l'uso di questo operato- re non è solo relegato alle definizioni delle funzioni di una classe! Infatti, questo operatore può essere usato ogni volta si renda necessario specificare una cosa del tipo "voglio che venga invocato il metodo M della clas- se C". Questo è proprio ciò che ci serve per accedere (da un oggetto della classe derivata) ai metodi della classe base una volta che essi siano stati ridefiniti nella classe derivata. Possiamo infatti accedere al metodo X::F(int) anche da un oggetto della classe Y, semplice- mente scrivendo: Y dummy; dummy.X::F(10); //giusto! In questo modo si è inteso dire che l'oggetto di invoca- zione dummy richiama il metodo F(int) della classe (base) X. Ovviamente, il metodo invocato tratterà adesso l'oggetto dummy (appartenente alla classe deri- vata) come fosse un oggetto della classe (base) X: que- sto significa che eventuali campi specifici della classe derivata non saranno considerati. Tornando al caso specifico delle classi Scheda e Scheda Avanzata, potrem- mo quindi scrivere: SchedaAvanzata XMen("Wolverine", "1235813", "io@wolverine.tv"); XMen. Scheda : :StampaFormattata(); ottenendo quindi una stampa a schermo come se X- Man fosse un oggetto della classe (base) Scheda invece che della classe (derivata) SchedaAvanzata: il campo email viene, semplicemente, ignorato dalla funzione invocata. EREDITARIETÀ MULTIPLA Una caratteristica molto interessante della program- mazione a oggetti, che il C++ implementa, è l'eredita- rietà multipla. Con questo nome si fa riferimento al Ó Contatta gli autori! Se hai suggeri- menti, critiche, dubbi o perplessità sugli argomenti trat- tati e vuoi proporle agli autori puoi scri- vere agli indirizzi: alfredo.marroccelli® libero.it (Alfredo') marcodelgobbo® libero.it (Marco) Questo contribuirà si- curamente a miglio- rare il lavoro di ste- sura delle prossime puntate. http: //www. itportal.it 3 ►►► 79 Avviso di chiamata Nell'articolo sia le funzioni Sche- Ó da A vanzatar.Imposta- ValiditàO che Scheda- MagneticaA vanzatar.I m postava Hd ita () han- no lo stesso compor- tamento, quindi la dif- ferenza tra il chiamare Cuna o l'altra potrebbe non essere molto evi- dente. Per questo ab- biamo inserito nel co- dice che trovate nel CD allegato, delle stampe a schermo che chiariranno l'effettivo percorso delle chia- mate effettuate. Con- sigliamo di dare un'oc- chiata! Scheda Scheda Avanzata Scheda Avanzatissima [a] Scheda Avanzata e s , Scheda Magnetica Avanzata J * Scheda Magnetica M Fig. 1: Catena di derivazione. meccanismo per il quale è possibile derivare una clas- se da più di una classe base, direttamente. Per "diretta- mente" si intende il fatto che la derivazione avviene nell'arco di un solo livello di ereditarietà, e non nel- l'ambito di una catena di derivazioni. Questo significa che le diverse classi base di una unica classe derivata possono anche essere completamente slegate tra loro e non avere niente in comune (anzi questo è proprio il caso più comune e utile in pratica). Per chiarire meglio questo concetto supponiamo di avere una classe Sche- daAvanzatissima derivata da Scheda Avanzata. La catena di derivazioni è mostrata in Fig. l.a. In questo caso NON si tratta di ereditarietà multipla, in quanto, come si può notare, tutte le classi che sono derivate di altre (cioè SchedaAvanzata e SchedaAvanzatissima) hanno una sola classe base. Il caso dell'ereditarietà multipla è esemplificato invece in Fig. l.b. In questo caso abbia- mo due classi base (SchedaAvanzata e SchedaMagnetica) dalla quale deriva la classe SchedaMagnetica Avanzata. Ma come si comporta un oggetto di una classe deriva- ta tramite ereditarietà multipla? Molto probabilmente il suo comportamento è intuibile dai concetti già ap- presi sull'ereditarietà. Infatti, così come una classe de- rivata in maniera standard racchiude ed estende le ca- ratteristiche della sua classe base, allo stesso modo una classe ottenuta mediante ereditarietà multipla, avrà i campi e le funzioni di tutte le sue classi base, esatta- mente come se ereditasse singolarmente da ciascuna di esse. Per fare un esempio, supponiamo che SchedaMa- gnetica sia definita nel seguente modo: class SchedaMagnetica { public: SchedaMagneticaQ; ~SchedaMagnetica(); void SetCodicePINQ; int GetCodicePINQ; private: int PIN; }; Definendo semplicemente SchedaMagneticaAvanzata come derivata di SchedaMagnetica e SchedaAvanzata, senza definire altri nuovi campi o funzioni, cioè nel se- guente modo: class SchedaMagneticaAvanzata : public SchedaMagnetica, SchedaAvanzata{ //... qui nulla... }; potremmo istanziare un oggetto che ci renda disponi- bili i campi delle due classi: SchedaMagneticaAvanzata sma; sma.Impostal\lome("Attarico II Babbaro"); //funzione di SchedaAvanzata sma.SetCodicePIN("0123"); //funzione di SchedaMagnetica in questo modo avremmo una classe che ingloba le al- tre due in maniera grezza, senza cioè offrire caratteri- stiche aggiuntive o funzionalità ulteriori rispetto a quelle che avremmo istanziando due oggetti diversi. Tuttavia, a volte, questo è utile ed è proprio quello che si vuole. Analogamente a quanto accade per l'eredita- rietà semplice, anche per quella multipla è possibile utilizzare la compatibilità degli oggetti, cioè a dire che un oggetto derivato può essere utilizzato in ogni pun- to in cui è richiesto un oggetto della classe base: void FunzioneDummyl(SchedaAvanzata s) {...} void FunzioneDummy2(SchedaMagnetica s) {...} SchedaMagneticaAvanzata sma; FunzioneDummyl(sma); //uso sma come SchedaAvanzata FunzioneDummy2(sma); //uso sma come SchedaMagnetica Ovviamente, come abbiamo già visto, non è possibile invertire il verso di questa compatibilità. ULTERIORI AMBIGUITÀ Cosa succede nel caso in cui le classi base hanno due funzioni con lo stesso nome? Quale delle due verrà "ereditata" dalla classe derivata e utilizzata quando viene invocata da un suo oggetto? Ci troviamo di fron- te a un caso di ambiguità simile a quello trattato in precedenza, che deve essere risolto in un qualche mo- do. Supponiamo, per fare un esempio, che sia Sche- daAvanzata che SchedaMagnetica abbiano la seguente funzione membro: void ImpostaValidita(bool valida); Scrivendo questo codice: SchedaMagneticaAvanzata sma; sma. Imposta Validita(true); quale funzione verrà chiamata? Quella di SchedaAvan- zata o quella di SchedaMagnetica? In realtà in questo ca- so non esiste alcuna discriminante valida per effettua- re una decisione, pertanto il compilatore segnalerà un errore in questo punto. Si badi bene che anche questo è un caso di mascheramento, per cui il compilatore se- gnalerebbe errore anche qualora le funzioni ImpostaVa- HditaO delle classi base avessero una lista di parametri differente: nel mascheramento conta solo il nome del campo. Cosa si può fare per ovviare a questa situazio- 80 ►►► F e b b r http: //www. itportal.it ne? Esistono diverse strategie. Una prima possibilità è quella di utilizzare, come visto in precedenza nel caso dell'ereditarietà semplice, l'operatore di scope resolu- tion "::" e scrivere, ad esempio: SchedaMagneticaAvanzata sma; sma.SchedaAvanzata: : Imposta Validita(true); così facendo il compilatore avrà le idee chiare su cosa vogliamo intendere. Una seconda possibilità, forse più adatta a questo contesto, è quella di ridefinire nella classe SchedaMagneticaAvanzata la funzione ImpostaVa- UditaO in modo che faccia esattamente ciò che deside- riamo; ad esempio potrebbe chiamare il relativo meto- do di una delle sue classi base: void SchedaMagneticaAvanzata: :ImpostaValidita(bool v) { SchedaAvanzata::ImpostaValidita(v); } o ancora potrebbe ridefinire completamente il com- portamento della funzione in questione, magari facen- dole impostare un nuovo campo privato di tipo boo- leano: void SchedaMagneticaAvanzata: :ImpostaValidita(bool v) { valida = v; //valida è dichiarato private in SchedaMagneticaAvanzata } Quest'ultima soluzione è senz'altro da preferire dal punto di vista semantico, in quanto il concetto di "va- lidità" di una scheda va al di là del fatto che si tratti di una scheda magnetica o quant'altro: una scheda è va- lida oppure non lo è, indipendentemente dalla sua na- tura. FUNZIONI VIRTUALI Abbiamo visto in precedenza come, per il concetto di compatibilità tra oggetto base e oggetto derivato, sia possibile usare quest'ultimo laddove è richiesto il pri- mo. Detta così sembra la cosa più banale del mondo, ma cosa succede nel seguente caso? void RendiValida(SchedaAvanzata& s) { s.ImpostaValidita(true); > SchedaMagneticaAvanzata sma; RendiValida(sma); Si utilizza in questo caso un oggetto di tipo SchedaMa- gneticaAvanzata al posto di uno di tipo Scheda Avanzata. All'interno di RendiValidaO è chiamata ImpostaValiditàO e, si noti bene, il parametro è passato per riferimento, quindi non entra in gioco alcun costruttore di copia. Verrà quindi chiamata la funzione ImpostaValiditàO di SchedaAvanzata (che è il tipo di oggetto che ci aspettia- mo venga passato quando scriviamo la funzione Ren- diValidaO) o quella di SchedaMagneticaAvanzataO (che maschera l'altra)? La risposta è che verrà invocata la funzione di SchedaAvanzata, in quanto, per le funzioni normali, il binding (cioè la scelta di quale funzione chiamare di volta in volta) viene fatto al momento di compilare la funzione (si parla di early binding), quan- do cioè non si sa di quale tipo sarà l'oggetto passato, o meglio, si presume ragionevolmente che esso sia del tipo specificato nei parametri; in questo caso, appunto, SchedaAvanzata. Ma come fare allora a utilizzare una funzione ridefinita in una classe derivata (che presu- mibilmente è stata ridefinita proprio perché quella ba- se non era adatta o sufficiente), sfruttando al tempo stesso la compatibilità? Bisogna introdurre un altro concetto: quello di funzione virtuale. Dichiarare una funzione come virtuale, significa dire al compilatore: "Quando incontri una chiamata a questa funzione non compilare subito il codice relativo, ma aspetta che ven- ga deciso a tempo di esecuzione qual'è la funzione giusta da agganciare qui". Il decidere a tempo di ese- cuzione la funzione da utilizzare si chiama, in lettera- tura informatica, late binding. Sfruttando il late bin- ding in RendiValidaO il codice: SchedaMagneticaAvanzata sma; RendiValida(sma); chiama effettivamente la funzione ImpostaValiditàO di SchedaMagneticaAvanzata, poiché di questo tipo è il pa- rametro passato. Per utilizzare il late binding è necessario che Imposta- ValiditàO in SchedaAvanzata sia dichiarata virtuale, tra- mite la parola chiave virtual: //nel file scheda_adv.h virtual void ImpostaValidita(bool valida); È quindi necessario in ogni caso fare uno sforzo di lun- gimiranza e prevedere a priori quali potranno essere le funzioni soggette a ridefinizione, per le quali è neces- saria la dichiarazione virtual. Anche in questo caso co- munque è bene non cadere nella sindrome del "dichia- ro tutto virtual" in quanto, il late binding è in ogni ca- so un meccanismo attuato a tempo di esecuzione, per cui meno ottimizzato e che potrebbe portare a delle inefficienze. CONCLUSIONI In questa lezione abbiamo trattato alcuni degli argo- menti più interessanti dell'ereditarietà in C++. Tutta- via non è ancora tutto in quanto questo meccanismo consente delle vere e proprie raffinatezze stilistiche e concettuali degne dei migliori programmatori. Noi ve le sveleremo nel prossimo appuntamento in cui parle- remo, tra l'altro di classi base astratte e funzioni vir- tuali pure. Alla prossima! Marco Del Gobbo & Alfredo Marroccelli Funzioni virtuali Dichiarare una funzione come Ó virtuale, significa dire al compilatore: "Quando incontri una chiamata a questa funzione non compi- lare subito il codice relativo, ma aspetta che venga deciso a tempo di esecuzione qual'è la funzione giusta da agganciare qui". http://www.itportal.it 3 ►►► 81 Ja\ Java MEDIA FRAMEWORK: STRUMENTI DI ACQUISIZIONE AUDIO E VIDEO Non bisogna essere degli esperti programmatori per realizzare applicazioni che facciano uso di dispositivi di acquisizione audio e video. Il pacchetto Java Media Framework mette a disposizione tutto quello che è necessario per riuscirci scrivendo soltanto qualche linea di codice! File Sul CD \soft\codice \JMFSourceCode2.zip Processor \/%\ Un Processor è un r^\ particolare tipo di Player che consente di ottenere come output i dati pre-elaborati per la riproduzione, natu- ralmente oltre a con- trollare e gestire la ri- produzione dei conte- nuti multimediali. Una volta istanziato il Pro- cessor, mediante il me- todo getDataOutput() è possibile avere il rife- rimento al DataSource dei dati che vengono prodotti in uscita. La semplicità di utilizzo degli strumenti presenti nei packages JMF è tale che per realizzare un multimedia player non è necessario essere esperti programmatori! Bastano poche istruzioni ed il gioco è fatto: • selezionare il file da riprodurre; • ottenere Turi corrispondente al percorso; • costruire un player controller per il file; • aggiungere alla finestra il componente di controllo e di riproduzione del player; • attivare, infine, il player! Per implementare i precedenti passi è possibile utiliz- zare come riferimento il seguente blocco di codice (trat- to dal metodo buildUIQ della classe Esempiol allegata). Player player; JFileChooser fileChooser = new JFileChooser("."); int status = fileChooser.showOpenDialog(this); if (status == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFileQ; try{URL uri = file.toURLQ; f inai Container contentPane = getContentPaneQ; player = Manager.createRealizedPlayer(url); //A Component visualComponentUI = player.getVisualComponentQ; if (visualComponentUI != nuli) contentPane. add( visualComponentUI, BorderLayout.CENTER); Component controlPanelComponent = player.getControlPanelComponentQ; if (controlPanelComponent != nuli) contentPane. add( controlPanelComponent, BorderLayout.SOUTH); player.startQ; //A } catch(Exception e){System.out.println(e); System.exit(-l);} >//if La classe Manager mette a disposizione due diversi me- todi statici per costruire un Player e cioè Manager. crea- tePlayer(. . .) e Manager. createRealizedPlayer (. . .). La diffe- renza sostanziale sta nella modalità di controllo sulla costruzione del Player. Quando usiamo il metodo Ma- nager. createPlayer( .. .) quello che otteniamo è soltanto il riferimento ad un oggetto Player che in realtà può an- cora essere in costruzione, in quanto il metodo non è bloccante: nel caso in cui il Player non fosse ancora pronto, la successiva richiesta del componente di ripro- duzione video produrrà un errore javax. media. NotReali- zedError. In realtà questa situazione non deve conside- rarsi un limite. La completa realizzazione dell'oggetto è segnalata da un evento di tipo RealizeCompleteEvent: basterà quindi realizzare un Controller Adapter per que- sto evento e inserire all'interno del metodo realizeCom- plete(RealizeCompleteEvent event) la sezione delimitata dal commento //A (vedere esempio proposto nel prece- dente articolo). L'altro metodo, Manager. creai eRealized- Player(...) è invece bloccante: soltanto dopo la costru- zione dell' oggetto verrà restituito il controllo al thread di esecuzione dell' applicazione. CAPTUREDEVICE Limitarci a riprodurre file multimediali è però una for- te limitazione nell'utilizzo dei packages JFM. La realtà 82^^ F e b b 3 http://www.itportal.it che ci circonda è una fonte inesauribile di informazio- ni che spesso vogliamo condividere con gli altri. Pos- siamo utilizzare quello che ci sta intorno come "sor- gente" dei contenuti audio e video delle applicazioni, acquisendoli mediante particolari dispositivi come te- lecamere digitali, webcam e microfoni. I dati ottenuti possono essere trattati, o direttamente memorizzati su disco in formato media (AVI, QUICKTIME, MP3, etc.) ovvero trasmessi attraverso la rete (streaming). Ciascun dispositivo hardware viene indicato come CaptureDe- vice e suddiviso in due categorie di sorgenti: di tipo pu- sh e di tipo pulì. La differenza è molto semplice: si par- la di push source quando la sorgente è di tipo continua (ed anche sullo stream si avrà un flusso continuo di da- ti), come ad esempio lo sono i microfoni. Una macchi- na fotografica digitale è invece una sorgente pulì in quanto produce dati "isolati", in singole immagini (scatti). Utilizzando Java Media Framework, tutti i cap- ture device assumono un livello di astrazione tale da trascurare completamente la loro natura hardware, consentendoci di individuarli soltanto mediante i for- mati dei dati prodotti. Ciascun dispositivo viene "mappato" su un generico CaptureDevice di cui ne co- nosciamo solamente le caratteristiche fondamentali a cui possiamo accedere usando il corrispondente Cap- tureDevicelnfo. Come nel caso visto in precedenza, cioè della riproduzione di file multimediali, anche in que- sto caso le istruzioni da seguire sono veramente sem- plici: • localizzare il CaptureDevice mediante apposita ri- chiesta al CaptureDeviceManager; • dal device risalire alle sue caratteristiche mediate il suo CaptureDevicelnfo; • sfruttando questo oggetto otterremo il MediaLoca- tor necessario per creare il DataSource da cui attin- gere i dati prodotti; • costruire il Player su questo DataSource; • infine, avviare il processo di acquisizione. Mediante chiamata al metodo statico getDeviceList( . . .) dXCaptureDeviceManager è possibile ottenere la lista dei dispositivi disponibili (o meglio dei corrispondenti CaptureDevicelnfo), java.util.Vector deviceList; CaptureDevicelnfo CaptureDevicelnfo; deviceList = CaptureDeviceManager.getDeviceList(null); if (deviceList.size()>0) CaptureDevicelnfo = (CaptureDevicelnfo) deviceList.fi rstElementQ; oppure accedere direttamente ad un particolare dispo- sitivo direttamente dal suo identificativo, sfruttando il metodo getDevice(deviceName). Il metodo getDeviceLi- st(...) ha come parametro di richiesta un Format, cioè un oggetto con il quale viene identificato, appunto, un particolare formato e che contiene tutte le informazio- ni necessarie al trattamento dei dati. Lasciando inde- terminato questo parametro, il CaptureDeviceManager restituirà la lista completa di tutti i dispositivi disponi- bili, altrimenti fornisce soltanto l'elenco dei CaptureDe- vicelnfo corrispondenti ai dispositivi che possono pro- durre dati nel formato richiesto. La classe Format ha due classi derivate principali: una per definire il gene- rico formato audio (AuàioFormat), l'altra per quello vi- deo (VideoFormat). Ciascuna di queste classi viene poi utilizzata per specificare ogni formato (rispettivamen- te audio e video) supportato dalle librerie JFM. Ad esempio, possiamo richiedere al CaptureDeviceManager soltanto i device di acquisizione video: per farlo speci- fichiamo un formato audio generico, senza cioè speci- ficare un particolare encoding type: AudioFormat audioFormat = new AudioFormat(null); ed utilizziamo l'oggetto Format come parametro al me- todo getDevicelisti . . .) deviceList=CaptureDeviceManager.getDeviceList (audioFormat); for (int index=0; index Un'operazione analoga possiamo realizzarla per cono- scere i dispositivi in grado di produrre dati in formato video: basterà sostituire l'oggetto AudioFormat con un VideoFormat. Una volta individuato il device che inte- ressa, il passo successivo sarà quello di ottenere il suo MediaLocator da cui costruire il DataSource di produ- zione dei dati necessario al Player MediaLocator mediaLocator = devicelnfo.getLocatorQ; DataSource dataSource = Manager.createDataSource( mediaLocator);. Player player = Manager.createPlayer(dataSource); oppure, in maniera più compatta Player player = Manager.createPlayer( devicelnfo.getLocatorQ); ma questa forma non è sempre utilizzabile. Cosa suc- cede quando abbiamo contemporaneamente una sor- gente audio ed una video (magari proveniente da due ava DataSource \f&\ Prendiamo una 1^1 sorgente di dati "digitali": il flusso pro- dotto viene incapsula- to mediante un ogget- to DataSource, che rappresenta quindi i vari media audio, vi- deo o combinazione di entrambi. Una sorgen- te può essere un file o uno stream via Inter- net: specificando la lo- cazione (mediante un semplice oggetto URL) oppure il protocollo (ad esempio file://) è possibile costruire il corrispondente Data- Source da passare in ingresso al Player. Quando la sorgente dei dati proviene da uno stream via Internet, è bene effettuare una di- stinzione tra due tipi di DataSource: 1) PULL DATA SOURCE: Io si ha quando è il client che inizializza il trasfe- rimento dei dati e ne controlla il flusso ope- rando direttamente sulla sorgente. Ad esempio, i protocol- li HTTP e FILE sono ti- pici casi di questo tipo di DataSource. 2) Push data source: in questo caso è il server che gestisce il flusso ed il client può soltan- to acquisire i dati sen- za aver un controllo di- retto su di essi, come nei sistemi di broadca- sting e video on de- mand. http://www.itportal.it 3 ►►► 83 Ja DataSink \y%\ Il DataSink è l'in- r^\ terfaccia di base utilizzata per realizza- re un particolare tipo di oggetti, in grado di recuperare i dati pre- senti su di un flusso e re-indirizzarli verso una qualche destina- zione. Un DataSink può ad esempio recu- perare i dati da un Da- taSource in uscita da un Processor e memo- rizzarli in un file multi- mediale (come visto nell'esempio propo- sto) Formati supportati da JMF 2.1.1 \/%\ La versione 2.1.1 versione J del package sup- porta diversi tipi di me- dia tra cui: • protocolli: FILE, HTTP, FTP, RTP • audio: AIFF, AU, AVI, GSM, MIDI, MP2, MP3, QT, RMF, WAV • video: AVI, MPEG-1, QT, H.261, H.263 JMF fornisce anche il supporto pure a formati "concorrenti" come ad esempio Flash 2 e Hot- Media. Player Un Player è un particolare oggetto che rende "riprodu- cibile" i dati provenienti da stream di dati audio/video in ingresso. Questo processo di trasformazione è ne- cessario per problemi di compatibilità con i dispositivi di riproduzione. Prendiamo ad esempio un CD audio. Per porer essere riprodotto, il player legge in input le sequenze di bit dal supporto ottico e le trasforma in un formato media compatibile con la scheda audio. Poi- ché i dispositivi sia di riproduzione che di acquisizione vengono trattati in maniera generica, il Manager deve riconoscere il formato dei dati da riprodurre (distin- guendo tra i diversi formati audio e video) e costruire un Player compatibile. Il comportamento di questo Controller è scandito in base al particolare stato in cui si trova: 1) UNREALIZED. Questo stato sta ad indicare che il Player è in fase di istanziazione, in quanto il Mana- ger deve prima identificare il formato dei dati da trattare. Come già detto in precedenza, se il Player si trova in questo stato non può ancora essere uti- lizzato. Bisogna aspettare la conclusione delle ope- razioni di preparazione. 2) REALIZING. Dopo che il Manager ha determinato il formato dei dati su cui si sta costruendo il Player, lo stato dell'oggetto viene commutato mediante una chiamata al metodo realize(), che ne attiva la procedura di acquisizione delle risorse necessarie. 3) REALIZED. Completata la fase di preparazione, il Player passa in questo stato, comunicandolo me- diante un evento RealizeCompleteEvent. A questo punto il Player è pronto, correttamente istanziato sul formato dei dati e con i necessari componenti visuali e Controls. 4) PREFETCHING. Chiamando il metodo prefetch() il Player comincia a prepararsi per la riproduzione, precaricando i dati, ottenendo l'uso esclusivo delle risorse necessarie. 5) PREFETCHED. Completato anche il precaricamento e l'allocazione delle risorse, il Player è finalmente pronto. 6) STARTED. Dallo stato Prefetched passa in Started appena viene chiamato il metodo start(). diversi dispositivi)? In teoria si potrebbe costruire un Player per ciascun MediaLocator (e quindi con il corri- spondente DataSource) in modo da controllarne sepa- ratamente la riproduzione, ed in questo caso possiamo far uso della forma compatta: Player videoPlayer = Manager.createPlayer (videoDevice.getLocatorQ); Player audioPlayer = Manager.createPlayer (audioDevice.getLocatorQ); videoPlayer.addControllerListener(this); videoPlayer.startQ; audioPlayer.start(); Per avviare la riproduzione è necessario operare sepa- ratamente su i due Player invocando il metodo starti). In alternativa è possibile metterne uno sotto il control- lo dell'altro videoPlayer.addController(audioPlayer); videoPlayer.startQ; e quindi attivare la riproduzione sincronizzata di en- trambi operando soltanto su di uno. Ma se invece di avere due distinti DataSource si volesse operare su di un unico stream, è possibile chiamare il metodo create- MergingDataSource(DataSource[] dataSources) sul Mana- ger per ottenere una combinazione (nel nostro caso tra audio e video). Naturalmente la forma compatta non è più applicabile, in quanto il Player va costruito sul Da- taSource combinato e non più sul MediaLocator (vedere la classe Esempio! nell'omonimo file Java): DataSource[] dataSources = new DataSource[2]; dataSources[0] = Manager.createDataSource (audioDevice.getLocatorQ); dataSources[l] = Manager.createDataSource (videoDevice.getLocatorQ); DataSource dualDataSource = Manager.createMergingDataSource(dataSources); Player player = Manager.createPlayer(dualDataSource); player.startQ; CLONEABLEDATASOURCE All'interno di un'applicazione possiamo avere più Player operanti ognuno su di un particolare stream da- ti. In pratica il Player "consuma" i dati che si presenta- no sullo stream per poterli elaborare, ripresentandoli in uscita in un formato idoneo alla riproduzione. Poiché questi dati in input vengono man mano estratti dal flusso, non rimangono disponibili per eventuali elabo- razioni da altri Player. Un caso tipico è quando si vuol salvare su disco le immagini video provenienti da una telecamera digitale e contemporaneamente visualiz- zarle. La necessità di avere contemporaneamente due operazioni distinte (riproduzione del flusso e salvatag- gio), costringe l'uso di altrettanti Player che debbono coesistere sugli stessi dati. Ma uno dei due esclude l'al- tro in quanto "consuma" i dati che si presentano. Per coesistere, ciascun Player deve operare su di un proprio flusso di dati, cioè su una copia personale del DataSource: come creare e rendere disponibile a ciascu- no il proprio stream media? Mediante il metodo stati- co della classe Manager, una versione "clonabile" del DataSource originale dataSource=Manager.createCloneableDataSource (dataSource); il nuovo dataSource mantiene ancora la caratteristica di un DataSource ma acquista anche la natura di Source- Clonable. A questo punto possiamo ottenere da questo oggetto tutte le copie (cloni) che vogliamo, che saranno 84 ►►► F e b b 3 http://www.itportal.it indipendenti l'una dall'altra ma non dallo stream ori- ginale DataSource sourcePlayerl = ((SourceCloneable) dataSource).createClone(); DataSource sourcePlayer2 = ((SourceCloneable) dataSource).createClone(); Player playerl = Manager.createRealizedPlayer (sourcePlayerl); Player player2 = Manager.createRealizedPlayer (sourcePlayer2); ai dati elaborati e convertiti nel formato; per ottenere un oggetto Processor in modalità Realized abbiamo la necessità di un Processor Model, costruito sul DataSource clonato, sui formati da adottare e sul FileTypeDescriptor del file multimediale. ProcessorModel processorModel = new ProcessorModel(dataSource, formats, outputType) Player processor = Manager.createRealizedProcessor (processorModel); ava Affinchè si abbia un flusso di dati sui cloni, è necessa- rio che anche l'originale sia soggetto ad un flusso. Se provassimo ad avviare i Player a questo stadio, non si avrebbe nulla in riproduzione, in quanto il DataSource originale è inattivo. SALVATAGGIO DI UN MEDIA A questo punto, per ritornare al caso di riproduzio- ne/salvataggio, ottenuti i cloni necessari si possono predisporre neir applicazione due moduli. Il primo avrà il compito di controllare e gestire la riproduzione del contenuto audio e video dell' acquisizione, mentre il secondo dovrà gestire il salvataggio dei dati in un fi- le media. Ciascun modulo avrà a disposizione un clo- ne dello stesso DataSource. Vediamo brevemente come è possibile salvare un flusso come un file multimedia- le, ad esempio QUICKTIME. Per prima cosa dobbiamo procurarci il formato da adottare nella trasformazione del flusso. L'audio dovrà avere un formato con encoding IMA4 mentre il video può essere Cinepak (che sono i formati definiti per il QuickTime). Format formats[] = new Format[2]; formats[0] = new AudioFormat(AudioFormat.IMA4); formats[l]=new VideoFormat(VideoFormat.CINEPAK); Poi costruire il descrittore da utilizzare nella definizio- ne del file e il MediaLocator da utilizzare FileTypeDescriptor outputType = new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME); MediaLocator dest = new MediaLocator ("file://./esempio.mov"); Costruito il Processor si accede al DataSource in output e su di esso definiremo il file-writer (in questo caso un DataSink). Al termine del flusso originale è necessario chiudere anche il file-writer per completare il salvatag- gio: non basta quindi bloccare il Processor ma è neces- sario fermare il file-writer, e quindi chiudere il file. La cosa però non è così immediata! Non possiamo ferma- re il file-writer contestualmente alla chiusura del Pro- cessor, in quanto possono ancora esserci dei dati sullo stream. Il problema è subito risolto se registriamo un acoltatore che resti in attesa di un evento EndOfStrea- mEvent: appena si raggiunge la fine dei dati presenti sullo stream, questo evento se catturato consentirà di chiudere anche il file-writer rimasto aperto. DataSource source = processor.getDataOutputQ; DataSink filewriter = Manager.createDataSink (source, dest); DataSinkListener listener = new DataSinkAdapterQ; filewriter.addDataSinkListener(listener); filewriter.openQ; filewriter.startQ; processor, sta rt(); Dove la classe DataSinkAdapter è: class DataSinkAdapter implements DataSinkListener{ public void dataSinkUpdate(DataSinkEvent event) { /^Modulo di visualizzazione*/ if (event instanceof EndOfStreamEvent) {try{event.getSourceDataSink().stop(); event.getSourceDataSinkQ.closeQ; }catch(Exception e){System.out.println ("DataSinkAdapter: "+e); System.exit(-l); m: JMFRegistry I L'operazione di map- I patura del dispositi- vo reale con il corrispon- dente CaptureDevice non è completamente auto- matica ma va controllata attraverso JMFRegistry, un'applicazione standalo- ne fornita con la versione 2.1 del tool JMF scaricabi- le dal sito della SUN. JM- FRegistry consente di re- gistrare tutti i dispositivi di acquisizione audio e vi- deo installati sul PC: la condizione necessaria è che nel sistema siano cor- rettamente presenti tutti i driver necessari per l'i- dentificazione e l'accesso ai dispositivi. Se JMFRegi- stry non riesce a indivi- duare un dispositivo può essere a causa di un erro- re nei driver o nel proces- so di installazione. } event.getSourceDataSinkQ.closeQ; Al posto di un normale Player, utilizzeremo un Proces- sor: questo perché non dobbiamo riprodurre i dati ma convertirli in un formato compatibile con il file media di memorizzazione. Diversamente dal Player, il Proces- sor ci consente di accedere al DataSource in uscita, cioè big. Antonino Panella http://www.itportal.it 3 ►►► 85 < < < < < < < < < < Corsi Avanzati Come REALIZZARE UN FTP CLIENT In questo appuntamento descriveremo gli strumenti che permettono d'implementare le funzionalità di un FTP Client. isual Basic FTP (File Transfer Protocol) è il protocollo che defi- nisce le regole per il trasferimento di file tra com- puter. L'FTP viene usato quando si scaricano, sul proprio computer, file prelevati da Internet, o quando dal proprio computer si trasferiscono file su un altro computer (ad esempio sul Server di un Internet Provi- der). L'FTP nasce nel 1972 nel MIT (Massachussets Insti- tute of Technology), le specifiche deirFTP vengono de- scritte nella RFC 114. L'FTP unisce i comandi del TCP/IP e del Telnet Protocol (Protocolli sviluppati con ARPAnet), si basa sul codice ASCII ed in particolare ne usa, solo, la parte inferiore (ASCII a 7 bit). I comandi dell' FTP oltre a controllare il flusso dei file, da e verso il Server, consentono, quando si trasmettono file, di in- teragire con il processo in atto sul Server. Ai server FTP è possibile accedere anche attraverso un Web Browser, in questo caso l'indirizzo da specificare è del tipo "ftp://ftp.microsoft.com", naturalmente con questo tipo di richiesta le informazioni visualizzate non saranno delle pagine HTML ma dei nomi di directory e di file. In questo articolo illustreremo come implementare la parte centrale di una FTP Client application. In Visual Basic per tale fine abbiamo vari strumenti: il controllo Internet Transfer, le funzioni dell' API di Windows e l'oggetto Winsock. Nello spazio di un articolo non sarà possibile presentarli tutti, per questo illustreremo som- mariamente il controllo Internet Transfer, descriveremo le funzioni dell' API di Windows dedicati ad Internet e faremo solo qualche cenno sul WinSock Control. INTERNET TRANSFER CONTROL Il controllo Internet Transfer (INET) consente di attivare una connessione Internet e di eseguire dei comandi. Il tipo di comando che può essere eseguito dipende dal protocollo di trasmissione selezionato cioè HTTP op- pure FTP. I principali metodi del controllo sono: Ope- nilRL ed Execute. OpenURL - Questo metodo apre un elemento (pagina, directory) che si trova all'URL specificato. La sintassi è la seguente: object.OpenUrl URL [,datatype] URL è l'indirizzo dove si trova l'elemento che deve es- sere aperto. DataType è un parametro opzionale che specifica il tipo di dato da leggere ("0" dato stringa, "1" dato come byte array). Execute - Questo metodo permette di inviare un co- mando ad un Server. Naturalmente i comandi che pos- sono essere inviati dipendono dal protocollo seleziona- to. La sintassi è la seguente: object. Execute uri, operation, data, requestHeaders URL, se specificato indica l'URL da utilizzare, neW Exe- cute, altrimenti l'URL utilizzato sarà quello specificato in OpenUrl. Operation è una stringa che specifica il tipo di operazione da eseguire (tra poco vedremo qualche esempio, non elencheremo tutti i valori, se siete inte- ressati potete controllare la guida in linea di Visual Ba- sic). Data è una stringa che specifica le informazioni che verranno utilizzate dall'operazione. RequestHeaders è una stringa che specifica informazioni supplementari. Le proprietà principali del controllo sono: Accetype, Protocol, URL ecc. Per esempio AccessType è utilizzata per specificare il tipo di accesso alla rete Internet. Come è noto la connessione di un computer client ad Internet può avvenire direttamente o tramite Proxy (cioè attra- verso una Intranet collegata alla rete tramite un dispo- sitivo di smistamento) che eventualmente funge da fil- tro. Per questo i valori che la proprietà può assumere sono: icNamedProxy cioè accesso tramite proxy; icDirect accesso diretta ad Internet; IcUseDefault impostazioni di default, in generale di- retto. Le altre proprietà che utilizzeremo saranno illustrate in seguito. In conclusione aggiungiamo che il controllo INET è in grado di supportare sia la trasmissione sin- crona che quella asincrona. La sincrona è associata al metodo OpenURL, che durante la trasmissione dei file non permette di eseguire altro codice. L'asincrona, inve- ce, è associata al metodo Execute che, appunto, consen- te l'esecuzione di altre operazioni durante il download dei file. Client server I /-SI In una configura- la^ I zione Client/Ser- ver FTP, il Server è un sistema in cui sono ar- chiviati i file (oppor- tunamente orga- nizzati in Directory) che attraverso un FTP Client possono essere manipolati in remoto. Sul Server FTP sono definiti gli account dei Client. I Client dopo aver specificato User- name e Password pos- sono accedere, in let- tura/scrittura, ad una parte oppure a tutte (dipende dal tipo di account) le directory del Server. Su alcuni Server, di solito, è de- finito l'account anony- mous (con password un indirizzo di posta elettronica o "guest") che contiene le direc- tory pubbliche del si- to. http://www.itportal.it b b 3 ►►► 87 Visual Basic m. Tcp/IP I II protocollo TCP | /IP è nato nel mondo Unix e ormai è disponibile su tutti i sistemi operativi. TCP è un protocollo che garantisce la cor- retta trasmissione dei dati inviati e ricevuti, conservando la se- quenza di trasmissio- ne e ricezione. Insie- me all'IP protocol (In- ternet Protocol), che si occupa essenzial- mente dell'instrada- mento dei pacchetti fi- no al destinatario, co- stituiscono il base layer di Internet. Dunque il TCP/IP è un protocollo orientato alla connessione e concepito per la tra- smissione punto-pun- to. Fig. 1: La form che permette di utilizzare il controllo Internet Transfer. FTP CLIENT Iniziamo a descrivere il codice per l'FTP Client. Create un nuovo progetto Visual Basic con due form (formi e formi), sulla formi inserite un controllo INET (naturalmente dopo aver referenziato la libreria Micro- soft Internet Transfer Control) ed una serie di pulsanti che ci serviranno per abilitare alcune funzionalità del protocollo FTP come: connessione al Server, spostamento e caricamento file ecc. Inoltre sulla form posizionate un textbox che servirà per mostrare il contenuto dell'URL aperto tramite Openllrl (controllate la Fig. 1). Analizziamo le istruzioni che consentono di stabilire una connessione e successivamente di utilizzarla per eseguire i comandi FTP. Nel Click del pulsante Connet- ti settiamo i parametri del controllo INET1 necessari per l'impostazione di una connessione FTP. Private Sub Connetti_Click() With Inetl .UserName = "Anonymous" ■ Password = "guest" .AccessType = icDirect ■ RemotePort = 21 ■ Protocol = icFTP .URL = "ftp://pasta" End With End Sub In particolare effettuiamo una connessione con User- Name "anonimo" e Password "guest" (ospite). Questi va- lori consentono di accedere alla parte pubblica di uno spazio FTP. Successivamente specifichiamo il tipo di connessione impostando la proprietà AccessType su ic- Direct. Specifichiamo che utilizziamo la porta di con- nessione 21 e il protocollo FTP ed infine impostiamo TURL del Server cioè ftp: li 'esempio. Facciamo notare che il server non è remoto ma si trova sullo stesso compu- ter utilizzato per sviluppare il programma (questo s'in- tuisce dal modo in cui è specificato l'indirizzo FTP). Come s'intuisce dal nome del pulsante nel Click di "Chiudi" specifichiamo il codice per chiudere la con- nessione. Questo lo facciamo utilizzando il metodo Execute e il comando FTP "CLOSE". Vediamo ora il comando che ci consente di aprire un fi- le con nome "provaftptxt.txt" che si trova nella root del- lo spazio FTP pubblico (per l'organizzazione delle di- rectory dello spazio FTP controllate la Fig. 3). Private Sub Openurl_ Click() Textl.Text : = Inetl ■openurl("esempio/provaftptxt txt") End Sub Nel Click del pulsante Openurl richiamiamo il metodo Openllrl passandogli l'indirizzo ed il nome del file da aprire, il risultato di questa richiesta verrà restituito nel textbox Textl. Per scaricare il file localmente utilizziamo la seguente procedura: Private Sub Scarica_Click() Inetl.Execute , "GET provaftptxt.txt c:\provadowload2.txt" End Sub In cui dopo aver invocato il metodo Execute sull'ogget- to Inetl gli passiamo come operazione FTP una "GET" (cioè preleva) che ha come parametri il file da preleva- re ed il percorso locale dove trasferire il file (cioè c:\provadowload2.txt). Come accennato la suddetta operazione avviene in modalità asincrona, quindi la stessa operazione poteva essere effettuata in modo sin- crono utilizzando il metodo OpenllRL. La lista dei file della root la richiediamo attraverso il metodo EXECU- TE e l'Operatore DIR. Se però volessimo la lista dei fi- le presenti in una sotto directory, ad Execute dovrem- mo passare "DIR" più il nome della sotto directory nel- la forma: DIR [nome], ecc. Quindi possiamo considera- re un'istruzione del tipo: Private Sub dir_ Click() Inetl.Execute , "DIR" End Sub Private Sub Chiud _Click() Inetl.Execute , ' CLOSE" End Sub Naturalmente, la creazione della connessione deve es- sere fatta prima dell'esecuzione delle altre operazioni per questo conviene inserire del codice per il controllo di questo aspetto (questo lo faremo quando descrive- remo le funzioni dell' API). Notate come in Fig. 1, la Formi ospita un pulsante con nome UsaAPI, questo serve per mostrare la formi dove abbiamo previsto il codice che richiama alcune funzioni dell' API. Inizia- mo, quindi, la descrizione della seconda parte del no- stro progetto che prevede l'uso delle API per realizza- re le operazioni che abbiamo implementato in prece- denza attraverso il controllo INET. FTP CLIENT CON LE API Anche sulla formi posizioniamo una serie di pulsanti (Fig. 2). Le funzioni API le dichiariamo in un modulo (Modulel.bas). In esso dichiariamo anche le seguenti costanti e variabili. Public Const INTERNET SERVICE FTP = 1 88*-^ F e http: //www. itportal.it Fig. 2: La form che permette di utilizzare le funzioni dell'API. Public Const INTERNET_SERVICE_GOPHER = 2 Public Const INTERNET_SERVICE_HTTP = 3 Public sessioneinternet As Long Public sessioneftp As Long 'qui vanno inserite le dichiarazioni delle funzioni API 'che descriveremo tra poco Sessioneinternet e sessioneftp servono per conservare rhandle della sessione Internet e della sessione ftp; le costanti verranno utilizzate dalla funzione InternetCon- nect per specificare il tipo di servizio (come sarà chiaro tra poco). Nel Click del pulsante Apri Internet creiamo la sessione Internet. Private Sub ApriInternet_Click() sessioneinternet = InternetOpen("progettol", 0, vbNullString, vbNullString, 0) End Sub La sessione internet viene inizializzata richiamando la funzione API InternetOpen di cui vediamo brevemente la sintassi: Public Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" ( ByVal sAgent As String, ByVal nAccessType As Long, ByVal sProxyName As String, ByVal sProxyBypass As String, ByVal nFlags As Long) As Long Dove: • sAgent, è il nome dell'applicazione che chiama la funzione API; • nAccessType, specifica il tipo di accesso (diretto, tramite proxy, ecc.); • sProxyName, è una stringa che contiene il nome dell' eventuale proxy, può essere nuli; • sProxyBypass, nome del proxy opzionale, può es- sere nuli) • nFlags, flag indicante diverse opzioni: connessione asincrona, da cache, offrine, per questo parametro abbiamo utilizzato il valore di default. La funzione restituisce rhandle della connessione che verrà utilizzata nella procedura Connessionelnternet. Notate che le API si trovano nel file Winlnet.dll. Private Sub ConnessioneInternet_Click() If sessioneinternet = Then MsgBox "bisogna prima creare una sessione Internet" Exit sub End If sessionefcp=InternetConnect(sessioneinternet, "pasta", "21" "anonymous", "guest", INTERNET_SERVICE_FTP, 0, 0) End Sub Nella suddetta procedura testiamo innanzitutto resi- stenza di una sessione Internet, se la risposta è affer- mativa andiamo ad "aprire" una sessione ftp richia- mando la funzione API InternetConnect, della quale ve- diamo sommariamente la sintassi. Public Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" ( ByVal hlnternetSession As Long, ByVal sServerName As String, ByVal nServerPort As Integer, ByVal sUserName As String, ByVal sPassword As String, ByVal nService As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Long • hlnternetSession, handle sessione internet (creata come descritto in precedenza); • sServerName, stringa che indica il nome del ser- ver; • nServerPort, porta del server, per il servizio FTP (sarà la 21); • sUserName, username per la connessione; • sPassword, password per la connessione; • nService, tipo di servizio richiesto (FTP, http). Gli ultimi due parametri non li abbiamo utilizzati (l'impostiamo sul valore zero). Questa funzione resti- tuisce Thandle della sessione FTP (se l'operazione ha successo). Per chiudere la connessione utilizziamo il codice inserito nel pulsante Chiudi Connessione. Private Sub ChiudiConnessione_Click() Cali InternetCloseHandle(sessioneinternet) Cali InternetCloseHandle(sessioneftp) End Sub Con la ChiudiConnessionejClick chiudiamo sia la sessio- ne Internet che quella ftp, il tutto attraverso Internet- CloseHandle che restituisce il valore booleano true se l'operazione ha avuto esito positivo. La sintassi di que- sta funzione è semplice: Public Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hlnet As Long) As Integer isual Basic a; Web server I Gli esempi fatti | nell'articolo sono eseguiti utilizzando Vi- sual Basic e US {In- ternet Information Server) che come è noto permette di ge- stire un Server Web ed un Server FTP. Naturalmente con i si- stemi operativi Win- dows 2000 e Windows XP si lavora con US, mentre con Windows 95/98 si lavora con Personal Web Server (PWE). Quest'ultimo si trova nella suite Visual Stu- dio oppure nella car- tella add-ons del Cd del sistema operativo. Se sul vostro computer non avete installato né US e né PWS potete testare gli esempi iscrivendovi ad una Community come Ly- cos: http://www.tripod.lycos.it/ Ultimo avvertimento il PWS o l'US installato deve essere nella stes- sa lingua di Visual Ba- sic altrimenti avrete delle sgradite sorpre- se. http://www.itportal.it b b 3 ►►► 89 Visual Basic Winsock \/%\ Winsock è il con- l^^l trollo che consen- te di utilizzare i proto- colli di trasmissione TCP/IP, UDP, FTP ecc. Il controllo WinSock, fa parte della libreria Mi- crosoft Winsock Con- trol 6.0 (in cui è pre- sente l'ocx MSWIN- SCK). Questo controllo è stato descritto nel- l'articolo: Il controllo per accedere ai servizi di rete, nel quale abbia- mo illustrato come im- plementare una Chat. Fig. 3: La finestra delle proprietà del controllo Internet Transfer Accetta come parametro l'handle della connessione. Ora vediamo come effettuare il Download di un file. Nel pulsante Scarica file inseriamo il seguente codice: Private Sub ScaricaFile_Click() If sessioneftp = Then MsgBox "bisogna prima creare una sessione ftp" Exit sub End If If FtpGetFile(sessioneftp, "provaftptxt.txt", "c:\provadowload.txt", False, 0, 1, 0) = False Then MsgBox "errore" End If End Sub Dopo aver constatato l'esistenza di una sessione ftp, la funzione richiama TAPI che consente di scaricare il fi- le e cioè la FtpGetFile, di cui descriviamo i parametri fondamentali: Public Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" ( ByVal hFtpSession As Long, ByVal IpszRemoteFile As String, ByVal IpszNewFile As String, ByVal fFaillfExists As Boolean, ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean • hFtpSession, handle della sessione ftp; • IpszRemoteFile, file da scaricare; • IpszNewFile, percorso e nome del file destinatario. Per gli altri parametri utilizziamo i valori di default. Notate che il file da scaricare è provaftptxt.txt e che verrà scaricato nella root del disco e:/ (con il nome pro- vadowload.txt). Vediamo come effettuare un upload verso il server "remoto", per tale scopo utilizziamo la seguente funzione. Public Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" ( ByVal hFtpSession As Long, ByVal IpszLocalFile As String, ByVal IpszRemoteFile As String, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean • hFtpSession, handle della sessione ftp; • IpszLocalFile, file da scaricare sul server remoto; • IpszRemoteFile, percorso e nome del file sul ser- ver remoto; • dwFlags, flag indicante il tipo di trasferimento (ASCII o binario); • dwContext, impostato su zero. Ecco come utilizziamo questa funzione: Private Sub Scarica_Click() If sessioneftp = Then MsgBox "bisogna prima creare una sessione ftp" Exit sub End If If FtpPutFile(sessioneftp, "c:\provaput.txt", "\provaftp\provaput.txt", 1, 0) = False Then MsgBox "errore" End If End Sub La funzione restituisce true se l'operazione avviene correttamente. L'eliminazione di un file sul server può essere fatta attraverso la funzione FtpDeleteFileO. FtpDeleteFile(sessioneftp, "\provaftp\provaput.txt") In questo caso i parametri sono semplicemente: Thandle della sessione ftp ed il nome del file da can- cellare sul server. Dalla funzione sarà restituito il valo- re true o false in base all'esito dell'operazione. ^Jnjxj J File Modifica Visualizza P J J ■*= Indietro " ^ " gj | J Indirizzo |(^I ftp: //pasta/ 3 ^ Vai provaftp provaftpTX,. ^Jnjxj File Modifica Visualizza Prefei * ■sh Indirizzo |Cg] ftp://pasta/provaftp/ J^J ^Vai d m £ provaftp2 provaA5P,asp ProvaHTML, Utent j.Jf7 Intranet locale ^ |utente: A IH Intranet locale Fig. 4: Uno spazio FTP visto attraverso il Browser Internet Explorer Internet Transfer. CONCLUSIONE Ora che avete capito come utilizzare il protocollo FTP potete realizzare un FTP Client con l'interfaccia nello stile Explorer. Questo tipo d'interfaccia di solito preve- de un "albero" (treeview) che presenta la struttura del- le directory presenti nell'account ed una listview che mostra i file contenuti nella directory selezionata. Massimo Autiero 90^^^ F e http: //www. itportal.it Modellazione tramite Surface» 3D Studio Max Pietro Canini Vediamo come utilizzare uno tra i più versatili strumenti di modellazione, il Surface, costruendo una vasca. Non mancheranno gli strumenti già visti in precedenza, come: le operazioni booleane, le estrusioni ed alcuni modificatori. Ben ritrovati davanti ai nostri PC con 3dsmax (in uscita la versio- ne 5.0, con tante novità; prima tra tutte un nuovo motore di rendering cui è possibile applicare Radiosity e Global lllumination). Uno dei metodi migliori per modellare oggetti curvi, organici e non, complessi sono gli stru- menti di Superficie. Questi strumenti sono composti da due modificatori: Cross Section e Surface. Il Cross Section crea delle spline di congiunzio- ne tra spline esistenti. Ad esempio se abbiamo un cerchio sotto ed un cer- chio sopra e gli applichiamo il Cross Section, vediamo delle spline che colle- gano i due cerchi per formare un cilin- dro. Nota bene che le spline che cree- remo noi, dovranno essere ordinate e unite (se le creiamo separatamente) col comando Attach. Applicando poi il modificatore Surface al modello spline, avremo delle patch che ricoprono la struttura da noi creata. Nel caso del cilindro, oltre al reticolato in wireframe, avremo anche le patch che formano una struttura chiusa e visibile. Dei con- sigli utili per chi usa questi strumenti sono: l'utilizzo di figure di riferimento da applicare su dei piani in back- ground; lavorazione su metà modello, per poi specchiarlo (se è simmetrico); utilizzare lo snap3d a vertice; creare una copia del modello usando l'opzio- ne Reference ed applicandogli il modifi- catore Surface, in modo da vedere, mentre si modellano le spline, i cam- biamenti sull'altro oggetto; la maggior parte delle volte, quando dei vertici coincidono, Max chiederà di saldare i vertici, è opportuno dire di No in quan- to è sempre possibile unirli dopo col comando Weld. Con questo metodo dunque, è possibile creare teste umane, automobili, aerei, ecc.. Modelleremo un oggetto semplice nella sua creazione, ma fondamentale per capire subito questo metodo di model- lazione, la vasca. Vedremo, dopo la modellazione: il materiale da applicargli e cioè un bianco con leggere riflessioni sfocate tramite lo shader Raytrace; altri parametri quali align, array, ed alcuni modificatori. Iniziamo a modellare... 1 La prima spline Avviamo 3DSMax ed iniziamo a creare le spline necessarie per il nostro Surface. Andate quindi nel pannello Create/ Shapes e cliccate su Rectangle. Nella vista Top create un rettangolo di dimensione qualsiasi, ed andiamo nel pannello Modify per definirne la grandezza. Nei parametri del rettangolo (Rullout Parameters) impostiamo Length = 70; Width = 140; Corner Radius = 15. Questa sarà la base della nostra vasca, ora creiamo le altre spline partendo da questa. http://www.itportal.it e b b 3 >>> 91 DBSQu gMSSkBmim f 2-3-4-5 Le altre spline Sempre nel pannello Modify aprite la tendina dei modificatori e dalla lista scegliete Edit Spline, in modo da trasformare il rettangolo in una forma (Spline) modificabile (Edit). Ora col modificatore applicato scegliete di lavorare a livello Spline, quindi selezionate il rettangolo che diventerà rosso. Nella vista Front spostate (Select and Move), solo sull'asse Y, il rettangolo in alto (di circa 15 unità) tenendo premuto Shift da tastiera (in modo da clonarlo). Ingranditelo (Select and Uniform Scale) di circa 15 unita (3DStudio in X, Y, Z segnerà quindi 115 unità perché parte da 100). Ora ancora col Select and Move e Shift premuto cloniamo sempre sull'asse Y l'ultima spline creata, spostandola in alto di circa 45 unità. Stesso procedimento per quest'ultima spline creata, questa volta su di 6 unità. Ora ingranditela di 20 unità (X, Y, Z 120 u). Il Cross Section 6 A questo punto disattivate la modalità spline (ricliccandoci sopra). Dalla tendina dei modificatori scegliete Cross Section. Nei suoi parametri spuntate Smooth in modo da avere le linee di giunzione smussate. Se provate ad applicargli anche il modificatore Surface, noterete che la forma della vasca c'è, ma non è chiusa alla base. Quindi rilevate il Surface che avete appena messo per provare, lasciando Cross Section come ultimo modificatore. Nella lista dei modificatori scegliete di nuovo Edit Spline. ■ 92 >>> F e http: //www. itportal.it Chiusura 7-8-9-10* Scegliete di lavorare a livello Segment. Mettete la vista prospettica in modo da poter vedere tutti i vertici che formano la base (la prima spline creata). Attivate lo Snap3D, cliccateci col tasto destro sopra e nel Grid and Snap Settings spuntate solo l'opzione Vertex. Nel rullout Geometry, cliccate su Create Line e disattivate l'opzione accanto PolyConnect. Nella vista prospettica, tracciate delle linee verticali tra i vertici T — lopì) i — adou! nvor r" Linear |~" Bind first r Closed r Bind l< Connect | Inserì | Make First | Fuse | Reverse | superiori e quelli inferiori (con lo snap attivo, la freccetta punterà direttamente sui vertici). Disattivate il Create Line ricliccandoci sopra. Ora selezionate una linea verticale che avete appena creato e sempre nel rullout Geometry impostate il valore 2 accanto a Divide, quindi cliccate su Divide. Ora al segmento verranno aggiunti 2 vertici; fate lo stesso per l'altra linea verticale. Riattivate Create Line e create le linee orizzontali come in Fig. 10 (ad ogni linea creata, quindi da vertice a vertice, cliccate col tasto destro del mouse per interrompere il segmento). M1 II Surface Disattivate Create Line e la modalità di lavorazione sul segmento, ricliccando su Segment. Dalla lista dei modificatori scegliete Surface e lasciate i parametri così come sono. Ecco creato l'interno della vasca. Se dovessero esserci delle parti bucate sulla base, significa che qualche vertice non è sovrapposto a quello sottostante; quindi spostatelo esattamente sopra e quando si chiederà di saldare, scegliete No. Abbiamo visto quindi che il modificatore Surface funziona su poligoni quadrati, quindi quando sono composti da quattro vertici. Infatti il procedimento fatto per chiudere la base non è altro che l'aggiunta di vertici affinché si formino dei poligoni quadrati. Continuiamo con la creazione della vasca. ■ http://www.itportal.it e b b 3 >>> 93 DBSQu gMSSkBmim 12-13-14-15-16 II materiale Come avrete notato, un lato della vasca è trasparente; quindi aprite il Material Editor e rimediamo a questo fatto. In uno Slot vuoto, nella tendina Shader Basic Parameters spuntate l'opzione 2-Sided (in modo da rendere visibile la m \[T ':. >»: ', j .. f "'-. v: i-' |Blinn "3] V Wire F 2-Sided Face Map V Faceted Vi'Mfr ì~' '.•:•■: rcs\ti?:-?rj Ànnbient: | Diffuse: Q Specular: 1 U U e -Self-lllumination r Color fO t\ _| Opacitv: fTOO ^J Specular Highhghts Specular Level: [70 ^J Glossiness: [50 ^J Soften: [oX~ t\ WM jfr^ l fe l x i # i ù i °è l o) i © ifjìf «, & rj^ \ |MapttO _*J Noise r - Coordinates |- Source:|Object>Cir^ _^j Map Channel: fi ^J Offset Tiling Angle: X:|0,0 Zj |1.,0 t] 10,0 t] Y:|0,0 t] 1 1.0 ijj (0,0 t| z: : jao tj 1 1.0 Z] |0,0 t] Blur: pTo t\ Blur offset: 1 0,0 t\ r| - Noise Parameters Noise Type: (* Regular C Fractal C Turbulence Noise Threshold: High:) 1,0 t} Levels:|3,0 vj Size:|5,0 t] Low:|0,0 t] Phase:|0,0 t] ^^^^^^ Maps Color 81 | None | W Swap | Color tt2 p None | [7 [|+ Output |] parte trasparente); impostate il colore Diffuse Bianco (RGB = 255); Specular Level = 70; Glossiness = I «fa I X 1*1 A l°èlsJJ® |[ìff«iA \ |Maptì3 TJl <* Use Environnrif Global Locai W W Enable Flaytracing 1^ R Àntialiasing F W SelfReflect/Refract W W Àtmosphere F F7 Reflect/Refract Material ID W W Object: ed objects V " W P" Color Density / Fog Raytracer Parameters <* ÀutoDetect C Reflection C Refraction Options... i •■:'• •. ". :■•;-- Background ■' : : he f.nvironrneri! SeUinq: ■ Ir EI _j."""^"'L'£_ Copy Àntialiasing Parameters ni l i si ili i s(-i l. i Global--> Locai | Local-->Global Adaptive Control 4nfeliasìng is curranl'iv' enabbi I - Àdaptive Initial Rays: [4 Threshold: fai ' t\ Man. Rays: [32~ Blur/ Defocus (Distance Blur) Blur Offset: fO0 ^J Defocusing: [02 ^] BlurAspect: pLO _^| Defocus Aspect: pTo t\ Blur Map: Defocus Map: Jr Jr E rtasikix i#ia iiaifijj® i[ìff.«!c \ Il ■ Default T[ Standard ULAA ■ J — ! i-0 Soften: fai - ^J :.,-r-i,ur-H ,',-ihM. ' r Amount r Ambient Color . . . 1 1 00 ^J r Dif fuse Color.... flOO"^J _ V Specular Color . .| 1 00 t] \~ Specular Level . 1 1 00 ^J _ l~~ Glossiness 1 1 00 ^J r Self-lllumination. 1 1 00 ^J _ r Opacità fl00~^J _ r Filter Color flOT^J _ W Burmp [3 t\ _ Map None 50. Nella tendina Maps cliccate su None accanto a Bump per definire un leggerissimo rilievo; dalla lista scegliete Noise. Impostate, nei parametri del Noise, Size = 5. Tornate un livello indietro (Go to Parent) e mettete Bump Amount = 3. Ora cliccate su None accanto a Reflection e dalla lista scegliete Raytrace. In RayTrace Parameters cliccate su Option ed atti- vate l'opzione Àntialiasing in Global. Ora aprite il menù a tendina Àntialiasing e puntate l'opzione Override Global Settings; in Defocusing impostate 0,2. In questo modo avrete delle riflessioni sfocate ma con un notevole aumento di tempo nel rendering. Tornate un livello indietro e mettere Reflection Amount = 15. Applicate il materiale alla vasca se non l'avete ancora fatto. Creiamo, tramite le operazioni booleane, la struttura base della vasca. Andate nel pannello Create / Geometry / Extended Primitives e cliccate su Chamfer Box. Nella vista Top create la geometria di dimensioni Length = 110; Width = 210; Height = 85; Fillet = 2,5. Con il box selezionato, La base 17 cliccate sul comando Align (si può trovare anche nel menù a tendina in alto Tools / Align; oppure ALT+A) e poi sulla vasca. Nei parametri di allineamento in Align Position spuntate l'opzione X Position e Y Position; quindi cliccate su Apply e poi su Ok. Ora il box risulterà centrato rispetto alla vasca. ■aia U^M *|fc|&|«|BI|T| |ch a mfe,B™01 | H0dfeLiSt •I CopHoles Tapcr iSH- Rela, =Hi " -rSrl ■WI^IV E f- Parameters ■ 94 >>> F e http: //www. itportal.it 1 8-1 9-20 La sottrazione 24-25-26 Aggiustamenti Nel pannello Create/ Geometry / Standard Primitives cliccate su Box e nella vista Top create la geo- metria di dimensioni Length = 87,048; Width = 169,492; Height = 86,629 e posiziona- telo come in Fig. 18. Selezionate il Chamfer Box creato prima, andate nel pan- nello Create / Geometry / Compound Objects / Boolean e cliccate su Pick Operand B e selezionate l'ultimo Box creato. Lasciando i parametri booleani così come sono, avrete una sot- trazione del box, l'interno della vasca diverrà di nuovo visibile. Noterete che le maioliche a de- stra e in basso hanno dimen- sioni non conformi, andiamo ad aggiustarle. Per far sembrare che sono state tagliare dalla mano dell'uomo, selezionate tutta la fila in basso, andate nel pannello Modify e dalla lista dei modificatori scegliete Slice. Nelle sue opzioni scegliete Remove Bottom. Si creerà così un piano di ritaglio che eli- mina la parte sottostante. Bisogna però regolare il piano per far si che tagli le maioliche quando inizia lo smussamente del chamfer box sottostante. Fate quindi click su Slice in modo da lavorare a livello sotto l'oggetto e quindi muovere il ripiano (Gizmo) sul- l'asse Y. Una volta spostato il gizmo ricliccate su Slice per disattivare la modalità sub- objects; selezionate la fila di maioliche a destra ed eseguite lo stesso procedi- mento, questa volta però ruotando il gizmo di 90 gradi. Ora renderizzate. Conclusioni 21-22-23 Le maioliche Il tutorial si conclude qua, potete perfezionare la scena applicando texture agli oggetti creati; magari sulle maioliche applicando sempre una leggera riflessione sfocata. Abbiamo visto come il nuovo metodo di modellazione Surface sia efficiente e se usato con maestria, perdendoci molto più tempo, si possono tirare fuori delle superfici organiche realistiche. Il mio spazio si conclude qui, quindi ci ritroviamo i prossimi mesi con un altro interessante articolo. Creiamo un Chamfer Box di dimensioni Length = 30; Width = 30; Height = 2,5; Fillet = 0,5 e posizionatelo come in Fig. 21; ecco la nostra prima maiolica che moltipliche- remo per creare le altre, solo sul lato frontale (se le volete anche sugli altri lati, seguite di nuovo questo procedimento). p^sJP 1 p— sjp- fTooTi Sj 1 100.0 31; Array Dimension -J| [iOOO-U [T Scale ij 1™^ \Wn~ i\ [iOOO-^P^n. T ir spuntate l'opzione 2D; Count ID = 7; Count 2D = 3; Incrementai Row - « Copj * 20 [3— si p — si p — si Rra~sJ r 3 D n — =j fM — u p - u p - U ^,,, J . r Offset Z = -31, quindi Reset Al Parameters cliccate su Ok. n>- 1 ™ | Con la maiolica selezionata sce- L_x 1* ^Srr* gliete l'opzione Array (menù a tendina Tools/ Array; oppure l'icona nella barra degli stru- menti in alto). Mettete: Incrementai / Move / X = IBI. ■ http://www.itportal.it e b b 3 >>> 95 4 4 < < < 4 4 < < Advanced Edition Simulare UNA RETE CON NETWORK SIMULATOR Networking Analizzare il comportamento di una rete, locale, metropolitana, cablata, wireless e di qualsiasi altra natura. È fondamentale per ricavare le massime prestazioni. Network Simulator, brevemente NS, è un simu- latore universale di reti, conosciutissimo in ambito accademico, ma non solo, permette di progettare una rete di telecomunicazioni di qualsiasi ti- po e ottenere informazioni di ogni genere sul suo fun- zionamento. NS permette di simulare praticamente ogni aspetto della rete, partendo dai suoi componenti fondamentali, cioè nodi, router, link, passando per i di- versi protocolli coperti, ad esempio CSMA/CD a livel- lo di linea, IP a livello di rete, TCP a livello di trasporto e così via, coprendo praticamente l'intera pila OSI. Il cuore di NS è scritto interamente in C++ ed in filosofia open source, permettendo di creare nuovi oggetti o modificare quelli esistenti. Infatti, col susseguirsi delle sue varie release, attualmente siamo giunti alla versio- ne 2.1b9, sono state varie le aggiunte e modifiche da parte dei gruppi di lavoro sparsi su tutto il pianeta. L'u- tente normale, il sistemista di rete, lo studente di reti di telecomunicazioni, ha inoltre a disposizione un lin- guaggio di scripting, OTcl (Object Tool Command Lan- guage), che in maniera semplice e veloce permette di descrivere una topologia di rete, il suo traffico, il movi- mento dei nodi mobili, e i risultati che si vogliono rica- vare dalla simulazione. PERCHE DUE LINGUAGGI? L'Object Tel, è un'estensione del Tcl/Tk per la pro- grammazione object-oriented, in pratica fornisce un'in- terfaccia per l'utente, che così non deve assolutamente mettere mano al codice C++. In pratica NS è costituito da una gerarchia di classi C++, detta gerarchia compi- lata, ed una gerarchia OTcl detta invece interpretata. Le due gerarchie sono strettamente legate, dal punto di vi- sta dell'utente c'è una relazione uno ad uno fra una classe C++ ed una OTcl. L'utente crea nuovi oggetti at- traverso l'interprete, questi oggetti vengono istanziati al suo interno e a questo punto vengono cercati i corri- spondenti oggetti nella gerarchia compilata. Sembra più complicato a dirsi che a farsi, ma c'è naturalmente un motivo che porta all'uso di due linguaggi diversi per un simulatore di reti. Da una parte la simulazione dettagliata di protocolli di rete richiede un linguaggio di programmazione di sistema, che sia efficiente nella manipolazione di byte, degli header dei pacchetti, e nell'esecuzione di algoritmi su grosse quantità di dati. Per questi compiti quello che importa maggiormente è la velocità di esecuzione run-time. Dall'altra parte in- vece simulare una rete o progettarla, richiede la possi- bilità di variare parametri, creare gli scenari di simula- zione, rimandare in esecuzione una stessa simulazione più volte, compiti per cui una veloce esecuzione run-ti- me è meno importante rispetto alla velocità di iterazio- ne, cioè cambiamento del modello - riesecuzione. COMINCIAMO Esistono varie distribuzioni del simulatore, per Unix e Windows, da compilare a partire dai vari componenti che lo costituiscono, o in pacchetti all-in-one che con- tengono tutto ciò che ci serve. Consigliamo di usare NS sotto linux, sul quale d'altronde è stato sviluppato, per- ché avremo a disposizione una marea di tool comple- mentari che ci faciliteranno il compito di simulazione ed analisi dei risultati, oltre a poter dare un'occhiata al- l'intero codice sorgente di NS. Il pacchetto all-in-one per le piattaforme Unix è pero, nell'ultima release, di quasi 40Mb, così chi vuole iniziare a sperimentare con Ns può anche procurarsi le versioni precompilate per Windows, sia di Ns che del tool Nam. Nam, o Network Animator, darà un aspetto anche grafico alle nostre si- mulazioni, permettendoci di seguire visivamente la lo- ro evoluzione, ad esempio seguendo il movimento dei pacchetti lungo i link, il riempirsi delle code in un rou- ter o anche il movimento di nodi mobili. Nei pacchetti all-in-one sono inoltre contenuti tutti i sorgenti sia del simulatore NS, che di Nam, che di tutti gli altri tools. Ad esempio per facilitare la creazione dei grafici dei ri- sultati, troviamo l'utility Xgraph, mentre per facilitare la creazione degli scenari delle simulazioni, e per poter così programmare intere campagne di simulazione in modo da avere risultati più reali, troviamo degli script e dei file eseguibili per la generazione del traffico e per la generazione dei movimenti dei nodi mobili. Senza File sul CD \soft\codice\NSimulator\ a: Prestazioni I II C++ è veloce | da eseguire ma lento da modificare, rendendolo ideale per l'implementazione dettagliata di un pro- tocollo. L'OTcl gira più lentamente ma può essere cambiato ve- locemente e interatti- vamente. http://www.itportal.it 3 ►►► 97 Networking Linux r^&l Nell'articolo ab- V----J\ biamo visto e di- mostrato come sia possibile con NS simu- lare una qualsiasi tipo- logia e topologia di re- te, sia come punto di partenza per il proget- to, sia come strumento per l'analisi delle pre- stazioni. Per chi è dav- vero interessato consi- gliamo di scaricare e installare la versione Linux di Ns e natural- mente la completissi- ma documentazione. contare il fatto che possiamo anche dare un'occhiata ad una grossa raccolta di esempi su ogni tipo di rete, cosa che senz'altro aiuta più di ogni manuale. Sia in Win- dows che in Unix si lavora in ambiente a riga di co- mando. L'input da dare al simulatore è costituito da un file di testo con estensione tei contenente non solo la descrizione della rete da simulare ma anche le direttive del simulatore, ad esempio il tempo di simulazione, i file di input /output, gli eventi da tracciare, o anche dettagli puramente grafici come il colore e la dimen- sione dei nodi. UN PRIMO ESEMPIO Vediamo un primo semplice esempio per cominciare a prendere pratica con il linguaggio OTcl e i tool che ab- biamo a disposizione. Supponiamo di voler simulare una semplice rete costituita da due nodi, connessi da un link full-duplex, e l'unico tipo di traffico presente sia di tipo costante, con pacchetti di dimensione 500 bytes spediti da un nodo all'altro. Iniziamo a scrivere il nostro primo script, con un semplice editor di testo. In- nanzitutto si definisce un oggetto Simulator ed il nome del file di output per Nam: ^JnJxJl | « | M |^J »» | ►► | ± II" 0.800000 | stEP=2.0i Fig. 1: Il risultato della nostra prima simulazione. set ns [new Simulator] set namFile [open trace. nam w] poi, con il seguente commando, diciamo ad Ns di trac- ciare tutto sul file .nam $ns namtrace-all $namFile A questo punto dobbiamo definire i due nodi ed il link che li congiunge, con una capacità di 1Mb e tempo di propagazione di 20ms: set NodeO [$ns node] set Nodel [$ns node] $ns duplex-link $Node0 $Nodel 1Mb 20ms DropTail set DuplexLinkO [$ns link $Node0 $Nodel] Non resta che generare il traffico, dal nodo che abbia- mo chiamato NodeO faremo partire dei pacchetti da 500 byte a bit-rate costante di uno ogni 0.005 secondi, che verranno ricevuti da Nodel. Ecco come fare nel detta- glio: gli end-point di rete dove vengono generati e con- sumati pacchetti sono detti Agent, per prima cosa defi- niamo un Agent di tipo UDP, chiamandolo UDP0: set UDP0 [new Agent/UDP] e lo assegniamo al nodo che abbiamo chiamato NodeO: $ns attach-agent $Node0 $UDP0 Quindi abbiamo bisogno di un Agent che riceva i pac- chetti, per fare ciò creiamo un Agent di tipo Nuli, e lo "attacchiamo" al secondo nodo: set NullQ [new Agent/Null] $ns attach-agent $Nodel $Null0 All' Agent UDP del nodo NodeO assegniamo inoltre il generatore di traffico Constant Bit Rate, con i relativi pa- rametri: set CBRO [new Application/Traffic/CBR] $CBR0 set interval, 0.005 $CBR0 set packetSize, 500 $CBR0 set random_ $CBR0 attach-agent $UDP0 Ora possiamo conettere i due Agent: $ns connect $UDP0 $Null0 Essendo Ns un simulatore ad eventi discreti, bisogna decidere il tempo di durata della simulazione, che sce- glieremo pari a 10 secondi e gli istanti di inizio e fine della trasmissione dei pacchetti. Per comodità inoltre definiamo una semplice procedura che ci servirà a chiudere il file di output e lanciare l'esecuzione di Nam. proc finish {} { global ns global namFile $ns flush-trace dose $namFile exec nam trace.nam & exit } Il generatore di traffico CBR inizierà a trasmettere all'i- stante 0.5 secondi e finirà all'istante 4.5 secondi: $ns at .5 ' $CBR0 start" $ns at 4.5 "$CBR0 stop" $ns at 10.0 "finish" 98^^ F e b b 3 http://www.itportal.it Infine possiamo chiamare il metodo run del simulatore: $ns run Supponendo di aver salvato lo script come esempiol .tei possiamo far partire la simulazione dal prompt dei co- mandi con: > ns esempiol. tei. Dopo qualche secondo, per una simulazione così semplice, vedremo apparire la finestra del nam (vedi Fig. 1). Ns permette natural- mente di simulare situazioni molto più dinamiche, co- me tutti sappiamo è purtroppo possibile che un link fra due nodi di una rete ad un certo punto si guasti o si congestioni, o comunque non permetta la trasmissione di pacchetti su di esso. La caduta di un link è simulabi- le in Ns in modo molto semplice. Ad esempio se nel no- stro primo esempio vogliamo che il link fra i due nodi ad un certo punto cada, diciamo nell'intervallo di tem- po [2,3] secondi, basta aggiungere due righe nella se- guente maniera: $ns rtmodel-at 2.0 down $Nodel $Node2 $ns rtmodel-at 3.0 up $Nodel $Node2 Anche nella finestra del Nam vedremo questa situazio- ne, con il link che cambia colore e i pacchetti che vngo- no persi. SIMULIAMO UNA RETE WIRELESS MOBILE Cerchiamo di esplorare le capacità di NS con un esem- pio più complesso, ma che, con gli strumenti a disposi- zione, non richiede molto più di quello che abbiamo vi- sto. Supponiamo di voler simulare una rete wireless 802.11 con i nodi aventi capacità di movimento, cioè ab- biamo a che fare con una vera e propria rete mobile. Inoltre vogliamo ottenere dalla simulazione risultati ri- guardanti ad esempio quanti dei pacchetti spediti arri- vano a destinazione, cioè il cosiddetto "Packet Delivery Ratio". La prima cosa da fare è quella di configurare i nodi mobili, compresa l'antenna e l'interfaccia radio. Usiamo un antenna omnidirezionale, e settiamo i para- metri in modo da averla ad un altezza di 1,5 metri e con guadagno unitario sia in trasmissione che in ricezione: Antenna/OmniAntenna set X_ Antenna/OmniAntenna set Y_ Antenna/OmniAntenna set Z_ 1.5 Antenna/OmniAntenna set Gt_ 1.0 Antenna/OmniAntenna set Gr_ 1.0 Per quanto riguarda l'interfaccia radio, la configuriamo in modo che funzioni come una Lucent WaveLan 914MHz., basta consultare il manuale della scheda e i parametri corrispondenti sono: « I « I ■ I » I » I ± Fig. 2: Un clic su un pacchetto per scoprirne tutte le caratteristiche. Phy/WirelessPhy set Rb_ 2*le6 Phy/WirelessPhy set Pt_ 0.2818 Phy/WirelessPhy set freq_ 914e+6 Phy/WirelessPhy set L_ 1.0 A questo punto configuriamo i nodi mobili, basta va- riare i parametri del comando node-config, descritti in modo esauriente nel manuale ufficiale di Ns, quelli che ci interessano maggiormente sono i seguenti: node-config -adhocRouting DSR \ #protocollo di routing ad hoc -IIType LL \ #tipo di link layer -macType Mac/802.11 \ #protocollo di livello MAC -ifqType Queue/DropTail/PriQueue \#accoramento e droppaggio dei pacchetti -ifqLen 50 \ lunghezza della coda -antType Antenna/OmniAntenna \ #tipo di antenna -propType Propagation/TwoRayGround \ #propagazione del segnale -phyType Phy/WirelessPhy \ #livello fisico -channelType $opt(chan) \ #canale fisico -topolnstance $wtopo \ #la topografia della rete -agentTrace OFF \ -routerTrace ON \ #tracciamo solo i pacchetti a livello Router -macTrace OFF Con altri parametri, qui non indicati, è anche possibile definire il modello energetico del nodo, ad esempio la potenza di trasmissione e ricezione, oltre ad altre carat- teristiche che qui stiamo trascurando. Ora può avveni- re la creazione vera e propria dei nodi, supponiamo di volerne 5: for {set i 0} {$i < 5 } {incr i} { set node_($i) [$ns_ node] $node_($i) random-motion ;# disabilita il movimento casuale} Phy/WirelessPhy set CPThresh_ 10.0 Phy/WirelessPhy set CSThresh_ 1.559e- ■11 Phy/WirelessPhy set RXThresh_ 3.652e- ■10 Per quanto riguarda la mobilità dei nodi, sotto Linux, è comodo e conveniente utilizzare l'utility setdest, che crea dei pattern di movimento casuali su file, diretta- mente richiamabili dallo script, specificando il nome del file da includere con il comando: Networking Sul Web • Sito ufficiale http://www.isi.edu /nsnam/ns/index.html • NS by example http://nile.wpi.edu/NS/ • Marc Grei's Tutorial http://www.isi.edu/nsnam /ns/tutorial/index.html http://www.itportal.it 3 ►►► 99 Networking Bibliografia • THE VINT PROJECT - "THE NS MANUAL (FORMERLY NS NOTES AND DOCUMENTA- TION)", UC Berkeley, LBL, USC/ISI e Xerox PARC. source miadir/file_traffico Altrimenti possiamo definire da codice il modello di movimento con delle righe come le seguenti: # inizialmente il nodo è nel punto (x,y,x)=(200, 100,0) $node_(0) set X_ 200.0 $node_(0) set Y_ 100.0 $node_(0) set Z_ 0.0 # all'istante 2.0 secondi si muove verso il punto (300,200,0) $ns_ at 2.0 "$node_(0) setdest 300.0 200.0 0" Un'altra possibilità ancora, è quella di far muovere un nodo in modo casuale, basta abilitare il movimento random e usare la primitiva start: $nodomobile random-motion 1 $nodomobile start Non resta che generare il traffico. Come visto nel pri- mo esempio, per creare una connessione di tipo CBR dal nodo 2 al nodo 3, che inizia all'istante 10.0, proce- diamo così: set udp_(0) [new Agent/UDP] $ns_ attach-agent $node_(2) $udp_(0) set null_(0) [new Agent/Null] $ns_ attach-agent $node_(3) $null_(0) set cbr_(0) [new Application/Traffic/CBR] $cbr_(0) set packetSize, 512 $cbr_(0) set interval, 0.05 $cbr_(0) set random_ 1 $cbr_(0) set maxpkts, 10000 $cbr_(0) attach-agent $udp_(0) $ns_ connect $udp_(0) $null_(0) $ns_ at 10 "$cbr_(0) start" Sempre nel pacchetto all-in-one di Ns, è contenuto uno script, cbrgen.tcl che, mandato in esecuzione con gli ap- propriati parametri, genera un file contenente la de- scrizione del traffico della rete. Analogamente a quan- to visto per i pattern di movimento possiamo include- re questo file nello script con il comando source. Lo script completo e funzionante è contenuto nel file esempio2.tcl. Dandolo in pasto al simulatore NS otter- remo due file di output. Uno con estensione .tr che con- tiene il tracing di ogni evento della simulazione. A que- sto punto basta analizzare quest'ultimo, magari con un tool esistente (ad esempio trgraph), oppure con qual- che riga di codice possiamo scriverci un parser perso- nalizzato. Ad esempio, la riga r 40.647 2 RTR 8 cbr 512 [0 0] [2:0 3:0 32 0] [2] La r iniziale ci dice che si tratta di un evento di ricezio- ne, all'istante 40.647, da parte del nodo 2, di un pac- chetto CBR di 512 bytes. Un semplice programmino in Java (vedi NSParser.java) che calcoli la percentuale di m File Views Ànalysis |^ 4* M ■ 1 ► ►► <& © O & ED ^"** ack 16708:2 Sent at 49.674575 68 bytes Monitor Filter Dismiss Fig. 3: Dettagli su un pacchetto di acknowledgement. pacchetti TCP spediti, ricevuti, e persi, e quindi ci mo- stri il valore percentuale del Packet Delivery Ratio, po- trebbe contenere un metodo come questo: try { BufferedReader fr=new BufferedReader(new FileReader(file)); String line=fr.readLine(); while(line! = null) { if(line.indexOf("tcp")>0) { if(line.startsWith("r")) recv++; else if(line.startsWith("s")) sent+ + ; else if(line.startsWith("D")) drop++; } line=fr.readLine(); } fr.closeQ; System.out.println("Pacchetti spediti = "+sent); System.out.println("Pacchetti ricevuti = "+recv); System.out.println("Pacchetti persi = "+drop); System. out.println("Packet Delivery Ratio = "+(((double)recv/(double)sent)*100)+"%"); } catch(FileNotFoundException fnf) {System .out.println(fnf.toString());> catch(IOException ioe) { System. out.println(ioe.toString());> Fig. 4: Le statistiche ottenute attraverso una piccola applicazione Java. Se eseguiamo il programma passando come argomen- to il nome del file di trace da analizzare otteniamo il ri- sultato voluto (vedi Fig. 4). Comunque, leggendo la parte di documentazione riguardante proprio il tra- cing, troverete tutti i dettagli suir argomento. L'altro fi- le di output, con estensione .nam, ci darà la possibilità, come detto, di seguire l'evoluzione della rete, il movi- mento dei nodi, i pacchetti spediti, (sia a livello tra- sporto, sia a livello di routing. Ad esempio la Fig. 2, ci mostra come, cliccando su un pacchetto, riusciamo a sapere che è di tipo TCP e di dimensione 540 bytes, e cliccando a questo punto su Monitor possiamo seguire il pacchetto per tutta la sua vita. La Fig. 3 invece ci mo- stra un pacchetto di acknowledgement. Se ci interessano invece in particolare i meccanismi di routing, possiamo seguire la loro evoluzione seguendo la propagazione delle onde da router a router. Antonio Pelleriti 100 ►►► F e b b 3 http://www.itportal.it A d Testare IL CODICE CON JUNIT Tutti sanno che testare il codice è un'operazione noiosissima, una cosa che nessun programmatore ha mai voglia di fare... E invece non è così. Non ci credete? In questa serie di articoli vi spiegheremo come scrivere codice a prova di bomba e divertirvi mentre lo fate. Tutto grazie ad una nuova, rivoluzionaria tecnica: i "test unitari' Junit Vi è mai capitato di passare una notte intera cur- vi sulla tastiera alla ricerca di un maledetto bug? Vi è mai successo che il vostro codice di- ventasse complesso come un piatto di spaghetti, tanto che ogni volta che correggevate un bug ne saltavano fuori altri due da qualche altra parte? Allora gioite, peccatori! Non diciamo che i tempi delle vostre soffe- renze siano completamente finiti, ma se seguirete que- sta serie di articoli possiamo garantirvi che passerete molte più notti dormendo sereni nel vostro lettino. In questa serie di tre articoli vi introdurremo al piacere dei Test Unitari, della Grande Barra Verde e del Test- First Design. Attraverso questi strumenti potrete rag- giungerete il Nirvana della programmazione, uno sta- to di grazia nel quale i programmatori tornano a casa dal lavoro sereni, consapevoli che il loro codice funzio- na e che non riserverà brutte sorprese il mattino se- guente. Certo, anche noi sappiamo che i test sono una bestia antipaticissima. Tutti i programmatori sanno che dovrebbero scrivere i test, così come tutti sanno che do- vrebbero perdere qualche chilo e magari smettere di fumare. Ma quante persone conoscete che testano re- golarmente il proprio codice? Quasi nessuno, vero? I test sono noiosi, fanno perdere tempo, e spesso non aiutano più di tanto a trovare i bug. Almeno questo va- le i test che abbiamo usato finora. Ma esiste un'altra ca- tegoria di test, diversi da quelli che conosciamo. Si chiamano "unit test" oppure, all'italiana, "test unitari". Perché sono diversi? Per cominciare sono diversi per- ché non testano il sistema dall'esterno, come se fosse una scatola nera, ma da dentro. I test unitari testano di- rettamente il codice, in ogni sua piccola parte. Se l'idea vi sembra stupida, aspettate di vedere come funziona- no. E poi provateli. ESEMPI DALLA VITA QUOTIDIANA Il modo migliore per capire come funzionano i test uni- tari è sbirciare da sopra la spalla di qualcuno che li usa. In mancanza di meglio cercheremo di spiegarveli con qualche esempio. Questo mese potremo fare solo i pri- mi passi. Speriamo che bastino, se non a convincerci, almeno ad incuriosirvi abbastanza da farvi leggere i prossimi due articoli di questa serie e a provare queste tecniche per conto vostro. Lo scenario: dobbiamo scri- vere un programma per gestire il nostro conto corren- te. Useremo la tecnica dei test unitari e la libreria JUnit (ne parliamo nelle barre laterali). La classe che voglia- mo scrivere è una semplice rappresentazione di un im- porto di denaro in ingresso o in uscita dal conto. Dob- biamo decidere come rappresentare l'importo. Per adesso useremo due numeri per indicare rispettiva- mente gli Euro e i centesimi, e un valore booleano per indicare se l'importo è positivo o negativo, cioè se si tratta di una somma di denaro in ingresso o in uscita. Useremo questa rappresentazione sia all'interno della classe che nel costruttore. Ecco quindi la prima versio- ne della nostra classe Importo: package serie_junit; public class Importo { private final boolean _positivo; private final long _euro; private final long _cent; public Importo(boolean positivo, long euro, long cent) { _positivo = positivo; _euro = euro + (cent / 100); cent = cent % 100; } Nel costruttore i centesimi oltre i 100 vengono auto- maticamente convertiti in Euro. I tre valori vengono poi conservati nei rispettivi campi privati. I campi so- no tutti final (cioè costanti). Quindi l'oggetto è "immu- tabile", cioè non può più essere modificato dopo che è stato creato. Ora ci serve un modo per leggere il conte- File sul CD \soft\codice \ExtremeProgramming\ I prossimi appuntamenti [/SI Questa serie sarà I -J\ composta da tre articoli. Nei prossimi due numeri di ioPro- grammo troverete il se- condo articolo ("Pro- grammare con ritmo") e il terzo ("Abbracciare il cambiamento"). http://www.itportal.it 3 ►►► 101 Junit Programmazi one estrema I "test unitari" sono diventati fa- grazie alla cosid- Extreme Pro- Ó mosi detta gramming (o XP), una metodologia recente e molto popolare per svi- luppare il software. Probabilmente in futu- ro parleremo ancora di Extreme Programming sulle pagine di ioPro- grammo. Comunque i test unitari non sono strettamente legati al- l'XP, quindi potete usarli anche se l'XP non vi interessa affat- to. JUnit Uber Alles Del framework JUnit hanno det- to: "Mai prima d'ora nella storia della program- mazione così tante persone hanno avuto un debito così grande nei confronti di così poche righe di softwa- re". Potete trovare l'ultima versione di JUnit (at- tualmente la 3.8.1) sul sito http://www.junit.org o sul CD allegato a questo numero di io- Programmo. Per usare JUnit aggiungete sem- plicemente il file ju- nit.jar al vostro clas- spath (oppure, se usa- te un IDE come JBuil- der, registrate il file come libreria nel vo- stro progetto). mito di un Importo. Scriviamo un metodo getVoloreO che converte la rappresentazione interna in una String: public String getValoreQ £ String risultato = ""; if(!_positivo) risultato += "-"; risultato = risultato + _euro + "." + _cent; return risultato; } Appena abbiamo finito di scrivere il metodo, un cam- panellino trilla nella nostra testa. E' la nostra coscienza, che ci ricorda che è arrivato il momento di scrivere un test unitario per verificare se il metodo getVoloreO e il costruttore funzionano. Ecco quindi la prima regola del perfetto autore di test unitari: Ogni volta che scrivi un frammento di codice, anche se piccolo e semplice, scrivi anche un test per verifi- care che il codice funzioni. Naturalmente nessun test ci garantirà mai che il nostro codice non contiene bug. Ma anche se non possiamo testare tutto, più cose testiamo meglio è. All'opera, dunque. IL PRIMO TEST NON SI SCORDA MAI Per scrivere il nostro primo test unitario con il fra- mework JUnit dobbiamo scrivere una classe che eredi- ta da junit. framework.TestCase. Ecco lo scheletro di un test unitario per la classe Importo: package serie_junit; import junit.framework.*; public class Testlmporto extends TestCase £ public static void main(String[] args) _£ junit.textui.TestRunner.run(Testlmporto.class); } Per lanciare la classe (che in realtà non contiene ancora nessun test) possiamo passarla al metodo statico run() della classe junit. textui.TestRunner. Per comodità abbia- mo invocato questo metodo dal maini) della stessa Te- stlmporto. Proviamo a lanciare questo maini) e vediamo cosa succede. Ecco l'output: Time: There was 1 failure: 1) warning(junit. framework. TestSuite$l) junit. framework. AssertionFailedError: No tests found in serie_junit_l .Testlmporto at serie_junit_l.TestImporto.main(Testimporto.java:14) FAILURESM! Tests run: 1, Failures: 1, Errors: Ci basta un solo sguardo alle ultime due righe del- l'output per vedere che il nostro test è fallito. Il motivo è anche spiegato esplicitamente nel messaggio di erro- re: JUnit non ha trovato i test che si aspettava nella clas- se. Dunque dobbiamo scrivere almeno un test. Ciascun test è un metodo public void il cui nome inizia con la sequenza di caratteri ''test". Il TestRunner tro- verà e chiamerà "automagicamente" questi metodi. Dobbiamo testare il metodo Importo. getValorei), quindi per chiarezza chiamiamo il nostro test con il nome test- GetValoreQ: public void testGetValoreQ { Importo il = new Importo(true, 12 ,06); assertEquals(il. getValoreQ, x 12.06 "); Importo i2 = new Importo(false, 0, 99); assertEquals(i2. getValoreQ, x -0.99' '); } Qui stiamo vedendo all'opera il succo della filosofia JUnit. Se un test restituisse lunghe sfilze di numeri, ben presto ci stancheremmo di farlo girare e di perdere del tempo per controllarne il risultato. Un test deve invece essere semplice, e deve avere un risultato booleano che possa essere verificato con un solo colpo d'occhio: o riesce, o fallisce. Per raggiungere questo obiettivo dob- biamo usare le cosiddette asserzioni, cioè dichiarare il risultato che ci aspettiamo di ottenere dal codice che stiamo testando. La classe TestCase ci mette a disposi- zione diverse asserzioni, e tra queste assertEqualsi) , un metodo che accetta due parametri e restituisce true se e solo se i due parametri sono uguali. La versione di as- sertEqualsi) che abbiamo usato noi accetta due para- metri di tipo long. Ad esempio: Importo il = new Importo(true, 12, 06); assertEquals(il. getValoreQ, "12.06"); In questo caso abbiamo costruito un importo e abbia- mo dichiarato: mi aspetto che il metodo getValorei) di questo importo restituisca la stringa "12.06". Possiamo usare tutte le asserzioni che vogliamo in un test. Il test riesce se e solo se tutte le asserzioni riescono, e fallisce se anche una sola asserzione fallisce. Ecco tutto questo discorso distillato in una seconda semplice regola: I test unitari devono essere semplici. Un test unitario può avere solo due risultati: o riesce, o fallisce. Notate che per testare il metodo getValorei) abbiamo creato due oggetti Importo con parametri "quasi a ca- saccio", ma non del tutto. In particolare abbiamo scel- to un importo positivo e uno negativo, per cercare di 102 ►►► F e b b 3 http://www.itportal.it A d non avere sorprese in nessuno di questi due casi. Tor- neremo suir argomento tra poco. Intanto proviamo a lanciare il test e vediamo qual è il risultato questa vol- ta: Time: There was 1 failure: 1) testGetValore(serie_junit.TestImporto) junit.framework.ComparisonFailure: expected:< > but was:<...0...> at serie_junit.TestImporto.testGetValore( Testlmporto.java : 22) at serie_junit.TestImporto.main(TestImporto.java:ll) FAILURESM! Tests run: 1, Failures: 1, Errors: Un altro errore! Questa volta non è colpa della man- canza di test. Il test c'è, ma è fallito. Cosa è successo? NON È MAI TROPPO SEMPLICE Purtroppo la versione di JUnit che stiamo usando dà un messaggio di errore piuttosto ambiguo quando fal- lisce un confronto tra stringhe, quindi non è del tutto facile scoprire dov'è il problema. Ma di sicuro sarebbe stato molto più difficile se avessimo scoperto questo bug solo tra qualche tempo, magari senza poter più sa- pere in quale parte del codice andarlo a cercare. Alme- no in questo caso siamo sicuri che Terrore deve essere nelle poche righe di codice che abbiamo appena scrit- to. Riuscite a vederlo? L'errore sta nel fatto che il nostro metodo getValoreO non aggiunge uno zero dopo la virgola se il numero di centesimi è inferiore a 10. Quindi l'output della prima chiamata a getValoreO non è "12.06" come ci aspettava- mo, bensì "12.6". Non è difficile correggere questo bug modificando il metodo getValoreO: public String getValoreO £ String risultato = ""; if(!_positivo) risultato += "-"; risultato = risultato + _euro + "."; if(_cent < 10) risultato += "0"; risultato += _cent; return risultato; > Facciamo girare di nuovo il test: Time: OK (1 test) Successo! Ci basta leggere l'ultima riga per vedere su- bito che il nostro metodo getValoreO funziona. E' una vera fortuna che questo problema sia saltato fuori su- bito, anziché tra qualche tempo. Dite la verità: magari non ve ne eravate accorti nemmeno voi. Eppure questo codice sembrava talmente semplice che probabilmente non vi sarebbe mai venuto in mente di testarlo. Questa è la dimostrazione del fatto che anche un pezzo di co- dice semplicissimo può contenere dei bug - anzi, pro- babilmente sarete d'accordo con noi se diciamo che i bug nel codice più semplice sono anche i più difficili da trovare. Certo, scrivendo anche i test scriviamo molto più codice, quindi abbiamo più possibilità di sbagliare. Cosa succede se sbagliamo qualcosa nei test? La stessa cosa che succede se sbagliamo qualcosa nel codice: l'errore salta fuori immediatamente appena il test fallisce. Quindi è vero che potenzialmente possia- mo commettere più errori, ma la gran parte di questi errori sono immediatamente visibili, e quindi innocui. CATTIVI QUANTO BASTA Nessun test ci darà mai la garanzia assoluta che il co- dice funziona al di fuori dei pochissimi casi particolari che testiamo direttamente. Diamo quindi un altro sguardo al nostro test. Siamo sicuri di non aver di- menticato niente? Abbiamo detto che un test scritto be- ne non deve semplicemente provare due o tre casi qualsiasi, ma deve anche cercare i casi particolarmente infelici: i casi limite, i casi particolari, eccetera. Insom- ma, un buon test deve essere un po' perfido. Chiedia- moci ad esempio quali sono i casi limite della classe Importo. Quando creiamo un Importo, quali sono i valo- ri massimi e minimi che possiamo passargli? Rivedia- mo il costruttore: public Importo(boolean positivo, long euro, int cent); Il primo parametro è booleano, quindi non presenta problemi. Per quanto riguarda gli altri due parametri poniamoci questi vincoli: euro >= cent >= euro <= Long.MAX_LONG (massimo valore di un long in Java) cent < 100 I primi due vincoli per ora vengono lasciati al buon cuore di chi usa la classe. Se qualcuno passa dei valori negativi al costruttore di Importo, il codice non funzio- na più. Per ora supponiamo che i client della classe sappiano come usarla, quindi facciamo finta di niente (il mese prossimo risolveremo questo potenziale pro- blema). Il terzo vincolo è sempre automaticamente ri- spettato, perché il compilatore impedirà a chiunque di passare come secondo parametro del nostro metodo qualcosa che sia più grande di un long. II quarto vincolo è interessante. Cosa succede se qual- Junit a: Java è, se vi pare I Tutti gli esempi di I questa breve se- rie di articoli saranno in linguaggio Java, per tre motivi: perché la comunità Java è stata la prima a sostenere e adottare i test unitari; perché Java è un lin- guaggio orientato agli oggetti semplice e comprensibile; e anche perché useremo per i nostri test una libreria di nome JUnit, che è scritta in Java. Se pre- ferite qualche altro lin- guaggio object-orien- ted, come C# o C++, seguiteci pure tranquil- lamente. Non dovreste comunque avere pro- blemi a capire gli esempi, e sicuramente troverete in rete una conversione di JUnit per il vostro linguaggio preferito. Ne esistono per quasi tutti i lin- guaggi di programma- zione terrestri, inclusi quelli meno disposti a collaborare (come Vi- sual Basic e altri lin- guaggi non orientati agli oggetti). Date uno sguardo alla pagina http : //www, x program - minq.com/software.htm Se invece ancora non conoscete alcun lin- guaggio object-orien- ted, cosa aspettate a impararne uno? http://www.itportal.it 3 ►►► 103 Junit Troppe lingue r^ftl Nel codice dei no- \---J\ stri esempi usia- mo nomi per metà in inglese e per metà in italiano, come getValo- re(). Mescolare le due lingue è di solito una cattiva idea, e alla lun- ga può rendere il codi- ce piuttosto confuso. Ma in questo caso vo- gliamo rispettare le convenzioni di Java, come quelle sui metodi "get" e "set", e allo stesso tempo vogliamo facilitare la vita ai let- tori che non hanno molta pratica con l'in- glese. Quindi abbiate pazienza, e rassegna- tevi a vedere altri nomi in "lingua mista" nel corso di questa serie. Se conoscete bene l'in- glese, vi consigliamo di non usare l'italiano nel vostro codice se non nei commenti. cimo crea un Importo passandogli un numero di cente- simi maggiore o uguale a 100? Per come abbiamo scrit- to il codice l'operazione è legale e i centesimi in ecces- so vengono automaticamente convertiti in Euro. Ma i test che abbiamo scritto fino ad ora non verificano co- sa succede in questo caso particolare. Tanto per andare sul sicuro, aggiungiamo un paio di test. In uno provia- mo a passare esattamente 100 centesimi (equivalenti ad 1 Euro), così ne approfittiamo anche per verificare che l'output sia corretto nel caso in cui i centesimi del- l'importo sono esattamente pari a (un altro caso limi- te). Nel secondo test scegliamo un numero di centesi- mi molto alto, per verificare che tutti vengano conver- titi in Euro come ci aspettiamo. Notate che stiamo cer- cando a tutti i costi proprio i casi "sfortunati" nei qua- li il codice potrebbe fallire. public void testGetValoreQ < Importo il = new Importo(true, 12, 06); assertEquals(il.getValore(), x 12.06' '); Importo i2 = new Importo(false, 0, 99); assertEquals(i2.getvalore(), x -0.99" ); Importo i3 = new Importo(true, 12, 100); assertEquals(i3.getValore(), x 13.00' '); Importo i4 = new Importo(false, 12 , 375); assertEquals(i4.getvalore(), *-15.7E "); } Facciamo girare di nuovo i test. Successo su tutta la linea! Il nostro codice si comporta secondo le aspettative. Ad esempio, 12 Euro e 375 cen- tesimi diventano 15 Euro e 75 centesimi. Possiamo dir- ci soddisfatti, ed enunciare la terza regola dei test uni- tari: Quando scrivi un test unitario, lascia spazio al Mister Hyde che è in te. Cerca di trovare quei casi limite o quelle situazioni sfortunate nelle quali il tuo codice potrebbe non fun- LA GRANDE BARRA VERDE Ora che abbiamo scritto il nostro primo test unitario possiamo fare un altro salto di qualità. Finora abbiamo usato la versione testuale del TestRunner, contenuta nel package junit textui. D'ora in poi useremo il Te- stRunner grafico del package junit swingui. Modifichiamo il metodo Testlmport.mainQ: public static void main(String[] args) £ junit.swingui .TestRunner. run(Testlmporto.class); } Ora lanciamo i test. Visto che differenza? La barra verde che vedete al centro della finestra indi- ca che tutti i test si sono conclusi con successo. Da ora Test class name: serie Junit.Testlmporto I-I I ■■■ I E Reload classes every run 1 1 Runs: 1/1 x Errors: x Failures: Ju x Failures £ Test Hierarchy JFinished: 0,032 seconds Fig. 1: La versione visuale del TestRunner. in avanti, questa barra verde sarà il nostro passaporto per il paradiso. Un programmatore abituato a scrivere i test unitari per il proprio codice può tenere il Te- stRunner aperto sullo sfondo e far girare i test in conti- nuazione, anche ogni volta che compila. Se appare la barra verde, si va avanti. Se invece un test fallisce la barra diventa rossa. Allora ci si ferma e si corregge il codice (o il test) malfunzionante. Quando vi sarete abi- tuati a questo modo di lavorare non accetterete com- promessi: solo se il vostro codice è completamente te- stato e se vedete una barra verde andrete avanti tran- quilli. La pace e la serenità che i test unitari vi daranno sarà una delle sensazioni più piacevoli che potrete spe- rimentare nella vostra attività di programmatori. Ora sappiamo anche perché i test devono essere boo- leani. Solo così potranno essere verificati da un sistema automatico, e daranno un risultato visibile al primo sguardo e non ambiguo. Di conseguenza saremo invo- gliati a far girare tutti i test ogni volta che facciamo una modifica al codice, per quanto piccola. Quello che era un compito gravoso diventerà un piacere, perché ba- sterà premere un pulsante per avere una "pacca sulla spalla virtuale" nella forma di una barra completa- mente verde, che ci rassicurerà circa il corretto funzio- namento del nostro sistema. E così siamo arrivati alla quarta e ultima regola del per- fetto autore di test unitari: Lancia i test in continuazione. Se vedi una barra verde, vai avanti tranquillo. Se vedi una barra rossa, fermati e correggi il proble- ma dovunque esso sia - nel codice o nei test. Ci state prendendo gusto? Allora arrivederci al prossi- mo numero di ioProgrammo, dove affronteremo un argomento che di solito non viene associato alla pro- grammazione dei computer: il ritmo. Paolo Perrotta 104 ►►► F e b b 3 http://www.itportal.it F T W A Il Software sul CD-ROM di ioProgrammo Ecco i migliori software scelti per voi da ioProgrammo: Borland JBuilder 8 trial, XML Spy 5.0 Professional Edition, AppMentor 3.0 for Visual Basic. Installazione ActiveX in Delphi I Dal menu "Com- ponent" selezio- nare la voce "Import ActiveX Component"; nella schermata pre- sente a video è visibi- le una list box conte- nente l'elenco degli ActiveX installati nel sistema; da questi è possibile scegliere il componente e instal- larlo mediante il bot- tone Instali. Tramite l'uso dei bottoni "Re- move" e "Add" è pos- sibile rispettivamente rimuovere un compo- nente dalla list box o aggiungerne uno non presente in essa ma comunque residente in qualche directory del sistema. Dalla list box "Palette Page" si seleziona la sezione (Standard, Additio- nal, Win32, etc, etc) nella quale troverà posto l'icona del com- ponente. Borland JBuilder 8 trial Borland ha da poco rilasciato la nuova versione di JBuilder 8.0. Questa nuova release è caratteriz- zata da una struttura completa- mente rinnovata e basata sul fra- mework open source Jakarta Struts. Da parte degli sviluppato- ri è stata data una notevole im- portanza alle funzioni di colla- borazione, testing e di debug- ging, ora, per esempio, l'Ide può eseguire lo "spot debug", solo su una piccola parte del codice (per poi modificarlo nell'immediato), su JSP o anche su codice non Ja- va. Le funzioni di collaborazione, per lo sviluppo in team azienda- li, invece, si avvalgono dei col- laudati tool di Rational: Clear Case, per la gestione e l'integra- zione del codice, CVS per la gestione delle versioni e del codi- ce sorgente. In aggiunta JBuilder 8 supporta tutte le ultime versio- ni degli application server di Bor- land, Bea Systems, Oracle e Sun Microsystems. Sono inoltre sup- portati i sistemi Aix di Ibm e la piattaforma Hp-Ux. Per l'attiva- zione del prodotto si richiede la registrazione sul sito Borland (www.borland.com). Borland_JB\ AUTOFORM@asp Sviluppato da una software house italiana, AUTOFORM@asp è un interessante tool di sviluppo RAD che, poggiandosi su tecnologie consolidate e molto diffuse, per- mette di rendere più rapide molte fasi della progettazione di applica- zioni gestionali, oltre a garantire la coerenza estetica delle interfacce prodotte. Autoform consente la creazione di applicazioni per Inter- net, reti locali e stand alone garan- tendo la semplificazione dell'inter- facciamento con Microsoft SQL Server e Oracle, la generazione di applicazioni multilingua, l'interfac- ciamento degli OLAP Services di Microsoft, la gestione sicura delle transazioni, numerose funzioni per l'export dei dati. L'ambiente auto- form è composto da una serie di strumenti che si possono racchiu- dere in due grandi categorie: stru- menti lato server e strumenti lato client. Dalla parte server abbiamo un motore di database, un'applica- zione che fa da interfaccia tra il database e l'applicazione client e un generatore di report. Lato client troviamo l'applicativo che fa da interfaccia utente e che comunica con il componente server tramite rete locale o via Internet. È pos- sibile installare Autoform in tre modalità differenti: • Server: è la tipologia di installa- zione naturale, richiede Micro- soft NT o 2000, e permette la connessione contemporanea di più client in una LAN; • ASP: permette l'accesso al ser- ver via Internet utilizzando il protocollo http; • Standalone: installazione e uti- lizzo su una singola worksta- tion. Versione dimostrativa. Autoform \ Ariacom Business Reports 1.6 Un potente ambiente per la crea- zione di report automatizzata. At- traverso la creazione dinamica di query SQL, anche gli utenti meno esperti potranno gestire e trarre informazioni dai più complessi da- tabase relazionali. Ariacom Busi- ness Reports può coprire vasto spettro di esigenze: dalla pubblica- zione di una directory su Internet , all'analisi multidimensionale di un data-warehouse. Le funzioni prin- cipali sono: generazione e ottimiz- zazione dinamica delle query, avanzate funzioni di report, limita- zione all'accesso dei dati attraverso un'attenta gestione della sicurezza, possibilità di generare report in di- versi formati (html, csv, txt) e su dispositivi diversi (file, stampante, fax, email) e molto altro. bfreel6.exe Ulead Cool 3D Studio 1.0 Che dobbiate realizzare il nuovo logo della vostra software-house, un filmato introduttivo sul vostro nuovo prodotto, o semplicemente una gif animata che non sia banale, Ulead Cool 3D Studio è il prodotto che fa per voi! La creazione di gra- fica e testo animata raggiunge in questi prodotto livelli di semplcità ed eccellenza mai visti in preceden- za e, grazie alla grande compatibi- lità garantita in output, sarà pos- sibile distribuire i filmati che pro- durremo in molteplici formati: se- quenza di fermo immagini, anima- zione GIF e filmati Flash. Si integra perfettamente con gli altri prodotti Ulead per l'editing Video come MediaStudio Pro e VideoStudio. Versione di valutazione valida trenta giorni. Ulead_Cool3D\ Code Counter Pro 1.0 Una comoda applicazione che con- sente a noi programmatori di quantificare il lavoro che abbiamo svolto o stiamo svolgendo. Code Counter si occupa di valutare il numero di righe, spazi bianchi, commenti e quant'altro sia interes- sante per effettuare statistiche sul codice. I linguaggi supportati sono numerosi: C/C++, Java, Delphi /Pascal, VB, PHP, ASP ed altri ancora, è possiblile effettuare anali- si anche su porzioni di testo che non siano riconducibili a codice sorgente. Versione di prova valida trenta giorni, presenta alcune limi- tazioni nel suo utilizzo. ccountp.exe 106 ►►► F e b b r http://www.itportal.it s o w SUL C D Code Magic 4.2 "Ma non l'avevo già risolto?" que- sta la tipica domanda di noi svilup- patori quando ci troviamo di fronte ad un problema che ci sembra di aver già affrontato, ma non ricor- diamo quando, ne' come. Questa applicazione ci viene in aiuto pro- prio in situazioni come queste. Grazie ad un parser particolarmen- te efficace, riesce ad archiviare tutto il codice che produciamo all'inter- no di una vista ad albero che per- mette un facile reperimento in fase di produzione del codice. Il syntax highlighting supporta i più noti lin- guaggi di programmazione. cm_setup.exe Dev-Pascal 1.9.2 Un completo e gratuito ambiente di sviluppo per Pascal, dotato di tutte le più utili caratteristiche degli IDE moderni. Tra le funzioni disponibili segnaliamo: colorazio- ne del codice personalizzabile, wizard per la creazione di pacchet- ti di installazione, supporto per la creazione di DLL, sviluppo assisti- to per le interfacce, possibilità di impostare dei template per la pro- duzione rapida di codice. Nella di- rectory è anche presente un de- bugger che si integra perfettamen- te nell'ambiente di sviluppo. DevPascal\ Expert Troubleshooter 5.0 Basato su un motore di intelligen- za artificiale, Export Troubleshou- ter consente di costruire applica- zioni di monitoraggio per scoprire immediatamente gli eventuali malfunzionamenti di apparecchi elettronici ed elettromeccanici. So- no disponibili diversi approcci: dalla verifica manuale assistita dal PC, alla generazione di pattern per l'individuazione degli ambiti criti- ci. Particolarmente interessante ri- sulta essere la generazione auto- matica di codice in Basic, C e Pa- scal per la creazione di applicazio- ni di test. ETSetup\ FontLab 4.52 Un sofisticato editor di font che consente di importare, esportare, creare e modificare font nei loro formati nativi. Inoltre, grazie ad un tool di disegno vettoriale, è possibi- le intervenire con precisione nella modifica e nella creazione di font personalizzati. Questa nuova ver- sione include una potente sistema di macro. Versione dimostrativa. FL4WinDemo.exe Hephaestus 2.01 Un completo kit per la realizzazio- ne di giochi di ruolo che permette di scrivere la propria avventura attraverso un semplice linguaggio di scripting e di condividere il gio- co con chiunque altro abbia instal- lato Hephaestus. Essendo scritto interamente in Java, Hephaestus gira su qualsiasi piattaforma. Gratuito.90 hephaestus.zip IBasic Standard Version 1.99c Una occasione per chi si avvicina al mondo della programmazione: Ibasic consente di scrivere applica- zioni stand alone per Windows con estrema semplicità, adottando una sintassi sovrapponibile in buo- na parte al Basic standard. Il lin- guag gio è sufficientemente com- pleto, comprendendo oltre 180 fra comandi e funzioni. In questa nuo- va release, è stata migliorata del 150 percento la velocità di esecuzio ne, sono state inserite nuove fun- zioni per la gestione dei file ed è stata migliorata l'interfaccia utente. ibasic.zip Instant Report 1.4 Un generatore di query SQL che consente la costruzione completa- mente visuale dei report. Conce- pito per essere il più semplice pos- sibile, permette di realizzare report anche complessi con pochissimi colpi di mouse. Compatibile con tutti i più diffusi database sia free che commerciali. Gratuito. ireport-L4.zip NexJDE l.Oa Una comoda applicazione che va ad collocarsi nella categoria degli Universal Data Access tool: con- sente di effettuare query e ottenere risultati a partire da dati immagaz- zinati in un qualsiasi database: Oracle JDBC, SUN JDBC ODBC, PostgreSQL, mySQL, Sybase, e MS SQL Server. I risultati possono es- sere mostrati in una tabella HTML o all'interno di un'apposita inter- faccia utente. JDELight.zip Metamill 2.2 Un software per la modellazione software UML completamente visuale. Metamill risulta partico- larmente semplice da usare, in spe- cial modo se paragonato ad altri software di modellazione UML. Tra le caratteristiche più interes- santi c'è il repository attraverso cui più sviluppatori possono collabo- rare utilizzando i medesimi ele- menti senza "pestarsi i piedi". Una volta sviluppato il modello, Me- tamill è in grado di produrre il codice relativo sia in C++ sia in Java. Shareware. Mmill221.exe MyTool 1.2 Un interessante front end per MySQL comprensivo un editor per effettuare tutte le più comuni ope- razioni di gestione di DBMS per via visuale. Interessante la funzio- ne di monitoraggio dello stato delle query sul server. Versione di- mostrativa. mytool-setup.exe Pro-Test 1.0 Una interessante applicazione che consente di velocizzare e rendere più efficiente una delle parti più noiose della creazione del softwa- re: il testing. Pro-Test si occupa di generare casi di test per analizzare la robustezza del codice che scri- viamo. Usato con attenzione, Pro- Test può migliorare e velocizzare sensibilmente la produzione di co- dice. Versione di prova valida die- ci giorni. Pro-Test_10Day_Trial.EXE ResRipper 3.8 Davvero comodo questo tool che consente di analizzare velocemen- te le risorse immagazzinate nei Risorse Java mgm Molte delle ri- ■» sorse Java ripor- tate all'interno del CD ROM sono munite di file .java, .class e di file html per essere testate. Nel caso di compilazione del file .java si dovrà utiliz- zare un opportuno strumento, come ad esempio il JDK di Sun. Per utilizzarlo si do- vrà operare da prom- pt del DOS, accedere alla directory bin del- l'ambiente stesso ed avviare il Java Com- piler digitando la stringa: javac "nome- file". Installazione componenti VC++/C++ I file C++, delle f librerie facenti parte del CD-ROM, possono essere diret- tamente inclusi all'in- terno dei vostri pro- getti. Le risorse Visual C++ sono invece for- nite del file progetto, quindi dall'ambiente di sviluppo Microsoft utilizzare la voce di menu "Open Project" ed automaticamente tutti i file afferenti al progetto stesso ver- ranno visualizzati, e da tale ambiente è possibile inoltre ese- guire l'applicativo stesso. Se invece ri- sorsa riportata è una dll, allora essa potrà essere direttamente inclusa all'interno di un progetto. http://www.itportal.it 3 ►►► 107 S O F T W A Installazione ActiveX in Visual Basic rèi Dal menu Proget- ^**to selezionare la voce Componenti (CT RL + T); nella scher- mata presente a video è visibile una list box contenente l'elenco dei componenti Acti- veX installati nel si- stema; da questi è possibile selezionare uno o più componenti e confermare median- te il bottone OK; qua- lora il componente non fosse installato nel sistema ma fosse comunque presente nel computer è pos- sibile selezionare quest'ultimo tramite l'utilizzo del bottone "Sfoglia" mediante il quale si ha accesso al- le directory del siste- ma; da queste è pos- sibile localizzare il componente da instal- lare. nostri dischi e che sono normal- mente nascoste: migliaia di imma- gini (BMP, DIB, GIF, JPG) icone, filmati (AVI, MPG, GIF animate), font e suoni. Tutte risorse normal- mente bloccate e nascoste all'inter- no delle applicazioni e che, grazie a ResRipper, possono ora essere individuate, estratte e riutilizzate nei nostri progetti. Versione di prova valida trenta giorni. resripper_trial_version.exe Stylus Studio 4.5 Forte dei molti premi vinti e di una base di installato che conta oltre 30.000 sviluppatori sparsi in tutto il mondo, Stylus Studio si presenta come un potente tool visuale per lo sviluppo e la gestio- ne di documenti XML e XSL. Rimarchevole la qualità della mappatura dei documenti che Stylus Studio offre per via grafica, consentendo una più facile analisi dei documenti. Permette di gestire e modificare tutti i tipi di file che appartengono ad un'applicazione XML: oltre a documenti XML, abbiamo il pieno supporto per gli XSLT stylesheets, DTD e gli XML schema, oltre ai file Java. Gira su Windows NT - 2000 -XP. Tra le funzioni più interessanti, segnalia- mo la possibilità di effettuare il debug e la presenza di una poten- te funzione di preview sulle tra- sformazioni realizzate. Un prodot- to che può risultare valido sia ai principianti che agli esperti e si presta anzi a fare da guida in un percorso di apprendimento delle tecnologie legate a XML. All'atto dell'installazione è richiesto un collegamento Internet al fine di riempire una breve form. Una chiave di attivazione verrà inviata alla propria casella di posta elet- tronica. StylusStudio\ UltraEdit-32 9.2 UltraEdit-32 è principalmente un editor esadecimale con un com- pleto supporto per le macro e nu- merose funzioni avanzate come la conversione di file da DOS a Unix. In questa nuova versione sono sta- te aggiunte delle comode funzio- nalità di autocompletamento, oltre ad un miglioramento complessivo dell'interfaccia. Versione di valutazione valida 45 giorni. uedit32.zip XMLEditPro 2.0 Semplice ed intuitivo, questo edi- tor XML ha giusto l'essenziale per la creazione e l'editing di docu- menti XML. Proprio questo suo essere spartano, lungi dall'essere un limite, diventa un pregio se visto sotto l'ottica della semplicità dell'interfaccia e della velocita. Gratuito. xep2setup.exe XML Spy 5.0 Professional Edition Il più avanzato strumento di svi- luppo per XML. Uno dei punti di forza risiede nella possibilità di switchare velocemente fra quattro differenti viste per ogni documen- to: Enhanced Grid per il dettaglio di tutti gli elementi: Schema Design; Text View per una programmazio- ne a più basso livello e una vista di preview che anticipa come il docu- mento sarà visualizzato da un comune browser. E' possibile svi- luppare sia documenti XML che DTD che saranno poi pubblicabili via FTP attraverso lo stesso XML Spy. Davvero eccezionale il supporto offerto ai Web Services grazie ad apposite interfacce per i protocolli SOAP,XSLeWSDL. XmlSpy\ Dev-C++ 5 Beta 7 (4.9.7.0) Uno dei migliori ambienti graniti di sviluppo per C++. In questa versione, l'interfaccia raggiunge livelli di eccellenza: scrivere, com- pilare, eseguire ed effettuare il debug, tutto molto efficiente e con la possibilità di avere delle icone stile Gnome. Quasi una scelta obbligata per chi vuole sviluppare applicazioni per Win32 e non vuole affrontare la spesa di un Visual C++. Tra le caratteristiche più interessanti: completamento automatico del codice, manager di progetto, syntax highlighting per- sonalizzabile, creazione assistita di librerie statiche e DLL, supporto per i packages e molto altro anco- ra. Provatelo. devcpp4970-gcc32.exe Visual Paradigm for UML Community Edition Un ambiente di sviluppo integrato per UML che farà la gioia di tutte le figure coinvolte nello sviluppo di applicazioni di grandi dimen- sioni. Completamente gratuito, non rinuncia ad una ottima inter- faccia di livello professionale e copre tutte le maggiori esigenze di chi si occupa di UML. Da provare. VParamUML\ AppMentor 3.0 for Visual Basic Una piattaforma che consente di velocizzare la produzione di nuove applicazioni Visual Basic, grazie ad un approccio template- oriented che consente di ridurre i tempi di sviluppo anche dell'80 percento. L'accesso ai database è realizzato attraverso una logica n- tier che consente di demandare buona parte della gestione a basso livello. Che siate sviluppatori esperti o "apprendisti stregoni", AppMentor può essere la soluzio- ne giusta per fare un passo decisi- vo verso lo sviluppo di applicazio- ni professionali. Versione di valutazione. AppMentor3\ ActivePerl 5.8 La versione Windows di una com- pleta distribuzione Perl. Otre al linguaggio, potrete sperimentare il Perl Package Manager (PPM) che, attraverso una semplice interfac- cia e riga di comando, permette di installare e gestire moduli ed estensioni. La copiosa documenta- zione di cui è fornita questa distri- buzione varrebbe da sola a instal- lare il pacchetto. Gratuita. ActivePerl-5.8.0.804-MSWin32- x86.zip 108 ►►► F e b b r http://www.itportal.it LE FAQ DI IOPROGRAMMO Le risposte alle domande più frequenti Ogni mese troverete riportate le domande che più spesso giungono in redazione. Capita frequentemente che, affrontando linguaggi in continua evoluzione, si diano per scontati alcuni concetti o alcune caratteristiche di base: queste pagine sono l'occasione per ribadire o spiegare meglio queste nozioni. Java Cosa accomuna Java e C# Quando fu presentato, il C# sollevò molte perplessità per l'eccessiva somiglianza con Java. Addirittura si ventilò un'accusa di pla- gio, poi caduta nel vuoto, soprattutto per- ché, come spesso accade in questi casi, mol- te delle caratteristiche che C# avrebbe "mu- tuato" da Java erano in realtà preesistenti a Java stesso, della serie: "chi non ha peccato scagli la prima pietra"! Ormai le polemiche sono spente e C# si è di- mostrato semplicemente un nuovo (e ottimo per la verità) linguaggio, e ci sembra interes- sante, a mente fredda, riassumere breve- mente i principali punti di contatto fra il gioiello di Sun e l'astro nascente di Micro- soft. Eccone un sommario elenco: • Entrambi prevedono la compilazione in un codice indipendente dalla piattafor- ma, codice che viene eseguito da una macchina virtuale. • Un meccanismo di Garbage Collection e la scomparsa dei puntatori (fondamen- tali in C++, il C# ne permette l'uso in particolari porzioni di codice dichiarate esplicitamente unsafe). • Potenti meccanismi per la reflection. • Assenza di file Header (anch'essi fonda- mentali in C++). • Tutte le classi derivano dalla classe base Object. • Supporto per i thread attraverso un meccanismo di lock attivato sulle por- zioni di codice marcate come locked /synchronized. • Supporto per le interfacce con eredita- rietà multipla per le interfacce stesse e singola per le implementazioni. • Disponibilità delle inner class. • Assenza di funzioni o costanti globali: tutto appartiene al dominio delle classi. • Uso della "notazione punto" al posto degli operatori "->" e "::" utilizzati in C++. • Tutti i valori sono inizializzati prima di poter essere usati. • Impossibilità si usare gli interi per valu- tare le condizioni di if. • I blocchi di Try/ Catch possono avere la clausola finally. Cosa sono gli obfuscator? Il bytecode racchiude le informazioni che descriviamo con il codice sorgente Java. Il bytecode è generato dal compilatore Java ed è relativamente semplice risalire al codice sorgente attraverso un'operazione di rever- se engineering: molti decompilatori, alcuni pubblicati anche nei CD allegati a ioPro- grammo, consentono infatti di passare dal bytecode al codice sorgente in maniera pres- soché automatica, e con un buon grado di approssimazione. La proprietà intellettuale legata al codice da noi prodotto è dunque re- sa abbastanza vulnerabile. Gli obfuscator (offuscatori) rendono il lavoro dei decompi- latori più arduo, ed il codice che si riesce a ti- rar fuori è meno leggibile e meno utile. Esi- stono essenzialmente due tipi di obfuscator: una prima categoria si occupa di cambiare i nomi di classi, campi e metodi con sequenze di caratteri senza senso, con l'obiettivo di di- sorientare e far perdere il senso complessivo del progetto. Ciò che rimane in chiaro è la struttura dell'applicazione che, con un più attento lavoro di reverse engineering, può comunque essere "riutilizzata". Una secon- da categoria di obfuscator, oltre ad offuscare i nomi, si occupa di offuscare anche il flusso di esecuzione dell'applicazione, attraverso delle leggere modifiche al bytecode che ren- dono oscuro il flusso di esecuzione dell'ap- plicazione senza (ovviamente) modificarne il comportamento. Tipicamente gli offusca- tori di questo tipo intervengono nelle strut- ture di selezione e nei costrutti di loop, mo- dificandoli in maniera tale da non consenti- re la ricostruzione del sorgente Java equiva- lente. Perché dichiarare un metodo "synchronized"? La sincronizzazione dei metodi in Java consente la realizzazione di sezioni criti- che nella programmazioni multithread. Sezioni cioè in cui è necessario garantire che la concorrenza fra i vari thread sia in qualche modo "sospesa". Un problema che si presenta tipicamente nell'accesso a risorse condivise; mettiamo il caso che due applicazioni vogliano scrivere sul- l'hard disk: per garantire la consistenza dell'informazione, è ovviamente necessa- rio che quale delle due cominci per prima a salvare i dati non venga interrotta dal- l'altra. In questi casi si adotta una sorta di "lucchetto" virtuale associato alla risorsa (in questo caso l'hard disk): se l'applica- zione Appi vuole scrivere sul disco, deve innanzitutto procurarsi il lucchetto del- l'hard disk. L'applicazione App2, che pu- re vuole scrivere, tenta di procurarsi il lucchetto dell'hard disk ma deve attende- re, poiché il lucchetto è detenuto da Appi. Quando Appi avrà finito le sue operazioni di scrittura, avrà cura di rila- sciare il lucchetto. Lucchetto che verrà in- tercettato da App2, che inizierà le sue operazioni di scrittura, e così via. Ecco spiegata, in termini poco ortodossi, l'uti- lità della nozione di "lucchetto" nella programmazione, multithread. Un meto- do è dichiarato synchronized secondo la sintassi: public synchronized int scriviValoreQ { - } http: //www. itportal.it 3 ►►► 109 Grazie a questa dichiarazione, ad ogni istanza della classe cui il metodo appar- tiene sarà associato un lucchetto. Nel mo- mento in cui un thread invocherà il meto- do scriviValoreO, esso diverrà detentore del lucchetto associato alla classe di cui scriviValoreO è metodo. Il possesso di que- sto lucchetto durerà fino alla completa esecuzione del metodo scriviValoreO, ciò significa che, per tutto questo tempo, nes- sun altro thread potrà invocare un meto- do synchronized di quello stesso oggetto. Se il metodo, oltre ad essere dichiarato synchronized, è definito anche come sta- tic, il lucchetto sarà esteso all'intera classe e non più alle singole istanze. Perché dichiarare una classe abstract? La parola chiave abstract serve a indicare una classe o un metodo che non ha im- plementazione. Quando una classe è di- chiarata abstract, essa non può essere istanziata. Solo le sottoclassi di una classe astratta possono essere istanziate, sempre che non siano state dichiarate abstract a loro volta. Similmente, dichiarando un metodo come abstract, si specifica che il metodo non è implementato nella classe in cui è dichiarato, ha solo la signature: abstract class Risorsa { //■■■ public abstract int leggiValoreQ; } Un metodo astratto esiste solo allo scopo di essere sovrascritto (overridden) nelle sottoclassi della classe in cui è dichiarato. E' bene tenere presente che un metodo astratto può essere dichiarato solo nel- l'ambito di una classe astratta, mentre non è obbligatorio che una classe astratta presenti dei metodi astratti. Nel momen- to in cui si dichiara una sottoclasse di una classe astratta, la sottoclasse deve neces- sariamente fornire una implementazione dei metodi astratti della superclasse, o in alternativa essere dichiarata a sua volta come classe astratta. Qual è il ruolo di static in Java? Quando dichiariamo static una variabile di una classe, quella varibile viene istan- ziata una sola volta, qualsiasi sia il nume- ro di istanze create a partire da quella classe. In pratica, se un'istanza della classe cam- bia il valore di quella variabile statica, il cambiamento si riflette in tutte le altre istanze della classe. Se invece dichiaria- mo un metodo static, il campo di azione del metodo si sposta dalle istanze della classe alla classe stessa. Un metodo stati- co può fare riferimento solo a variabili e metodi statici e non accetta V over ride nel- le classi derivate dalla classe di apparte- nenza: un metodo static è implicitamente definito final. Cos'è un file JAR ? JAR è un acronimo e sta per Java ARchi- ve. Un file .jar è semplicemente un archi- vio di file compressi. In un file JAR è pos- sibile sistemare tutte le classi necessarie ad un'applicazione con il vantaggio di una notevole riduzione dello spazio oc- cupato dalla versione del software che di- stribuiremo. Supponiamo di voler creare un file .jar, chiamato archivio.jar, in cui si- stemeremo tutti i file presenti in una di- rectory. Ecco la sintassi: jar -cf archvio.jar *.* Lo switch -e è servito a indicare la vo- lontà di creare un nuovo archivio, mentre con -f abbiamo la possibilità di specifica- re il nome del file. Per avere il dettaglio delle operazioni compiute da Jar, possia- mo aggiungere lo switch -v. jar -cvf ajarfile.jar *.* In questo modo appiamo attivato V opzio- ne "verbose", con il risultato di avere in output tutte le informazioni riguardo ai file inseriti nell' archivio. Per estrarre il contenuto di un file Jar, la sintassi è la se- guente: jar -xf ajarfile.jar È possibile visualizzare V elenco completo delle opzioni disponibili avviando il co- mando jar, senza fornire alcun parametro. Cos'è la Extreme Programming? Con Extreme Programming (XP) si inten- de una serie di regole e principi per lo svi- luppo rapido di software professionale. Lo scopo è dunque duplice: massima ve- locità nella realizzazione e massima qua- lità possibile del prodotto finito. XP rappresenta una sorta di metodologia, anche se una delle più leggere e semplici da seguire, che può essere ripetuta ogni qual volta si debba sviluppare un'appli- cazione. Vediamo brevemente le dodici principali regole della Exreme Program- ming: 1) Il punto di partenza è rappresentato dalla scrittura delle cosiddette "User Stories". In questa fase i committenti e gli sviluppatori collaborano per la definizione specifiche funzionali e del tempo necessario alla realizzazione dell'applicativo. I committenti sono chiamati a definire scrivere queste storie: ognuna rappresenta una fun- zionalità del sistema. Le singole storie saranno esaminate dagli sviluppatori al fine di valutare il tempo di svilup- po necessario. Normalmente, se una storia impiega più di tre settimane, si considera troppo grande e va spezza- ta in più sotto-storie, se invece richie- de meno di una settimana, vuol dire che si è scesi ad un livello di dettaglio eccessivo e la storia va accorpata con un'altra. 2) Rilasciare nuove versioni il più pre- sto possibile. Rilasciare spesso al cliente nuove versioni, aggiungendo anche poche caratteristiche per volta, consente di avere un continuo feed- back sul lavoro e permette di correg- gere immediatamente eventuali errori della progettazione. 3) Scegliere una metafora per il sistema che si va implementando. La metafo- ra consente di ricordare facilmente i nomi degli oggetti coinvolti nel pro- getto o consente di "indovinarli" se non li si ricorda. 4) Usare sempre il design più semplice possibile. Le richieste possono cam- biare da un giorni all'altro e dunque è bene progettare con l'obiettivo di sod- disfare solo le richieste attualmente formulate. Inoltre, il fatto che l'XP sia un processo iterativo, consente di pro- seguire per affinamenti successivi. 5) Testare continuamente. Prima di scri- vere una nuova funzione, il program- matore scrive un test per metterla alla prova. Il lavoro si considera fatto quando i test hanno esito positivo. Esistono due tipi di test: !!()►►► F e b b r http: //www. itportal.it a. Unit Test: sono test automatici scritti dagli sviluppatori per pro- vare il codice mentre lo scrivono. Generalmente ogni Unit Test pro- va una singola classe o un piccolo insieme di classi, inoltre per effet- tuare gli Unit Test si adottano tipi- camente dei framework specifici come il JUnit (descritto nelF artico- lo dell' ottimo Paolo Per rotta pre- sente in questo stesso numero di ioProgrammo) b. Acceptance Test: noti anche come test funzionali, sono definiti dai committenti allo scopo di verifica- re che il sistema rispetti comples- sivamente le specifiche. I test fun- zionali si occupano in genere di verificare Finterò sistema o larghe porzioni di esso. Un tipico test consiste in uno script equivalente alle azioni di un comune utente di fronte al sistema e nella verifica che il risultato sia coerente con le aspettative. 6) Cancellare sempre il codice super- fluo. Spesso si è portati a mantenere delle vecchie funzioni o classi non più necessarie, o anche pezzi di codice che ormai non utilizziamo più. L'XP consiglia di eliminare tutto il super- fluo il più spesso possibile. Grazie ai continui test abbiamo la garanzia di non buttare via niente di utile e pos- siamo dunque procedere ad un refac- toring continuo. 7) Programmazione in coppia. Tutto il codice è prodotto da due programma- tori seduti alla stessa workstation. In pratica, questo "trucco" consente di avere il codice revisionato nel mo- mento stesso in cui viene prodotto. Può apparire una misura eccessiva ma è stato dimostrato che il codice prodotto da due programmatori che lavorino assieme è migliore e più con- sistente del codice prodotto dagli stes- si separatamente. Tipicamente, il pro- grammatore che scrive "fisicamente" il codice bada alla correttezza sintatti- ca, mentre l'altro cerca di tenere uno sguardo di più ampio respiro, control- lando la correttezza semantica di quanto si va sviluppando. 8) Il codice è di tutti. Nessuna persona e nessuna coppia può dirsi proprietaria di un pezzo di codice. E' riconosciuto a tutto il team il diritto di agire su qualsiasi parte del codice. Chiunque può aggiungere funzionalità ed effet- tuare debugging su qualsiasi pezzo di codice in qualsiasi momento. Questa che potrebbe apparire una libertà ec- cessiva è possibile e assolutamente si- cura grazie alla presenza dei test: chiunque apporti una modifica, deve garantire che essa superi i test previsti per quella porzione di codice. 9) Integrare continuamente. Gli svilup- patori devono integrare le modifiche che apportano al più presto possibile e, in ogni caso, non deve passare più di un giorno prima che le modifiche vengano integrate nel sistema. Solo così è garantita la consistenza fra tut- te le unità di codice, oltre a permette- re a tutti gli sviluppatori di lavorare sempre con la versione più aggiorna- ta del codice. 10) Si lavora non più di quaranta ore a settimana. Tutti i programmatori van- no via al momento giusto, solo in si- tuazioni critiche è contemplata una settimana di straordinari. Se comin- ciano a presentarsi più settimane con- secutive di straordinari, vuol dire che c'è qualcosa da rivedere nel processo di produzione. 11) Il cliente deve essere sempre dispo- nibile. È necessario che ci sia un con- tinuo scambio di idee fra gli sviluppa- tori e il committente che non deve dunque limitarsi a presentare le User Stories air inizio del progetto, ma de- ve rappresentare un punto di riferi- mento costante per tutto il team. Le User Stories sono infatti dei canovacci molto approssimativi ed è necessario un continuo feedback del cliente per indirizzare correttamente lo sviluppo del progetto. 12) Codice standard. Il codice deve esse- re scritto rispettando standard comu- ni accettati da tutti. In pratica, leggen- do diverse porzioni di codice, non do- vrebbe essere possibile distinguere la "mano" del programmatore. Questa coerenza formale consente una facile leggibilità ed un semplice accesso a tutti gli sviluppatori verso qualsiasi porzione del progetto. Visual Basic Quando usare Private per le dichiarazioni di variabili e di procedure? Ogni volta che ciò è possibile, è bene usare Private per le dichiarazioni. Dichiarando Private le procedure e le variabili di un mo- dulo, infatti, queste saranno accessibili sol- tanto nel modulo stesso: Private variabile As Integer Private Sub Procedura() Conoscere a priori se una variabile o una procedura debba essere dichiarata Private o Public non è facile, specialmente se non si è valutato bene il problema al momento della redazione del codice. Se la variabile o la pro- cedura è dichiarata Public quando non do- vrebbe esserlo, si potrebbero creare proble- mi di eccessi di visibilità dell'oggetto in que- stione. Per fortuna, molti di questi problemi possono essere risolti con un'analisi auto- matica del codice. Come si può ottimizzare la velocità di visualizzazione? La velocità di visualizzazione di un pro- gramma è spesso più importante della sua reale velocità di esecuzione. Ecco al- cuni suggerimenti per aumentare la velo- cità di visualizzazione di un'applicazio- ne: • Impostare la proprietà ClipControls sul valore False. • Usare il controllo Image invece di Pic- tureBox e Label invece di TextBox quan- do possibile. • Nascondere i controlli quando si im- postano le proprietà; ciò impedisce di effettuare ridisegni multipli. • Mantenere nascosto un form prece- dentemente caricato per visualizzarlo velocemente. Questo metodo richiede un dispendio cospicuo di risorse di memoria. • Usare priorità basse per i processi lun- http:// www. itportal.it 3 ►►► 111 ghi. Si possono realizzare processi a bassa priorità di esecuzione collocan- do opportunamente dei controlli DoE- vents. Un ottimo posto dove collocare i DoEvents sono i cicli interni. Bisogna tenere presente, però, che l'utente po- trebbe premere qualche tasto. Il pro- cesso a bassa priorità deve tener con- to di ciò che l'utente può o non può fare durante la sua esecuzione. Impo- stare quindi le proprietà Processing col valore True. Usare barre di avanzamento di stato. Pre-caricare dati che serviranno suc- cessivamente. Per esempio, potete ca- ricare i contenuti di una tabella di un database in un vettore. Usare la caratteristica Show degli eventi Form_Load. Semplificare lo startup form o usare uno splash screen. C + + Quando vengono invocati i distruttori per le variabili locali? Le variabili "non-static" vengono distrut- te automaticamente quando V esecuzione esce dal loro ambito di visibilità. Senza voler approfondire troppo il concetto di visibilità, possiamo affermare che V ambi- to di vita di una variabile termina subito dopo il simbolo "}" in cui la variabile è stata dichiarata. Al momento dell'uscita da tale ambito, le variabili vengono di- strutte dal compilatore chiamando il di- struttore appropriato nella loro classe di appartenenza. Se l'oggetto ha allocato memoria (ad esempio un array), la distru- zione rilascia la memoria precedentemen- te impegnata: il main() { //■■■ {vettore v(5); // alloca la memoria // operazioni... } // fine dell'ambito di visibilità dell'oggetto vettore // l'oggetto vettore viene qui distrutto, e la memoria viene rilasciata //■■■ } Come funzionano le espressioni separate da virgola? Le espressioni separate da virgola deriva- no dal linguaggio C. Spesso queste istru- zioni sono impiegate all'interno di elicli for e while. Le regole sintattiche di queste istruzioni, tuttavia, sono lungi da essere intuitive. In primo luogo, un'espressione può essere composta di una o più espres- sioni secondarie separate da virgola. Consideriamo, ad esempio, la seguente espressione: if (++x, --y, cin.good()) L'istruzione condizionale if contiene tre espressioni separate da virgola. Il compi- latore C++ si assicura che ciascuna delle espressioni venga valutata e che i relativi effetti avvengano. In questo caso, la va- riabile x viene incrementata e la variabile y è decrementata. Tuttavia, il valore del- l'intera espressione separata da virgola è soltanto il risultato dell'espressione più a destra. Di conseguenza, l'istruzione if di sopra è vera solo se il metodo cin.goodO ritorna true. Consideriamo invece la se- guente espressione: int j = 10; int i=0; while( ++i, — j) { //- > Nel ciclo while, la variabile i viene incre- mentata mentre la variabile j è decremen- tata. Il ciclo prosegue fintanto che il valo- re di j è maggiore di 0. Come richiamare una funzione prima dell'avvio di un programma? Alcune applicazioni richiedono di invo- care funzioni prima che la parte principa- le del programma venga eseguita. Ad esempio, la funzione di log deve essere ri- chiamata prima dell'esecuzione dell'ap- plicazione. Il metodo migliore per invo- care queste funzioni è di collocare la loro invocazione all'interno del costruttore di un oggetto globale dell'applicazione, questo perché gli oggetti globali sono co- struiti prima che l'esecuzione passi al programma. Pertanto, le funzioni invoca- te in un costruttore di un oggetto globale verranno invocate prima della funzione maini). Ad esempio: class Login { public: LoginQ { attiva_login(); > h Login log; // istanza globale int main() { record * prec=Leggi_login(); //.. codice dell'applicazione } L'oggetto globale log viene costruito pri- ma che l'esecuzione passi alla funzione main(). Durante la costruzione, l'oggetto invoca la funzione attiva _login(). Quando l'esecuzione passa alla funzione main() dell'applicazione, è possibile quindi leg- gere i dati dal log file. Cosa sono i puntatori ai membri? Una classe può avere due categorie di membri: membri funzioni (metodi) e mem- bri dati (variabili membro). Inoltre, esisto- no due tipi di puntatori: puntatori a me- todi e puntatori a variabili membro. Gli ultimi sono i più diffusi poiché, general- mente, le classi non hanno variabili mem- bro pubbliche. Tuttavia, se utilizziamo codice C esistente che contiene dichiara- zioni struct o classi che hanno variabili membro dati pubbliche, i puntatori a queste variabili possono rivelarsi molto utili. La sintassi per i puntatori a membri è una delle più complicate del linguaggio C++. L'uso di tali puntatori, però, rappre- senta una caratteristica molto potente del linguaggio, perché permette di invocare una funzione membro di un oggetto sen- za conoscerne il nome. Analogamente, è possibile usare un puntatore per esami- nare o modificare il valore di una variabi- le membro senza conoscerne il nome. 112**> F e b b http: //www. itportal.it N B O Box L'esperto risponde... FLASH e VB ► ►►►►►►►►►►►►►► Gentile Giuliano Uboldi, sono un programmatore "in erba" di 14 anni ed avrei bisogno del tuo aiuto: nel numero 10 di ioProgrammo (12 102), ho trovato molto interessante l'articolo sull'integrazione di Flash con VB e provando a realizzare, per cominciare, il programmino sono in- cappato in un errore: innanzi tutto premetto che io utilizzo VB6 e Flash MX su una piattaforma WIN 2000. Ora, dopo aver compilato la parte Flash e quella VB ho provato a far partire il programma dal VB stesso, ma il debugger trova un errore nella Sub Aggiorna alla stringa: "Textl.Text = Flash l.GetVariable("areaTesto.text" )" (Textl è il Testo dell'articolo); l'errore riscontrato è: "Errore di runtime '-2147467259(80004005) Metodo 'GetVariable' dell'oggetto 'IShockVaweFlash' non riuscito La ringrazio e la prego di inviarmi o pubblicare al più presto una sua risposta. Distinti saluti. Alberto Santini Risponde: Giuliano Uboldi Caro Alberto, devo ammettere che, prelevando direttamente i file del- l'esempio dal ed ed utilizzandoli in Win 2000 si ottiene l'errore di runtime che hai riportato. Ciò è dovuto ad un errato ri- ferimento di VisualBasic rispetto al com- ponente da utilizzare per eseguire l'ap- plicazione in oggetto. Per correggere questo comportamento si deve, dal menù Progetto- riferimenti, controllare che il riferimento al controllo Shockwave Flash sia legato ad un file .ocx piuttosto che ad uno .oca. Se infatti il riferimento al componente ShockWave Flash è legato ad un .oca, anche se con lo stesso nome, il programma non funziona. Tutto ciò che devi fare quindi è selezionare dall'elenco che appare, selezionando le voci di menù sopra indicate, l'altro componente ShockWave Flash.ocx che sicuramente troverai. Vedrai che con questa semplice correzione riuscirai a far girare il tutto. Un caro saluto. MFC e WIN32 ► ►►►►►►►►►►►►►► Un saluto e molti complimenti per la rivista che leggo bramo- samente ogni mese. Domandina: che differenza c'è fra MFC e WIN32 (visualC++)? E' una questione di librerie, o anche di architettura? Alessandro Danilo Brevi Per realizzare un'applicazione che giri su piattaforma Windows, il compilatore deve interfacciarsi con il sistema operativo attraverso delle API (Application Program- ming Interface) che nel caso di Windows prendono proprio il nome di Win32 (dove 32 sta per 32bit). Inizialmente, le API di Windows vennero sviluppate come un insieme di procedure separate e rilasciate attraverso una piattaforma chiamata Windows API SDK (Software development Kit), inizialmente con Api a 16 bit. La realizzazione delle applicazioni sotto Windows era pertanto realizzata mediante l'uso di queste "procedure" di sistema. Un esempio di applicazione realizzata sono le prime versioni di Word per Windows. Ad un certo punto, visto il diffondersi della programmazione object oriented, un team venne incaricato di sviluppare un framework ad oggetti in cui venissero incapsulate le API di sistema. Questo per facilitare la realizzazione delle applicazioni e per permettere un elevato riutilizzo del codice. Il framework in questione è ap- punto MFC (Microsoft Founfation Classes). Visto il successo di questo framework, ad un certo punto si decise di riscrivere le API di Windows in chiave MFC. Pertanto, oltre a costituire un potente framework per lo sviluppo di applicazioni in ambiente Windows, le MFC rappresentano oggi le API stesse del sistema, e cioè il modo privilegiato di interfacciarsi con esso attraverso un'architettura ad oggetti. Applicazioni prive di form ► ►►►►►►►►►►►►►► Cara redazione, sono abbonato a ioProgrammo ormai da tre anni, e penso che questo segno di stima mi possa esimere dal formularvi i complimenti di rito. Arrivo subito al dunque: vorrei realizzare un'appli- cazione in Visual Basic, che non presenti alcuna form. Vorrei cioè che l'applicazione avesse le sembianze di un semplice message box... so che può sembrare una richiesta un po' strana, ma avrei bisogno di una risposta in tal senso. Vi ringrazio e vi auguro buon lavoro. Stefano Per ottenere il risultato che desideri, apriamo un nuovo progetto Visual Basic e andiamo ad eliminare tutte le form incluse nel progetto (essendo il progetto nuovo, si tratterà di eliminare semplicemente formi). Dal menu Progetto, selezioniamo la voce Proprietà e, dal combobox "Oggetto di avvio", selezioniamo "Sub Main". A questo punto, aggiungiamo un nuovo modulo standard al progetto e, all'interno di questo, creiamo una procedura chiamata "Main" attraverso un codice simile a quello che puoi trovare di seguito Private Sub MainQ MsgBox("Ciao!") End Sub Ovviamente, al posto del Message Box, puoi aggiungere qualsiasi codice, ed il tutto sarà eseguito senza che venga visualizzata alcuna form. Per contattarci: e-mail: iopinbox@edmaster.it Posta: Edizioni Master, Via Cesare Correnti, 1 - 20123 Milano http://www.itportal.it 3 ►►► 113 I L Sito de Gamasutra The Art & Science of Making Games http://www.gamasutra.com/ Chiacchierando con amici, colleghi e cono- scenti interessati al mondo della pro- grammazione, ho avuto modo di verifica- re come le mie radici di informatico affondino in un terreno fertile e condiviso. Correva la seconda metà degli anni '80, ed io ero ancora un bambino sotto i dieci anni. Già qualche volta, entrando di soppiatto nelle proibitissime sale giochi, ero rima- sto affascinato da quegli strani "scatoloni con la TV" che monopolizzavano l'attenzione di molti ragazzi un po' più grandi di me. Ricordo ancora, con grande nostalgia, i diabolici piani che orga- nizzavo insieme agli amici d'infanzia per andare a giocare liberamente senza essere scoperti dai nostri genitori. Evidentemente, però, non siamo stati molto abili, giacché in famiglia qualcuno intese immediatamente la mia passione per i gio- chi elettronici. Per la prima comunione, infatti, ricevetti in regalo uno storico Atari 2600, che non avevo richiesto e di cui nemmeno sospettavo l'esi- stenza. Quel regalo, visto col senno di poi, mi ha cambiato la vita. Di lì in poi, la mia passione per i videogiochi è cresciuta in maniera esponenziale. Prima il Commodore 64, poi numerose console quali il Master System, il Mega Drive ed il Game Gear (sì, ero un partigiano della fazione Sega, ai tempi della lotta contro Nintendo), quindi l'Amiga ed infine il PC Intel. Già ai tempi del Commodore 64 mi domandavo quali fossero gli "ingredienti" di un videogioco. Chi crea i video- giochi? Come è possibile costruire qualcosa di simile? Nessuno dei miei compagni di giochi sapeva dare risposta a queste domande. Così co- me le popolazioni primitive, nella loro ignoranza, attribuirono il fulmine al potere degli dei, per noi il "creatore di videogiochi" era una figura mitolo- gica ed inarrivabile. Dire "da grande voglio fare il creatore di videogiochi" era proprio come voler divenire astronauta, calciatore, pilota di Formula 1 o Presidente della Repubblica. Passavamo le sera- te estive a sognare il nostro videogioco ideale, come se di lì a poco l'avremmo realizzato. A dieci anni circa, sapevo qualcosa di programmazione applicata al Commodore 64, ma nulla di esatta- mente cosciente. Che un videogioco potesse esse- re realizzato in casa, lo scoprii poco tempo dopo. Il merito lo devo dare proprio ad una rivista e alla sua rubrica "Talent Scout", che recensiva le attra- zioni videoludiche sviluppate dai lettori. Scoperta la possibilità ed esplorati i mezzi, non so quantifi- care il tempo speso sulla tastiera, a digitare istru- zioni Basic e Pascal. Non credo di aver mai abban- donato il sogno di sviluppare videogiochi, benché la passione per la programmazione videoludica sia poi mutata lentamente in amore per l'informa- tica, in termini più generali. Chissà quanti di quei bambini sognatori, oggi, sono divenuti informati- pochi motti così ispirati. Progettare e realizzare videogiochi è qualcosa a metà strada tra l'arte e la scienza. Gamasutra è una sterminata raccolta di articoli che coprono tutte le fasi dello sviluppo di un videogioco moderno, dal design alla program- mazione, dalla grafica all'audio, fino a giungere alle questioni legali sollevate dalla pubblicazione G3FHà|\J6tWOrk GaineuBYelDpefBConleience (3 sin e Developer ilagazh T li e Art & Science o f AJ ù k i a e G ù m e s Ine Gamasutra. corri Gamasutra F e b b http: //www. itportal.it