News
Articoli
Download
Tips & Tricks
Link
Guestbook
 
 
 
 
Copyright© 2004
Ivanov Dmitrij
Evghegnevich
e-mail

Risoluzione consigliata
1024 x 768
 
Ti piace il sito?
Votalo con un clic sul
banner qui sotto ^_^
 
   
 
 
 
 
 
Gestione degli errori
 
In questo articolo affronteremo insieme uno dei problemi che mette in crisi i progettisti dei sistemi informatici e di conseguenza anche coloro che li realizzano, i veri ossi duri, cioè programmatori, coloro che fanno della programmazione uno stile di vita e del caffé la loro bevanda preferita. Per farla breve, parleremo della famigerata gestione degli errori ^_^.

Quante volte vi sarà capitato di preparare un pacchetto di installazione e di presentarvi con un sorriso smaliante a casa di un amico o dal cliente ed installare la delicatamente preziosa creatura nel territorio ostile, sul PC nemico? Molte volte vero? Durante l'installazione nulla di strano, ma poi, con la prima esecuzione il sorriso si trasforma in un'inevitabile smorfia di disappunto, ma porc... Run-time error? Che significa, ho fatto tutti i controlli, non manca nulla, ma PERCHE'? Vi è capitato qualche volta anche questo, vero? Ebbene sì, siamo prima di tutto uomini, le ragazze non me ne vogliano, e qualche cosa ci sfugge sempre, a chi più e a chi meno, ma basta anche un solo errore per fare pessima figura. Come si fa ad evitare gli errori? Direi che è praticamente impossibile, ma si può minimizzare il danno gestendo le situazioni impreviste.

Cominciamo subito con un bel esempietto. Facciamo come i ragazzini alle elementari: dividiamo le mele che stanno nel nostro cesto, tra un gruppo di amici, dando ad ognuno una quantità pari di cibo proibito. Possiamo esplicare questo calcolo con una semplice espressione algebrica:

MelePerCapoccia=TotaleMeleNelCesto / NumeroDiCapocce


Dividendo mele per capocce abbiamo risolto il dilemma di tutti i ragazzini che frequentano i primi anni delle elementari, ma ci rimane il nostro. Questa istruzione produrrà un errore? I più smaliziati diranno: "Ma non ci prendere in giro, è facile, si vede subito...". Per chi non vedesse l'inghippo, diciamo che si potrebbe presentare uno degli errori più comuni: divisione per zero. Infatti, se il NumeroDiCapocce è nullo ci sarà un bell'errore "division by zero" e non vedremo mai il corretto risultato del calcolo. Possiamo certamente prevedere questo tipo di situazione e porre un'istruzione condizionale if che devii opportunamente il flusso del programma. Parlando il linguaggio Visual Basic avremo:
 
If NumeroCapocce > 0 Then
	MelePerCapoccia = TotaleMeleNelCesto / NumeroDiCapocce
Else
	MelePerCapoccia = 0
End If
 
Certamente l'accorgimento risolve questo semplice problema, ma proviamo a pensare ad un problema più complesso, la cui soluzione si traduce in alcune centinaia di righe di codice. Siamo sicuri che ci sarà facile prevedere tutte le anomalie che possono presentarsi? La risposta è - "Non ci è dato questo dono!". Chi, sano di mente, si metterebbe a controllare la quantità di memoria disponibile prima di eseguire un'istruzione banale come quella del calcolo delle mele? Nessuno a mio avviso, o meglio, qualcuno forse ci ha fatto il pensiero e a questo punto sicuramente non sta più risolvendo il problema originale, ma se ne crea di altri ben peggiori. Eppure anche l'errore di memoria esaurita è un bel cruccio. ma per ora lo sorvoliamo.

Il discorso fatto serve per introdurre una scorciatoia che i progettisti di VB hanno voluto fornire ai programmatori e si chiama gestione degli errori. La gestione degli errori avviene tramite, manco a dirlo, dei gestori degli errori, che per semplicità di scrittura saranno abbreviati con GdE. Un GdE è un semplice ed utilissimo meccanismo che cattura gli errori come se fosse una trappola. Ogni volta che è attivo un gestore degli errori e si verifica un'eccezione, questa viene prontamente catturata dal gestore, che a questo punto può fare due cose: proseguire con l'istruzione successiva oppure lasciare al programmatore la decisione sul da farsi. Per usare i gestori degli errori è sufficiente conoscere poche istruzioni che elenco di seguito:

On Error Resume Next
On Error GoTo Etichetta
On Error GoTo 0


Analizziamo On Error Resume Next. Questa istruzione attiva un GdE che può essere assimilato come un gestore automatico. Alla presenza di qualsiasi errore questo GdE salta all'istruzione successiva. E' un buon meccanismo di difesa per evitare che il nostro programma vada in crash, ma ha un grosso difetto: non lascia indizzi sull'entità dell'errore, in altre parole non ci fa capire che tipo di errore si è verificato e potrebbe benissimo catturare un'eccezione non facendoci percepire l'anomalia nel codice. Ha un'altro limite non indifferente: è in grado di gestire solo una routine per volta. Vediamo come funziona. Per avere il nostro gestore all'interno di una funzione è sufficiente attivarlo prima delle istruzioni che crediamo possano presentare degli errori imprevisti. Visto che siamo molto meticolosi attiviamo il gestore prima di tutte le istruzioni della nostra funzione, le dichiarazioni preventive le possimo anche escludere.
 
Function CalcolaMelePerCapoccia(NumeroMele as Integer, _ 
	NumeroCapocce As Integer) As Single
						
Dim Risultato As Single

	On Error Resume Next

	Risultato = 0
	Risultato = NumeroMele / NumeroCapocce
	
	'visto che siamo buoni, diamo anche una mela in omaggio
	Risultato = Risultato + 1
	CalcolaMelePerCapoccia = Risultato
	
	'scrivo il risultato nella finestra di debug
	Debug.Print "CalcolaMelePerCapoccia = " & Risultato
End Function
 
Ora, se per un malaugurato motivo ci capitasse il numero di capocce nullo, la nostra funzione non manderà in crash l'intero applicativo, ma si limiterà semplicemente a fornire un risultato apparentemente corretto, ma formalmente errato perché si è verificato un imprevisto. Infatti, il GdE farà rapida cattura della divisione per zero e passerà il controllo all'istruzione successiva. Detto altrimenti, in caso di errore la CalcolaMelePerCapoccia restituisce 1, ma il risultato è la conseguenza di un errore e per tanto da ritenersi errato anch'esso. Abbiamo dunque evidenziato il primo difetto di On Error Resume Next.

Se vogliamo effettuare per N volte consecutive il calcolo delle mele possiamo scrivere un'altra semplice funzione.
 
Sub CalcolaMele(NumeroVolte as Integer)
				
Dim i as Integer
Dim NumeroMele as Integer
Dim NumeroCapocce as Integer
Dim Risultato as Single

	On Error Resume Next

	for i=1 to NumeroVolte
		NumeroMele = Val(InputBox("NumeroMele"))
		NumeroCapocce = Val(InputBox("NumeroCapocce"))
		
		Debug.Print "CalcolaMele-NumeroMele = " & NumeroMele
		Debug.Print "CalcolaMele-NumeroCapocce = " & NumeroCapocce
		
		Risultato = CalcolaMelePerCapoccia(NumeroMele, NumeroCapocce)
		Debug.Print "CalcolaMele-Ciclo " & i+1 &  _
				" MelePerCapoccia = " & Risultato
	next i
End Sub
 
La funzione InputBox ritorna la stringa digitata dall'utente, mentre la funzione Val converte la stringa in un numero, se la stringa non è un numero ritorna zero. Per capire il secondo difetto del GdE automatico disattiviamo con un commento (apice davanti all'istruzione ') il GdE nella funzione CalcolaMelePerCapoccia, ottenendo così la gestione degli errori solo all'interno di CalcolaMele.

Mandata in esecuzione CalcolaMele non abbiamo nessuna garanzia sull'input che l'utente produce. Supponiamo digiti 0 per il Numero di capocce. Il GdE è come uno sceriffo che ha la sua giurisdizione e nel nostro caso abbiamo uno sceriffo che controlla solo le eccezioni all'interno di CalcolaMele e non può fare nulla per la CalcolaMelePerCapoccia. Quindi avremo sicuramente una divisione per zero (NumeroMele / 0) che ci farà storcere il naso davanti al nostro amico, mentre tutto il nostro gran lavoro va in crash. Accidenti! Ecco spiegato anche il secondo difetto di questo GdE. Ovviamente potevamo lasciare attivo anche il GdE in CalcolaMelePerCapoccia, ma il risultato prodotto non sarebbe stato in ogni caso corretto. Sembrava uno sceriffo in gamba, ma in realtà è un po' lavativo. In alcune situazioni particolari può risultare utile, ma è meglio gestire gli errori in modo più diretto.

Per evitare gli inconvenienti del precedente gestore possiamo utilizzare On Error Goto Etichetta. Come prima, anche questa istruzione attiva un GdE, ma in caso di errore il flusso del codice lo decidiamo noi: possimo fare un salto in un punto ben preciso del nostro programma, segnalato da un'apposita Etichetta. In VB un'etichetta è una stringa alfanumerica seguita da due punti (:). Partiamo subito con l'esempio. Ripeschiamo la fatidica CalcolaMelePerCapoccia.

 
Function CalcolaMelePerCapoccia(NumeroMele as Integer, _
	NumeroCapocce As Integer) As Single
	
Dim Risultato As Single

	On Error Goto Errore
	
	Risultato = 0
	Risultato = NumeroMele / NumeroCapocce
	
	'visto che siamo buoni, diamo anche una mela in omaggio
	Risultato = Risultato + 1
	CalcolaMelePerCapoccia = Risultato
	
	'scrivo il risultato nella finestra di debug
	Debug.Print "CalcolaMelePerCapoccia = " & Risultato
	
	'la funzione ternima in questo punto se non si
	'verificano errori
	Exit Function
Errore:
	'il gestore degli errori salta all'etichetta
	'specificata se si verificano errori
	MsgBox "Generato da " & Err.Source & " Codice errore: " & _
		Err.Number & " Descrizione: " & Err.Description & _
		" CalcolaMele non ha funzionato correttamente!", _ 
		"ERRORE!", vbCritical
End Function
 
Il GdE si attiva esattamente nello stesso modo di quello precedente, ma è necessario specificare il nome di un'etichetta. Posizioniamo la nostra etichetta in fondo alla funzione, così potremo aggiungere e togliere codice senza dover modificare nulla dei calcoli sulle mele. Già ci stiamo facendo due meloni, manca solo di ingarbugliare i calcoli delle mele.

Quando si verifica un errore il caro GdE salta all'etichetta specificata e da qui abbiamo il pieno controllo del codice. Vi pare poco?

Lo scopo primo per un programmatore od un utente in caso di malfunzionamento del programma è individuare la causa dell'errore. Detto fatto. Scriviamo subito le istruzioni che ci indichino in modo lampante che si è verificato un errore aggiungendo più informazioni possibili. Più informazioni abbiamo e più velocemente riusciremo a risalire alle cause dell'errore. La MsgBox, che credo non sia necessario spiegare, permette di visualizzare un messaggio popup e ci possiamo scrivere la Divina Commedia. Tenete a mente che i romanzi qui non servono, è necessario essere precisi e relativamente concisi. Ricordate sempre che il messaggio lo dovrete decodificare al 99,9% voi stessi, quindi fate le cose per bene subito, così risparmierete del prezioso tempo poi.

Ci sono da notare un'altro paio di cose nuove. L'istruzione Exit Sub prima dell'etichetta, questa serve per terminare la procedura in quel preciso punto, il che significa la totale assenza degli errori durante l'esecuzione e di conseguenza tutto il codice scritto per il messaggio d'errore non deve essere eseguito. Exit Sub si utilizza per le procedure, per le funzioni si usa Exit Function e per le proprietà Exit Property. E per gli eventi? Visto che sono delle procedure (Sub) si usa Exit Sub.

L'ultima cosa interessante è l'oggetto Err. Che strano nome. Sarà stato derivato da Error oppure è un "articolo" delle borgate romane? E' la prima che ho detto? Decisamente! Questo simpatico oggettino è il nostro più grande amico e ci aiuta con le preziose informazioni che contiene. La proprietà Number restituisce il numero di errore che ci può servire per discriminare tra loro tutti gli errori, mentre la proprietà Description ci fornisce la descrizione dell'errore ed in ultimo troviamo la proprietà Source che ci ritorna il nome dell'oggetto o dell'applicazione che ha generato l'errore. L'oggetto Err è il posto dove il GdE scarica le informazioni di un errore catturato ed è sensato accedere alle sue proprietà dopo l'avvento di un errore. In realtà questo oggetto fa anche la propagazione degli errori, ma questo, anche se altrettanto interessante, non lo affrontiamo. Siamo quasi giunti alla fine e voi state diventando dei super guru in fatto di gestione degli errori, qundi pazientate ancora un pochetto ^_^.

Vi ricordate il GdE automatico che aveva il difetto di mancata giurisdizione sulle procedure che partono dal suo territorio? Sembra incredibile, ma On Error Goto Etichetta non ha questo difetto. Volendo, possiamo utilizzare questo gestore anche per gestire gli errori delle funzioni chiamate direttamente dal corpo della funzione chimante, evitando inutili sovraccarichi di lavoro.
Ecco di nuovo le due funzioni, ma CalcolaMelePerCapoccia non ha nessun gestore perché CalcolaMele è in grado di gestire gli errori di entrambe grazie a On Error Goto Etichetta.
 
Function CalcolaMelePerCapoccia(NumeroMele as Integer, _
	NumeroCapocce As Integer) As Single
	
Dim Risultato As Single
	Risultato = 0
	Risultato = NumeroMele / NumeroCapocce
	
	'visto che siamo buoni diamo anche una mela in omaggio
	Risultato = Risultato + 1
	CalcolaMelePerCapoccia = Risultato
	
	'scrivo il risultato nella finestra di debug
	Debug.Print "CalcolaMelePerCapoccia = " & Risultato
End Function

Function CalcolaMelePerCapoccia(NumeroMele as Integer, _
	NumeroCapocce As Integer) As Single

Dim Risultato As Single

	On Error Goto Errore
	
	Risultato = 0
	Risultato = NumeroMele / NumeroCapocce
	
	'visto che siamo buoni, diamo anche una mela in omaggio
	Risultato = Risultato + 1
	CalcolaMelePerCapoccia = Risultato
	
	'scrivo il risultato nella finestra di debug
	Debug.Print "CalcolaMelePerCapoccia = " & Risultato
	
	'la funzione ternima in questo punto se non si
	'verificano errori
	Exit Function
Errore:
	'il gestore degli errori salta all'etichetta
	'specificata se si verificano errori
	MsgBox "Generato da " & Err.Source & " Codice errore: " & _
		Err.Number & " Descrizione: " & Err.Description & _
		" CalcolaMele non ha funzionato correttamente!", _
		"ERRORE!", vbCritical
End Function
 
I gestori non incidono in modo rilevante sulle prestazioni del codice, ma è bene non abusarne inutilmente. Ogni gestore degli errori viene disattivato quando la funzione nella quale è stato reso attivo termina. Qundi, terminata la funzione, niente gestione degli errori se il controllo torna in un'altra funzione che non gestisce errori. Se si esce dalla giurisdizione di uno sheriffo e non si incontrano altri sheriffi allora gli errori fanno da padroni.

Ed eccoci all'utlima istruzione On Error Goto 0 (zero). Questa ha un compito facile facile: disattiva qualsiasi gestore degli errori. Subito dopo questa istruzione nessun errore sarà più catturato e di nuovo errori padroni. Ecco un'esempio.
 
Function CalcolaMelePerCapoccia(NumeroMele as Integer, _
	NumeroCapocce As Integer) As Single
	
Dim Risultato As Single
	On Error Goto Errore
	
	Risultato = 0
	Risultato = NumeroMele / NumeroCapocce
	
	'visto che siamo buoni, diamo anche una mela in omaggio
	Risultato = Risultato + 1
	
	'disattivo il gestore degli errori
	On Error Goto 0
	
	'se succede un errore da qui in poi e
	'se non c'è un gestore nella funzione chiamante
	'allora l'intero programma va in crash
	CalcolaMelePerCapoccia = Risultato
	
	'scrivo il risultato nella finestra di debug
	Debug.Print "CalcolaMelePerCapoccia = " & Risultato
	
	'crash sicuro
	Risultato = 2 / 0
	Exit Function
Errore:
	'il gestore degli errori salta all'etichetta
	'specificata se si verificano errori
	MsgBox "Generato da " & Err.Source & " Codice errore: " & _
		Err.Number & " Descrizione: " & Err.Description & _
		" CalcolaMelePerCapoccia non ha funzionato correttamente!", _
		"ERRORE!", vbCritical

End Function
 
Avete visto com'è vacile disattivare un gestore degli errori? E' facile almeno quanto attivarlo. Se non gestite gli errori nel vostro codice, allora state realizzando sistemi poco robusti.

Ora che avete approfondito l'argomento, potrete evitare la figura del crash dell'intero sistema. Vi capiterà l'errore segnalato, questo sì, ma non sarà mai come un'applicativo che termina bruscamente senza un'apparente motivo ed il conseguente gelo perché si ignora la causa; con il cliente che vi guarda storto o l'amico che vi deride.

Siamo giunti alla fine, lungi da me la presunzione di aver trattato l'argomento in modo completo, ma per quanto riguarda lo sviluppo di applicativi non strutturati in componenti ActiveX vi potete ritenere più che preparati. Con i gestori catturiamo tutti gli errori gestiti dal sistema e possiamo rimediare finché non si tratta di gravissimi errori a livello fisico. Che ve ne pare? Sembra piuttosto utile! Un po' di sana pratica vi aiuterà a testare le vostre conoscenze. Armatevi di tastiera e fate un sacco di esperimenti finché non sarete diventati padroni della teoria.