Search  
Saturday, June 24, 2017 ..:: Articole » Tratarea erorilor în Visual FoxPro ::.. Register  Login
 Articole Minimize

Tratarea erorilor în Visual FoxPro

Autor: Doug Hennig
Notă: Gestionarea erorilor este mai flexibilă şi, totodată, mai complexă decât cea din FPW 2.x. Această expunere analizează diferenţele din domeniu dintre FPW 2.x şi Visual FoxPro, şi evidenţiazş câteva strategii în crearea rutinelor de gestionare a erorilor.

În Visual FoxPro o mulţime de lucruri sunt mai complexe şi mai flexibile decât în FPW 2.x. Gestionarea erorilor nu face excepţie de la această regulă. În primul rând, există funcţii suplimentare de interceptare a erorii (nu numai ON ERROR - ca în FPW). Pe de altă parte, numărul mesajelor de eroare din Visual FoxPro s-a dublat (cu aproximaţie) faţă de cele din FPW, ajungând la peste 600. Mai mult, în anumite condiţii, unele erori sunt interceptate automat, iar altele nu pot fi interceptate de loc.

Datorită faptului că gestionarea erorilor este un subiect important şi foarte larg, vor fi mai multe expuneri care îl tratează. În această expunere vor fi expuse difenţele dintre Visual FoxPro şi FPW 2.6, şi vor fi detaliate câteva tehnici de implementare a unei rutine de interceptare a erorilor. Restul, în următoarele expuneri.

Noţiuni de bază în gestionarea erorilor

Gestionarea corectă a erorilor implică mai multe aspecte:

  • Scrierea unei rutine globale de interceptare a erorii
  • Determinarea cauzei erorii
  • Afişarea unui mesaj către utilizator (informându-l, în măsura în care îl priveşte, ce s-a întâmplat)
  • Încercarea de a rezolva problema - executarea din nou a comenzii care a generat eroarea, trecerea la instrucţiunea următoare, revenirea la programul apelant, ieşirea din aplicaţie, ş.a.m.d.

Scrierea unei rutine globale de interceptare a erorii nu este diferită de FPW 2.x. Se foloseşte în continuare comanda ON ERROR, ca în exemplul următor:

ON ERROR DO err_proc WITH ERROR(), SYS(16), LINENO()

Parametrii transmit codul de eroare, numele programului şi numărul liniei de cod care a generat eroarea. Puteţi transmite mai mulţi parametri, dacă doriţi.

Există câteva funcţii în FPW şi Visual FoxPro care sunt folositoare în determinarea cauzei erorii, incluzând ERROR(), MESSAGE(), LINENO(), SYS(16) şi SYS(2018). În Visual FoxPro există şi o funcţie nouă, numită AERROR(), care plasează informaţii despre ultima eroare într-un masiv. Este bine documentată în help. Deşi o mare parte din informaţiile oferite de această funcţie pot fi obţinute şi din alte surse, pentru anumite tipuri de eroare (cum ar fi cele generate de OLE, ODBC şi trigger-e) AERROR() oferă informaţii suplimentare, cum ar fi numele trigger-ului care a generat eroarea.

Informarea utilizatorului despre eroare este destul de simplu de făcut, şi de cele mai multe ori este combinată cu acordarea posibilităţii de a determina modul în care se va rezolva problema. Aici problema se complică: trebuie să decidem ce îi spunem utilizatorului şi ce posibilităţi îi oferim. Mesajul care trebuie afişat diferă funcţie de eroarea apărută şi textul din el trebuie exprimat pe un ton cât mai calm posibil, pentru că altfel utilizatorul intră în panică şi face figura aia cu trei degete (Ctrl+Alt+Del). De asemenea, mesajul trebuie să dea şi soluţii pentru rezolvarea problemei. De exemplu, dacă imprimanta este off-line, o gestionare corectă a situaţiei este afişarea unui mesaj care să îl întrebe pe utilizator dacă imprimanta are hârtie, dacă este pornită şi conectată la calculator, şi să ofere posibilitatea reîncercării tipăririi sau anularea ei. O altă situaţie tipică este încercarea de a modifica o înregistrare blocată de alt utilizator. În acest caz mesajul ar trebui să îl anunţe pe utilizator că altcineva face acelaşi lucru, şi să îi ofere posibilitatea de a anula modificările sau să încerce din nou.

Dat fiind faptul că sunt mai mult de 600 de mesaje de eroare, numai gândul că trebuie să scrieţi un text mai "uman" pentru toate vă poate îmbolnăvi de nervi. Din fericire, dacă studiaţi puţin mesajele de eroare din Visual FoxPro, veţi constata că ele se încadrează în 4 mari categorii:

  • "Chestia asta nu se întâmplă decât dacă ia calculatorul foc, sau aşa ceva..."
  • "Chestia asta nu s-ar fi întâmplat dacă programatorul ar fi băut mai puţină vodcă în seara precedentă..."
  • "Cum adică, vrei să spui că pot să lucreze mai mulţi utilizatori simultan pe programul ăsta?"
  • Erori care trebuie tratate individual (din fericire, foarte puţine).

O metodă de a gestiona această diversitate de mesaje de eroare este crearea unei tabele care să conţină codurile de eroare şi mesajele corespunzătoare. Rutina de tratare a erorii ar trebui să caute codul de eroare şi să afişeze mesajul corespunzător. Erorile dintr-o categorie afişează toate acelaşi mesaj. De exemplu, toate erorile care indică lipsa memoriei disponibile ar trebui să afişeze un mesaj de eroare care să îi spună utilizatorului să sistemul n-are suficientă memorie şi să îi sugereze să: a) închidă toate celelalte aplicaţii, b) să restarteze calculatorul şi c) dacă nici aşa nu merge, să cheme un puşti din vecini... :). Pentru exemplificare, iată lista tuturor mesajelor de eroare privitoare la insuficienţa memoriei:

  • 21 - Length of variable strings exceeds amount of memory.
  • 43 - There is not enough memory to complete this operation.
  • 1143 - Not enough memory for buffer.
  • 1150 - Not enough memory for file map.
  • 1151 - Not enough memory for filename.
  • 1600 - Not enough memory to open a table with the USE command.
  • 1986 - GDI memory is low, close one or more windows and try again.

Probabil gândiţi: "Hmmm, hai că nu e dracul chiar atât de negru, dar cine stă să facă tabela aia pentru 600 şi ceva de mesaje de eroare?" Din fericire, în Visual FoxPro există o comandă (am constatat cu stupefacţie că foarte mulţi programatori nici n-au auzit de ea) numită ERROR. Ea determină producerea unei erori. Această comandă este superbă pentru testarea rutinei de tratare a erorii - dacă n-ar fi existat, ar fi trebuit să scrieţi cod care să dea eroarea respectivă. O metodă rapidă de a genera tabela cu mesajele de eroare este să scrieţi un program care să genereze toate codurile de eroare posibile şi să adauge câte o înregistrare pentru fiecare din ele. Iată programul:

CREATE TABEL Erori (coderoare I, Message M)
INDEX ON coderoare TAG coderoare
ON ERROR DO salvezerr WITH ERROR(), MESSAGE()
FOR i = 1 TO 2500
	IF i = 1116 && Eroarea aceasta opreşte programul
		LOOP
	ENDIF
	ERROR i
ENDFOR
ON ERROR
USE

PROCEDURE salvezerr
LPARAMETERS pnEroare, pcMesaj
IF pnEroare <> 1941  && ignoră eroarea "Invalid error code"
	INSERT INTO Erori VALUES (pnEroare, pcMesaj)
ENDIF
RETURN

Înainte de a afişa mesajul de eroare către utilizator, eu personal prefer să scriu undeva, într-un log, ce s-a întâmplat. Chestia asta s-a dovedit utilă de nenumărate ori, pentru că raportările utilizatorilor sunt prea vagi pentru a înţelege corect problema. Fişierul în care scriu erorile poate fi un fişier text, dar mai bine o tabelă care conţine: data şi ora erorii (sau un câmp DATETIME), numele utilizatorului, codul de eroare, numărul şi conţinutul liniei de cod unde a apărut eroarea, şi un câmp memo care conţine toate variabilele definite în momentul respectiv (se scrie cu comanda SAVE TO MEMO nume_memo).

De asemenea, în această tabelă eu plasez şi un câmp numit "Optiune", în care scriu ce opţiune a ales utilizatorul când i-a apărut mesajul de eroare. De ce fac asta? Pentru că am văzut utilizatori cărora nu le pasă de erorile din program. Indiferent ce se întâmplă, ei resetează calculatorul. De cele mai multe ori, manevra asta este generatoare de probleme mult mai mari decât eroarea iniţială. Înainte de a afişa mesajul de eroare, în acest câmp scriu "RESET". Dacă utilizatorul resetează calculatorul în loc să aleagă una din variante, în câmpul acesta rămâne scris "RESET". Mai târziu, când mă uit în fişier, văd utilizatorii care îşi resetează calculatorul în timp ce rulează programele mele. Şi, evident, avem o mică discuţie în care le administrez electroşocuri. :)

Rezolvarea unei erori este complicată. Comanda RETRY execută din nou comanda care a generat eroarea, dar în cele mai multe cazuri nu prea este de folos, deoarece se produce aceeaşi eroare, iar utilizatorul nu are prea multe posibilităţi să rezolve problema de unul singur. Comanda RETURN întoarce controlul către următoarea linie de cod, dar fiindcă linia care a generat eroarea nu s-a executat, de cele mai multe ori se va genera o nouă eroare - cum ar fi o variabilă care nu s-a creat, ş.a.m.d. Comanda QUIT este o opţiune potrivită, deoarece majoritatea erorilor apar datorită programatorului sau sistemului, şi utilizatorii nu prea au ce face până nu se rezolvă.

O altă opţiune este folosirea comenzilor RETURN TO MASTER sau RETURN TO <nume_program> pentru a întrerupe programul care a generat eroarea şi revenirea la programul apelant sau la un program specificat. Această opţiune trebuie tratată cu atenţie, pentru că de cele mai multe ori sistemul rămâne "dezorientat": formulare sau ferestre mai există încă, deşi nu ar trebui, tabelele şi cursoarele specifice programului cu eroarea rămân deschise, etc. Rutina de tratare a erorii ar trebui să aducă sistemul într-o stare cât mai stabilă. Acest lucru înseamnă că rutina trebuie să aibă nişte informaţii despre structura aplicaţiei. De exemplu, rutina de gestionare a erorilor pe care am scris-o în programul meu de salarii execută următorii paşi:

  • Elimină din memorie toate ferestrele definite.
  • Elimină toate numele ferestrelor din meniul Ferestre.
  • Elimină evenimentele în execuţie ale tuturor ferestrelor.
  • Elimină toate setările specifice modulului în curs de execuţie.
  • Elimină comanda READ curentă.
  • Revine la programul principal.

Object.Error şi Form.Error

Tipul de gestionare a erorilor discutat mai devreme este un mod global de gestionare. Cu alte cuvinte, în toată aplicaţia este activă o singură rutină de gestionare a erorii. Dar Visual FoxPro oferă evenimentul Error, care permite implementarea tratarea locală a erorii.

Fiecare obiect din Visual FoxPro are evenimentul Error. Evident, nu orice obiect are şi o metodă Error. Ca să clarificăm diferenţa, un eveniment este un "declanşator" activat de utilizator sau de sistem (o apăsare de tastă, un click de mouse ori ceea ce Visual FoxPro crede că este o eroare), în timp ce o metodă este codul care se execută când apare un eveniment. Codul unei metode se execută şi când obiectului îi este transmis un mesaj care îl determină să execute metoda respectivă. La multe evenimente, cum ar fi Click Event, dacă obiectul nu are cod în metoda corespunzătoare, evenimentul este ignorat. Dar dacă apare o eroare, sunt câţiva factori care influenţează ceea ce se întâmplă în continuare.

Dacă apare vreo eroare, va fi apelată metoda Error a obiectului, dacă există. Formularul Eroare1 din fişierul ataşat acestui articol demonstrează acest lucru: butonul are o eroare în metoda Click, dar are şi o metodă Error, care interceptează eroarea. Metoda Error a unui obiect nu este apelată dacă eroarea este cauzată de modificările interactive ale utilizatorului. De exemplu, dacă aveţi un textbox bound la câmpul PRET din tabelă, şi regula de validare pentru câmpul respectiv este BETWEEN(0,10), introducerea valorii 20 nu va executa metoda Error a textbox-ului. În locul ei, va apare mesajul standard de la validarea câmpului, sau un mesaj generic dacă n-aţi introdus unul specific. Formularul Eroare2 demonstrează acest lucru.

Ce se întâmplă dacă apare o eroare într-un obiect care nu are o metodă Error? Să presupunem că în codul metodei Click este o eroare, dar butonul nu are o metodă Error. Iniţial, am presupus că metoda Error a obiectului părinte va trata problema. Din păcate (sau din fericire?) n-a fost aşa. În realitate se apelează rutina ON ERROR, dacă există vreuna. Dacă nu există nici una, Visual FoxPro execută propria sa rutină de eroare, şi în acest caz, putem considera că aplicaţia a "crăpat". Deci: metoda Error a unui formular nu acţionează ca o rutină de interceptare a erorii pentru obiectele conţinute în formular ci doar pentru erorile apărute în metodele formularului. Formularul Eroare3 demonstrează acest lucru.

"De ce aş vrea o rutină de gestionare a erorilor la nivel de formular?" Unul din motive este restrângerea întregii rutine de tratare a erorilor într-un singur loc, în loc să o duplicăm în fiecare obiect (sau în fiecare clasă pe care o folosim). Alt motiv este instaurarea "Regulii celor trei nivele". Un obiect poate avea o rutină de tratare a erorilor specifice lui însuşi. Erorile care îl depăşesc vor fi atribuite (Delegation) metodei Error a formularului. Toate obiectele pentru care nu este nevoie să scrieţi o rutină de interceptare a erorilor proprii nu vor avea deloc o metodă Error; ele vor apela rutina Error a formularului. Metoda Error a formularului va trata erorile specifice formularului şi va atribui restul erorilor rutinei globale de tratare a erorilor. Astfel, tratarea erorilor devine tot mai generică pe măsură ce urcăm în ierarhie. Din păcate trebuie să scriem cod pentru a obţine un astfel de sistem de gestionare a erorilor. Vom vedea imediat cum se implementează un astfel de mecanism.

Dat fiind faptul că metoda Error a formularului nu este în mod automat un gestionar de erori la nivel de formular, prima mea încercare a fost aceea de a folosi comanda ON ERROR ThisForm.Error(ERRORNO(), PROGRAM(), LINENO()) în metoda Init a formularului. M-am gândit că metoda Error a formularului va fi apelată de oricare obiect al formularului care n-are o metodă Error proprie; în acest caz este apelată rutina ON ERROR, care, la rândul ei, apelează rutina Error a formularului. Pare simplu şi firesc, nu-i aşa? Din păcate, nu merge. Visual FoxPro întoarce un mesaj de eroare care conţine textul "THISFORM can only be used in a method". La o analiză mai atentă, mesajul capătă sens: la interceptarea unei erori, Visual FoxPro execută un cod (este vorba despre comanda care urmează după ON ERROR) care se află în afara domeniului (adică în afara formularului), iar adresarea "ThisForm" nu poate fi folosită, pentru că execuţia programului este în afara formularului. Formularul Eroare4 din fişierul ataşat demonstrează acest lucru.

Calea de rezolvare a problemei este apelarea la colecţia de formulare a ecranului, astfel: ON ERROR _screen.ActiveForm.Error(ERROR(), PROGRAM(), LINENO()). Varianta aceasta funcţionează datorită faptului că _screen.ActiveForm are întotdeauna sens (cu excepţia situaţiei când nu este nici un formular activ, dar în cazul nostru nu este posibil), chiar dacă este apelat dintr-un eveniment de eroare. Formularul Eroare5 demonstrează acest lucru.

Pe scurt, iată ordinea în care sunt gestionate erorile:

  • Dacă eroarea este cauzată de modificările interactive ale utilizatorului, Visual FoxPro afişează propriul său mesaj de eroare.
  • Dacă eroarea apare într-o metodă a unui obiect care are o metodă Error, Visual FoxPro o apelează pe aceasta.
  • Dacă există o rutină ON ERROR, Visual FoxPro o apelează pe aceasta.
  • Visual FoxPro afişează propriul său mesaj de eroare.

Proiectarea unei scheme de tratare a erorilor

Acum a sosit momentul să aruncăm o privire peste o posibilă schemă de tratare a erorilor într-o aplicaţie. Sunt convins că doriţi să gestionaţi erorile într-un mod cât mai eficient posibil şi să permiteţi şi obiectelor să-şi gestioneze singure propriile erori. Schema următoare aplică "Regula celor trei nivele" pe care am menţionat-o mai devreme.

OBIECT

Dacă un obiect nu necesită o tratare a erorilor specifice, nu aveţi nevoie de o metodă Error. În schimb, dacă este necesară o tratare de acest fel, atunci metoda Error proprie trebuie să le gestioneze erorile proprii şi să le atribuie pe celelalte rutinei ON ERROR. În secvenţa de cod care urmează aveţi un exemplu. Funcţia STRTRAN() este folosită pentru a face trei substituţii în apelarea rutinei ON ERROR. În acest fel, în loc de SYS(16) se va scrie numele metodei care a generat eroarea, în locul lui ERROR() se va scrie codul de eroare, iar în locul lui LINENO() se va scrie numărul liniei. Şirul obţinut este trimis către nivelul superior, adică metoda Error a formularului.

LPARAMETERS nError, cMethod, nLine
LOCAL sir_eroare
DO CASE
CASE nError = ### && codurile de eroare care se tratează local
	* rutinele de tratare a erorilor
OTHERWISE
	sir_eroare = STRTRAN(ON('ERROR'), 'SYS(16)', ;
		"'PROCEDURA ' + UPPER(THIS.NAME + '.' + cMethod)")
	sir_eroare = STRTRAN(lcError, 'ERROR()', 'nError')
	sir_eroare = STRTRAN(lcError, 'LINENO()', 'nLine')
	&sir_eroare
ENDCASE

FORMULAR

Raţionamentul de la obiecte se aplică şi la formulare. Dacă n-au nevoie de o tratare a erorilor, nu vor avea o metodă Error. În caz contrar, metoda Activate a formularului ar trebui să salveze setarea curentă a lui ON ERROR într-o proprietate a formularului şi stabileşte ON ERROR către metoda Error a formularului. Metoda Error a formularului tratează erorile specifice lui, iar restul le atribuie rutinei iniţiale (cea salvată) de tratare a erorilor. Metoda Error a formularului se execută nu numai la apariţia unei erori în metodele formularului, ci şi la apariţia unei erori într-un obiect care nu are o metodă Error proprie sau când erorile unui obiect sunt atribuite rutinei ON ERROR.

Aici aveţi un exemplu de implementare:

Metoda Activate:

THIS.RutinaEroare = ON('ERROR')
ON ERROR _screen.ActiveForm.Error(ERROR(), ;
	SYS(16), LINENO())

Metoda Deactivate():

LOCAL eroare
eroare = This.RutinaEroare
ON ERROR &eroare

Metoda Error a formularului:

LPARAMETERS nError, cMethod, nLine
LOCAL eroare
DO CASE
	CASE nError = ### && codurile de eroare care se tratează în formular
		* rutinele de tratare a erorilor
	OTHERWISE
		eroare = STRTRAN(This.RutinaEroare, 'SYS(16)', ;  
			"'PROCEDURA ' + UPPER(This.Name) + '.' + cMethod)")
		eroare = STRTRAN(eroare, 'ERROR()', 'nError')
		eroare = STRTRAN(eroare, 'LINENO()', 'nLine')
		&eroare
ENDCASE

ON ERROR

Rutina ON ERROR trebuie iniţializată la lansarea aplicaţiei. Ea trebuie să accepte aceiaşi parametri ca şi metoda Error: codul erorii, numele programului sau al metodei care a generat eroarea şi numărul liniei în care a apărut eroarea. Codul următor este un exemplu:

ON ERROR DO err_proc WITH ERROR(), SYS(16), LINENO()

Aici aveţi un exemplu de rutină de tratare a erorilor:

PROCEDURE err_proc
LPARAMETERS nError, cMethod, nLine
LOCAL aEroare
= AERROR(aEroare)
* tratarea erorii

Concluzie

În această expunere am analizat opţiunile de tratare a erorilor pe care Visual FoxPro le pune la dispoziţia dvs. Ele sunt mai flexibile decât în FoxPro for Windows, dar flexibilitatea presupune şi complexitate. În expunerea următoare vom studia o rutină de tratare a erorilor implementată sub forma unei clase. Codul nu va fi complet şi gata de folosire, dar este un cadru pe care îl puteţi particulariza după dorinţă.

Descărcaţi Erori1.zip


    

 Google Ads Minimize

    

Copyright 2002-2013 Profox   Terms Of Use  Privacy Statement