Utilizzare periferiche di acquisizione da codice Visual Basic. Dalla versione 6 alla 2005
a cura di Alessandro Del Sole (requisiti: utilizzo delle API di Windows in VB 6, conoscenza intermedia di .NET Framework)Premessa
Questo articolo si propone di illustrare come interfacciarsi a periferiche di acquisizione (quali scanner, webcam ecc.) da codice Visual Basic, attraverso l'utilizzo di funzioni base API contenute nella libreria EZTW32.DLL, nonché come raggiungere questo stesso obiettivo con versioni profondamente diverse dell'ambiente di sviluppo, ossia la versione 6 e la 2005 di Visual Basic.In questa sede creeremo una libreria wrapper, che espone cioè dei metodi basati sulle funzioni API esposte dalla libreria che 'avvolge': EZTW32.DLL, specifica per utilizzare le periferiche di acquisizione.
Andremo così ad evidenziare differenze non solo di codice, ma anche strutturali dell'applicazione, in considerazione del modo di ragionare che passa da quello dello sviluppatore per un ambiente basato su COM a quello dello sviluppatore per .NET Framework.
Nel corso di questa comparazione avremo modo di osservare alcuni utili passaggi che ci serviranno per rendere la nostra libreria, anzi il nostro assembly, compatibile con le specifiche di .NET, meglio note col nome di Common Language Specifications (che da ora in avanti abbrevieremo con CLS), sia per quanto riguarda le convenzioni circa la stesura del codice, sia per quanto riguarda elementi inerenti la compilazione in formato eseguibile.
Ma non ci limiteremo solo a questo. Vedremo infatti come utilizzare uno strumento di analisi degli assembly per verificare la validità del nostro lavoro e daremo anche un'occhiata ai commenti XML, novità di Visual Basic 2005.
Per proseguire nella lettura dell'articolo è necessario scaricare dall'area download del sito l'archivio compresso che contiene i due progetti utilizzati, in versione VB 6 e in versione VB 2005. Ed ovviamente, qualora non lo abbiate già fatto, dovete installare il .NET Framework 2.0 sulla vostra macchina.
Potete invece scaricare la libreria EZTW32.DLL sul sito www.dosadi.com. Su questo sito sono disponibili diverse versioni della libreria, più potenti ma a pagamento. Quella segnalata è invece la versione gratuita. L'ultima versione aggiornata risale al 1999, ma è più che sufficiente per il nostro scopo. Se avete già installato periferiche di acquisizione difficilmente avrete necessità di scaricarla.
Partiremo dalla creazione di un progetto VB 6 per poi crearne da zero uno con le stesse funzionalità in VB 2005.
Le parole sottolineate non sono un caso. Utilizzare l'Upgrade Wizard di Visual Basic 2005 non produce lo stesso risultato, anche se apparentemente l'applicazione fa le stesse cose. Il codice creato dal Wizard, infatti, spesso si limita ad una mera traduzione del vecchio codice (peraltro a volte con scarso successo), ma non lo ottimizza correttamente per il .NET Framework. Alcuni accorgimenti vanno infatti aggiunti dal programmatore in base al tipo di applicazione di cui si necessita.
Nell'archivio che avete scaricato ho anche incluso una versione del progetto "migrata" tramite l'Upgrade Wizard. Al termine della lettura di questo articolo, date un'occhiata a questa e a quella che abbiamo creato daccapo. Le differenze risulteranno evidenti.La spiegazione circa le funzionalità implementate dalle classi in relazione all'acquisizione di immagini è dettagliatamente descritta nella parte dedicata al progetto in VB 6.
Nella parte dedicata a VB 2005 le spiegazioni saranno maggiormente orientate al modo di ragionare basato su .NET Framework.Fatta questa premessa, forse noiosa ma necessaria, possiamo iniziare!
Utilizzare periferiche di acquisizione da codice Visual Basic 6
Il primo passo da compiere è, ovviamente, creare un nuovo progetto. Poiché è nostra intenzione quella di creare una libreria dei tipi che funga da wrapper, il tipo di progetto da creare è una DLL/ActiveX. Sarà sufficiente che il progetto includa un unico modulo di classe, al quale ho assegnato il nome EzTwain.
Se non si volesse creare una libreria esterna, ma utilizzare il codice seguente includendolo in un progetto, si potrà creare un modulo anziché una classe ed aggiungere il codice medesimo impostando la visibilità delle dichiarazioni di costanti e funzioni da Private a Public.Fatto questo, passiamo a dichiarare le funzioni API e le relative costanti che utilizzeremo. Desidero sottolineare che le funzioni descritte in questo articolo sono solo alcune tra quelle contenute dalla EZTW32.DLL, ma sono sufficienti per creare una buona libreria per l'acquisizione.
Nel listato 1 sono elencate le dichiarazioni che noi utilizzeremo.
Nei commenti alle varie dichiarazioni sono specificati i valori che vengono restituiti in caso di successo o in caso di errore. Poiché i metodi che implementeremo restituiranno i medesimi valori restituiti dalle API, questi non saranno ripetuti ancora nel codice e nel corso dell'articolo.Visto che a noi le cose semplici non piacciono, rendiamo più completo (e complesso) il nostro progetto con una particolarità: la nostra libreria esporrà un particolare metodo che, invece di salvare l'immagine in un file specificato dall'utente, permetterà di salvarla in un file temporaneo dal nome univoco, restituendone il pathname completo al metodo chiamante. Per ottenere la stessa funzionalità in VB 2005 sarà sufficiente una sola riga di codice, come vedremo più avanti. In VB 6 sono invece necessarie alcune funzioni API che dichiariamo ora e che troveranno implementazione più avanti. Ecco le dichiarazioni:
'==========API per ottenere un nome di file temporaneo univoco ============== Private Declare Function GetTempPathA Lib "kernel32" _ (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long Private Declare Function GetTempFileNameA Lib "kernel32" _ (ByVal lpszPath As String, ByVal lpPrefixString As String, _ ByVal wUnique As Long, ByVal lpTempFileName As String) _ As Long Private Const UNIQUE_NAME = &H0Poiché implementeremo anche un metodo che ci consentirà di verificare la presenza su disco della libreria EZTW32.DLL, dichiariamo l'API GetSystemDirectory per ottenere il percorso completo della cartella di sistema di Windows:
'===============API per ottenere la directory di sistema==================== Private Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" _ (ByVal lpBuffer As String, ByVal nSize As Long) As Long Private Const MAX_PATH = 260Ora, nel listato 2, si passa alla parte relativamente più complessa, ossia la creazione delle funzioni che saranno poi esportate.
Per comodità di esposizione, e per non rendere troppo complicata la lettura dell'articolo, ho inserito dei commenti a lato di ogni funzione in modo da spiegare cosa fa ognuna di esse.A questo punto possiamo soffermarci su alcune parti del codice per delle importanti precisazioni. Se abbiamo installato un'unica periferica di acquisizione, può risultare conveniente utilizzare la funzione AcquireNative che sfrutta l'API TWAIN_AcquireNative. Native sta infatti ad indicare la periferica di default.
Entrambe le API TWAIN_AcquireNative e TWAIN_AcquireToClipboard disegnano una bitmap indipendente dal dispositivo (DIB) e la trattengono in memoria. Al termine delle operazioni, che nel nostro caso sono di scrittura su disco, ma potrebbero essere ad esempio di "passaggio" dell'immagine ad un'applicazione, è necessario rilasciare le risorse attraverso l'API TWAIN_FreeNative, onde evitare un inutile e rischioso spreco di memoria.
Per converso, il metodo AcquireToFilename produce lo stesso risultato, ma rilascia le risorse automaticamente.
In teoria con quest'unica API si potrebbe costruire una libreria per l'acquisizione di immagini.
Ci si chiederà allora il perché della presenza di due metodi che fanno la stessa cosa. Dalle ricerche che ho effettuato su Internet sembrerebbe che, almeno secondo un punto di vista didattico, la TWAIN_AcquireNative andrebbe preferita quando risulta installata un'unica periferica di acquisizione.I metodi AcquireNative ed AcquireToClipboard richiedono di specificare anche la qualità in bit dell'immagine.
Avremmo potuto lasciare all'utente la possibilità di passare un qualsiasi valore di tipo long, ma ci sarebbe stato un elevato rischio di errore, nel caso non si conoscessero i valori accettati dalle API, oppure avremmo potuto impostare per default un valore generico, ma non sarebbe stato professionale!
E' per questo motivo che abbiamo creato l'enumerazione PixelTypes. In questo modo abbiamo ristretto la scelta su un ambito di valori stabiliti da costanti che vengano accettati dalle funzioni richiamate. E' sicuramente più opportuno specificare come parametro un tipo PixelTypes, piuttosto che un generico long, che avrebbe messo l'utente nella posizione di non sapere con esattezza quale valore utilizzare.Come avrete notato, abbiamo realizzato due metodi che sfruttano la TWAIN_AcquireNative.
Il primo crea l'immagine su disco in un file di cui l'utente specifica il pathname e che viene salvata in formato bitmap (metodo AcquireNativeToFile).
Il secondo (metodo AcquireNative) crea l'immagine su disco in un file temporaneo, dal nome univoco. Il metodo restituisce alla funzione chiamante il pathname completo in modo che l'utente possa riutilizzarlo per i propri scopi, oppure restituisce una stringa vuota nel caso in cui siano stati rilevati degli errori.
Per far questo abbiamo creato delle funzioni ad accessibilità Private che sfruttano le API GetTempPath per ottenere la cartella speciale di Windows (vedi altro mio articolo) relativa ai files temporanei e GetTempFilenameA per ottenere un nome di file univoco, ossia che non vada in conflitto con nomi di files già esistenti. Ho reperito la snippet relativa alla creazione di files temporanei univoci sul sito www.freevbcode.com.A proposito di errori, avrete anche notato che non è presente una routine di gestione del tipo On..Error..GoTo...
Ho tralasciato di inserirla principalmente perché le funzioni API, in caso di errore, restituiscono dei valori numerici e non sollevano errori di run-time, che invece necessitano di una gestione ulteriore. Nulla vieta, però, di implementare un'apposita routine per migliorare il progetto.Possiamo finalmente compilare il progetto ed otterremo una DLL ActiveX perfettamente funzionante, che potrà essere usata nei nostri progetti mediante l'aggiunta di un riferimento.
Come potrete osservare anche nel progetto di esempio, per acquisire un'immagine è sufficiente richiamare dapprima il metodo SelectTwainSource per impostare la periferica da utilizzare.
Poi si passa all'acquisizione con il metodo AcquireToFilename.
Un semplice esempio di utilizzo potrebbe essere il seguente:Sub Scan() Dim S As New EzTwain S.SelectTwainSource() S.AcquireToFilename(Me.hWnd, "C:\MyImage.Bmp") S = Nothing End SubOgni chiamata ai metodi implementati, o alle API utilizzate, farà apparire la finestra con le opzioni di acquisizione prevista dal driver della periferica installata.
Per maggiori e più approfondite informazioni sulle funzioni implementate vi consiglio di visitare il sito PowerBasic Support Forum in lingua inglese (vedi anche alla fine dell'articolo).
Utilizzare periferiche di acquisizione da codice Visual Basic 2005
Passiamo ora ad esaminare quella che secondo me è la parte più affascinante e cioè la creazione del progetto in Visual Basic 2005.Il tipo di progetto che dobbiamo creare è una libreria di classi, in modo da ottenere una Dll compilata. Diamo al progetto il nome desiderato e tuffiamoci a capofitto. Io ho utilizzato il nome EZTwainLibrary, per uniformità col progetto in VB 6.
Anche in questo caso è sufficiente un solo file di codice per implementare quanto ci serve. Ma è necessario fare alcune precisazioni prima di passare al codice vero e proprio.
Tutto quello che segue si propone di aderire il più possibile alle specifiche delle CLS, quindi alcune cose che potranno sembrarvi fuori logica hanno un loro perché e tutto verrà spiegato nel corso dell'articolo.
Secondo le CLS, le dichiarazioni di funzioni API, che nel gergo .NET sono chiamate platform invokes ed abbreviate in pinvokes, debbono essere contenute tutte in una classe separata. Tale classe deve avere accessibilità Friend, in modo da avere visibilità all'interno del progetto, ma non all'esterno. Ad essa deve essere attribuito uno dei nomi convenzionali previsti per questo scopo, che sono: NativeMethods, SafeNativeMethods, UnsafeNativeMethods.
Per quanto sopra avremo necessità di creare due classi nello stesso file. Per cui, come prima mossa, dichiareremo uno spazio dei nomi (namespace) che chiameremo EZTwainLibrary, al cui interno sarà contenuta una classe chiamata EZTwain, che è la classe che implementa i metodi veri e propri per l'utilizzo delle periferiche di acquisizione e che sarà dichiarata come pubblica; una seconda classe che sarà chiamata per convenzione NativeMethods e che contiene le dichiarazioni delle funzioni API da utilizzare.Iniziamo la stesura del codice riga per riga, soffermandoci per ogni singolo aspetto da approfondire.
Le prime righe di codice da digitare sono le seguenti direttive:Imports System 'necessario per abbreviare il codice relativo 'alle successive chiamate all'attributoImports System.Runtime.InteropServices Alcuni aspetti delle Common Language Specifications
Il prossimo passo è quello di marcare l'assembly come compatibile con le specifiche di .NET.
Come già accennato nell'introduzione, obiettivo di questo articolo è anche quello di rendere compatibile l'assembly con le specifiche di .NET, quindi sin dall'inizio dobbiamo utilizzare delle direttive valide in tal senso.
Vediamo subito alcuni aspetti di quanto appena affermato.
- Secondo le CLS un assembly dovrebbe essere marcato come CLS-Compliant, cioè compatibile con le specifiche di .NET. Questo assicura una piena compatibilità con la piattaforma ed avviene mediante l'utilizzo congiunto di due attributi: Assembly e CLSCompliant.
- Il nome della classe EZTwain: ovviamente il nome è a scelta, ma non è un caso che maiuscole e minuscole siano state scritte in questo modo. Infatti, secondo le CLS, i nomi delle classi sono pascal-cased; se composti da più parole devono indicare in maiuscolo la prima lettera di ogni parola, in questo caso EZ e Twain. Tuttavia esiste un'eccezione: quando la prima parola è composta da due lettere, come in questo caso, entrambe devono essere in maiuscolo.
- Il perché della classe NativeMethods: come già visto, secondo le specifiche di .NET, tutte le dichiarazioni di funzioni API debbono essere contenute in un'unica classe separata che abbia uno dei nomi convenzionali sopra elencati.
Proseguiamo il nostro codice esaudendo il primo punto, cioè quello di marcare l'assembly come CLS-Compliant.
Il codice da inserire, immediatamente dopo le direttive Imports, è il seguente:'Firma l'assembly come aderente alle specifiche del Common Language Runtime <Assembly:CLSCompliant(TRUE)>Dichiariamo lo spazio dei nomi necessario a contenere le due classi:
Namespace EZTwainLibrary End NamespaceAnche per il nome del namespace vale il discorso sulla notazione fatto in precedenza per il nome della classe.
Ci occupiamo ora di creare la classe NativeMethods che dichiara le funzioni API contenute nella libreria EZTW32.DLL che ci occorrono (vedi listato 3). Vi rimando ad un mio vecchio articolo su come si dichiara un'API in Visual Basic .NET.La classe è stata dichiarata Friend in quanto deve avere visibilità esclusivamente all'interno del progetto e non anche all'esterno (a questo penserà la classe successiva), e così anche le funzioni in essa dichiarate.
Le funzioni vengono dichiarate Shared in quanto la classe che andrà ad utilizzarle non si presta, per la sua natura e per l'utilizzo massiccio di risorse di sistema, ad istanze multiple ed utilizza metodi che pertanto vengono detti "statici".
Una classe che implementa esclusivamente metodi Shared deve contenere un costruttore vuoto e Private, come imposto dalle CLS.Esaminiamo ora (listato 4) i punti salienti della classe EZTwain, che svolge il lavoro vero e proprio:
- La classe è stata marcata come compatibile con le CLS mediante l'attributo CLSCompliant ed è stata dichiarata pubblica in quanto esporrà i metodi quando compileremo il tutto;
- Essa è stata inoltre marcata come NotInheritable. Questo perché le CLS prevedono che una classe che implementi esclusivamente metodi statici Shared, come in questo caso, non dovrebbe essere ereditata.
L'equivalente di NotInheritable in C# è la parola chiave Sealed, che è utile conoscere in quanto strumenti di analisi, come ad esempio FxCop di cui parlerò più avanti, spesso non sollevano errori utilizzando la keyword di Visual Basic, bensì la Sealed di C#.- I nomi dei metodi e delle proprietà sono pascal-cased; ciò significa che, in un nome composto da più parole, va indicato in maiuscolo la prima lettera di ogni parola, mentre le altre sono minuscole, come in AcquireToClipboard;
- I parametri delle funzioni sono camel-cased; in pratica, in nomi composti da più parole (come ad es. outputFileName) il primo carattere della prima parola deve essere minuscolo ed in maiuscolo va indicata la prima lettera di ogni parola successiva, le rimanenti in minuscolo;
- Ci sono identificatori a cui il compilatore riserva un'attenzione particolare, come ad esempio Filename.
Se un identificatore di questo tipo deve essere usato nel codice, esso deve essere specificato in modo da non essere confuso con identificatori omonimi utilizzati normalmente dalle classi di base. Più semplicemente è corretto utilizzare FileName piuttosto che Filename;- Anche in questo caso la classe implementa un costruttore vuoto e Private dal momento che espone tutti metodi statici;
- Poiché il compilatore è in grado di capire che determinate funzioni svolgono un lavoro di Get/Set è consigliabile usare le proprietà invece dei metodi quando si tratta di Get a meno che questo non comporti un dispendio inutile di risorse o l'implementazione risulti particolarmente complessa;
- In VB 2005 si preferisce far restituire i valori a mezzo della parola riservata Return, piuttosto che assegnare il valore da restituire alla funzione sebbene questo sia comunque corretto;
- Si noti come il tipo long del progetto in VB 6 sia stato convertito in integer in VB 2005;
- Nel progetto in VB 6 abbiamo utilizzato l'API GetSystemDirectory per ottenere il percorso completo della directory di sistema ed abbiamo dovuto scrivere alcune righe di codice per implementarla.
In VB 2005 abbiamo semplicemente digitato Environment.SystemDirectory.
L'oggetto Environment ci permette di conoscere alcune caratteristiche del nostro sistema, tra cui diverse cartelle speciali di Windows, inclusa quella di sistema;- Le CLS suggeriscono di evitare gli spazi dei nomi con meno di cinque elementi.
Nel nostro caso, però, non potevamo farne a meno. Uno strumento di analisi solleverebbe un warning, ma non dimentichiamo le finalità didattiche del nostro progetto!Un semplice esempio di utilizzo potrebbe essere il seguente:
Imports EZTwainLibrary Sub Scan() 'Richiamando metodi shared non serve una nuova istanza della classe EZTwain.SelectTwainSource() EZTwain.AcquireToFilename(Me.Handle, "C:\MyImage.Bmp") End SubNell'archivio contenente il codice sorgente è presente un progetto di esempio che illustra alcune delle possibilità di utilizzo della nostra libreria.
Metodi e proprietà in stile .NET
Vorrei ora approfondire un paio di metodi che abbiamo esposto nella classe EZTwain. Il metodo IsTwainInstalled e il metodo AcquireToNative.Il metodo IsTwainInstalled, che determina la presenza della libreria EZTW32.DLL nella cartella di sistema seppur in modo piuttosto rudimentale, utilizza una funzione fornita dal nuovo spazio dei nomi My. Senza dilungarmi troppo su questo spazio dei nomi rivoluzionario, mi limito ad accennare che questo implementa ulteriori classi che forniscono accesso diretto a molti importanti contesti, quali l'utente attuale, il file-system, le connessioni di rete, ai quali era praticamente impensabile accedere da VB 6 se non attraverso un massiccio uso di API e conseguentemente centinaia di righe di codice. Adesso ne bastano davvero poche.
Il metodo AcquireToNative è dichiarato con Overloads. Questo operatore permette di definire più istanze dello stesso metodo, con parametri ed implementazioni diverse.
Nel nostro esempio il primo overload acquisisce un'immagine da scanner, la salva su disco con il nome fornito dall'utente e permette di specificare il riferimento della finestra chiamante; infine restituisce un valore integer che è uguale a quello restituito dalla correlata funzione API.
Il secondo overload acquisisce un'immagine da scanner e la salva in un nuovo file temporaneo, con riferimento alla finestra attiva. Restituisce un valore di tipo stringa che contiene il percorso completo del file temporaneo in cui risiede l'immagine creata, cosicché l'utente possa poi gestirla come meglio crede. Per ottenere questo, nel progetto VB 6 avevamo dovuto creare due funzioni con nomi diversi.La proprietà Version, che avevamo invece definito come metodo nel progetto VB 6, utilizza un parametro System.Globalization.CultureInfo.InvariantCulture nella conversione in stringa del valore che restituisce la versione della libreria.
Le CLS stabiliscono che quando si converte un valore numerico, come un integer o un double in una stringa, mediante il metodo .ToString, è opportuno specificare una formattazione della stringa valida a seconda della lingua utilizzata dall'utente, visto il particolare tipo di valore restituito.
Questo avviene mediante l'utilizzo dell'interfaccia IFormatProvider, che implementa dei metodi per la formattazione delle stringhe.
Nel progetto di esempio ho voluto rendere il valore restituito indipendente dalla lingua di sistema, pertanto ho inserito il valore neutro System.Globalization.CultureInfo.InvariantCulture.Infine una precisazione sui metodi Shared. Per emulare i metodi Shared in VB 6 sarebbe necessario dichiarare come pubbliche le funzioni API in un modulo anziché in una classe. Questo però, in VB 6, impedirebbe la creazione di una libreria esterna, mentre, come già anticipato, il modulo potrebbe essere parte integrante di un progetto. Questione di comodità.
Commenti XML
Visual Basic 2005, a differenza dei predecessori, permette di inserire all'interno del codice anche i commenti in formato XML.
In Visual Basic .NET 2003 questo era possibile solo attraverso l'installazione di un add-in chiamato VB-Commenter, mentre tale caratteristica è sempre stata presente in Visual C#. Finalmente in Visual Basic 2005 tale possibilità è stata implementata direttamente nell'IDE.La documentazione dei sorgenti mediante commenti in formato XML può rivelarsi molto utile se si sviluppano componenti piuttosto che applicazioni.
Esistono infatti dei programmi, come ad esempio NDoc, in grado di creare dei files della guida compilati a partire dai commenti XML inseriti nel codice, creando così degli Help molto simili a quello di Visual Studio, in cui per ogni libreria è descritta ogni classe con i suoi metodi, proprietà e quant'altro.Per inserire dei commenti XML è sufficiente posizionare il cursore sul rigo precedente a quello del codice che ci interessa e digitare tre apostrofi. Verrà automaticamente inserito un blocco XML in cui è possibile inserire commenti circa le funzionalità implementate dal codice in esame, come nella figura seguente tratta dal progetto a corredo:
![]()
Un assembly sicuro
Per completare il percorso di creazione dell'assembly è necessario renderlo sicuro. Questo non significa inattaccabile o esente da reverse engeneering, bensì dotato di una vera e propria firma, cosiddetta strong name, che lo renda definitivamente compatibile con le CLS.Immagino che qualcuno di voi sappia cos'è la Global Assembly Cache (GAC). E' una cartella creata dal .NET Framework in cui risiedono tutti gli assembly registrati, a seconda della versione di .NET utilizzata. Un assembly che risiede nella GAC non dev'essere ogni volta ricopiato nella cartella ove è situato l'eseguibile dell'applicazione che ad esso fa riferimento, come invece avviene per tutti gli altri assembly, in quanto il Common Language Runtime sa dove reperire questi componenti. Se più applicazioni utilizzano lo stesso assembly, può essere conveniente installarne una sola copia nella GAC ed averlo così sempre disponibile.
Qualora volessimo anche noi installare la nostra creatura nella GAC, dovremo obbligatoriamente dotarla di questa sorta di firma digitale.Una importante novità di Visual Basic 2005, e più in generale di Visual Studio 2005, è quella di poter compiere questa operazione direttamente dall'IDE. Con le precedenti versioni di Visual Studio .NET questo andava invece fatto inserendo alcune righe di codice nel file AssemblyInfo.Vb ed utilizzando gli strumenti a riga di comando sull'eseguibile.
Nella finestra delle proprietà del progetto selezioniamo la scheda "Signing". Applicando un flag sulla casella "Sign the assembly" potremo creare un nuovo strong name (o sceglierne uno esistente) con tanto di password. Penserà a tutto l'IDE che poi inserirà il nuovo file anche nella finestra Esplora Soluzioni.
Il codice sorgente a corredo di questo articolo è già dotato di un file chiamato EZTwain.Pfx, la cui password è vbt&t. Non avrete pertanto necessità di aggiungerne uno nuovo, ma è bene sapere come farlo.Compiliamo!
Dopo tanto peregrinare siamo giunti all'atteso momento. Siamo pronti per compilare la nostra soluzione. Il nostro assembly viene così creato ed è pronto per essere utilizzato nelle nostre applicazioni ed eventualmente anche per essere installato nella GAC.Analisi dell'assembly
Nella versione 2.0 del Framework SDK è compreso un utilissimo strumento chiamato Microsoft (r) FxCop. Esso fornisce strumenti di analisi per verificare la rispondenza degli assembly alle specifiche delle Common Language Specifications. Questa applicazione non viene automaticamente installata, ma nel menu Avvio è presente un collegamento al setup.Proviamo ad analizzare l'assembly appena creato, con il comando Add targets del menu Project. Fatto questo, clicchiamo su Analyze. FxCop riporterà l'esistenza di alcune incongruenze con le CLS. Questo è normale, in quanto i nomi dei parametri dei metodi sono composti da una sola parola. In effetti FxCop dice anche che se il programmatore sa di aver seguito correttamente le specifiche, il messaggio può essere ignorato. Di conseguenza possiamo proseguire con l'analisi.
Viene anche riferito che ci sono delle variabili locali inutilizzate e che sarebbe opportuno rimuoverle. A ben vedere in alcuni casi esso si riferisce a variabili create in automatico e contenute in files che l'IDE mantiene volutamente invisibili onde evitare manomissioni dell'utente. Negli altri casi possiamo soprassedere, in quanto si tratta di situazioni particolari dovute alla dichiarazione delle API.
Fatte queste considerazioni possiamo ritenere soddisfacente il lavoro che abbiamo svolto. Se avete voglia di vedere come cambia l'analisi dell'assembly, provate a togliere gli attributi CLSCompliant oppure la parola chiave NotInheritable nella dichiarazione della classe, provate a cambiare le proprietà in metodi od ancora provate ad inserire tutte le dichiarazioni API nella classe EZTwain eliminando la classe NativeMethods. Ripetete l'analisi dopo aver fatto qualche modifica di queste e noterete una sfilza di errori in più.
Gli errori evidenziati in rosso sono quelli da correggere immediatamente; quelli blu, a seconda dell'intensità, sono relativamente trascurabili. Per ottenere e visualizzare dettagli è sufficiente cliccare sull'errore, se poi si è connessi ad internet si potranno utilizzare i collegamenti ipertestuali che rimandano al sito web dell'applicazione. Microsoft (r) FxCop ha un proprio workspace sul sito GotDotNet, e può essere scaricato da solo, anche esternamente al Framework SDK.
Documentazione
Vi elenco alcuni dei siti dai quali ho reperito ulteriori informazioni sulla EZTW32.DLL:
- Controlling TWAIN devices from within Visual FoxPro ©;
- PowerBasic Support Forum (contiene un modulo.bas molto completo, da cui ho attinto parti di codice per la realizzazione del progetto. Il modulo è corredato da commenti molto approfonditi. Consiglio vivamente di dargli un occhiata!).
Disclaimer :o)
La maggior parte del codice e dei relativi commenti sono stati ripresi e tradotti dall'inglese. Mi scuso per eventuali (seppur improbabili) malfunzionamenti e vi invito a segnalarmi se qualcosa non funziona, affinché io possa tempestivamente apportare modifiche, se necessario.Conclusioni
In questo articolo abbiamo imparato alcune importanti nozioni su .NET Framework, sulle Common Language Specifications e sulla portabilità del vecchio codice costruendo daccapo un nuovo progetto.
Se però avete la curiosità di capire meglio il perché di tanto lavoro e di tante precisazioni, provate a guardare il progetto convertito con l'Upgrade Wizard di Visual Basic 2005 e noterete la sostanziale differenza, soprattutto se poi lo analizzate con FxCop.Credo inoltre che alcune snippets utilizzate nei progetti possano tornarvi utili in altre situazioni, indipendenti dall'utilizzo che ne abbiamo fatto in questo articolo.
Per ulteriori informazioni potete contattarmi al mio indirizzo visitare il mio blog.