Gestionarea erorilor în codul VBA - Macro-uri și programe VBA - Excel - Director de articole - Excel perfect

După ce am căutat în RuNet material pe tema gestionării erorilor în VBA, nu am văzut ceva ce mi-a plăcut în primele două pagini ale rezultatelor căutării. Poate că arătam prost, dar am decis să scriu propriul articol pe această temă.

Scuze, dar - puțină verbiaj :)

Erori în program

Erorile de rulare apar atunci când mediul de programare nu poate face ceea ce doriți. Pot exista multe astfel de situații. De exemplu:

Accesați un obiect după numele său, dar nu există niciun obiect cu acel nume în colecție

Doriți să selectați o celulă pe o foaie, iar această foaie nu este activă în prezent (o greșeală comună pentru începători Excel VBA)

Doriți să ștergeți rândurile filtrate de filtrul automat, dar filtrul nu a returnat deloc înregistrări și nu există nimic de șters

Faceți referire la un element de matrice care se află în afara limitelor sale.

Încercați să atribuiți o valoare unei variabile pe care nu o poate stoca. De exemplu, unei variabile Long nu i se poate atribui o constantă șir sau unei variabile Integer nu i se poate atribui o valoare mai mare de 32767 .

Mediul de execuție reacționează la oricare dintre aceste situații și la sute de alte situații într-un mod standard - întrerupe execuția programului în instrucțiunea în care a apărut o eroare sau, după cum se spune, o excepție. Ecranul afișează informații despre eroarea care a apărut și oferă opțiuni standard pentru continuarea lucrului:

Continuare - Acest element este întotdeauna inactiv în timpul unei erori. Este activ când utilizați instrucțiunea Stop în timpul execuției programului. Apropo, acesta este un operator foarte util pentru depanarea unui program.

Sfârșit (complet) - finalizarea execuției programului

Depanare (depanare) - trecerea în modul de depanare, în care puteți vedea ce operatora apărut o eroare care conține variabile, puteți chiar să trageți bara galbenă care evidențiază operatorul curent înapoi și să modificați valoarea variabilelor prin fereastra Imediat (totuși, aceasta este exotică). În general, butonul Debug vă permite să vedeți unde a apărut eroarea și să încercați să înțelegeți de ce s-a întâmplat.

De ce există erori în cod?

Multe erori în timpul scrierii codului apar din cauza neatenției sau a înțelegerii necorespunzătoare a ceea ce faceți. De obicei, există o mulțime de astfel de erori, în special pentru programatorii începători, dar aceste erori sunt destul de ușor de detectat și remediat, deoarece până nu le remediați, nimic nu funcționează. Ei bine, de exemplu, trebuie să extragi date din coloana a 5-a și extragi din a 6-a, dar pur și simplu nu sunt acolo. Este clar că vei observa acest lucru foarte repede.

Al doilea grup de erori sunt erorile optimistului. Când programul este scris în general corect, dar algoritmul nu este pregătit pentru loviturile destinului sub formă de acțiuni neașteptate din partea utilizatorului, erori I/O (te așteptai să citești date dintr-un fișier, dar nu a existat fișier cu acest nume, sau a fost blocat de o altă aplicație), prezintă configurații computer (diferite versiuni ale sistemului de operare sau birou, care diferă în unele detalii).

Erori de logică subtile. Cu cât programul este mai complex, cu atât este mai probabil ca modelul problemei din capul tău, programul tău și realitatea să nu fie destul de consistente între ele. Până nu obțineți o imersiune suficientă în sarcină, nu veți găsi și remedia astfel de erori. Uneori este nevoie de mult timp. Dar acest lucru este tipic pentru sarcini complexe.

Erori la intersecția dintre aplicația dvs. și serviciile OS, care duc la blocări neașteptate ale aplicației. Acest lucru nu ar trebui să se întâmple deloc, dar, după cum înțelegem, atât sistemul de operare, cât și biroul conțin erori, și tu(mai probabil) este posibil să utilizați incorect apelurile de sistem. Astfel de erori sunt un adevărat coșmar, mai ales când apar doar pe anumite configurații, în anumite condiții, sunt greu de surprins și de reprodus fiabil.

Sarcini ale mecanismelor de tratare a erorilor

Asigurați funcționarea stabilă a programului. Apariția unei erori, a cărei apariție nu ați prevăzut-o, va duce în majoritatea cazurilor la o terminare anormală a întregului program sau a unei părți a acestuia. La un anumit nivel de astfel de situații, acest lucru duce la faptul că programul devine imposibil de utilizat.

Informare. Nu este suficient să gestionați eroarea și să împiedicați terminarea programului. De asemenea, este necesar să se informeze în mod adecvat utilizatorul despre motivele comportamentului nestandard al programului. Acțiunile incorecte ale utilizatorului sunt adesea cauza erorilor în program, așa că este important să le raportați acestuia.

Protecția datelor împotriva daunelor. Programul este obligat să protejeze rezultatele muncii proprii sau ale utilizatorului de daune neintenționate. Acțiunile distructive ar trebui să fie prevăzute cu casete de dialog adecvate de avertizare. Adesea, o eroare care nu este gestionată corespunzător poate deteriora datele de care aveți nevoie.

Fișier exemplu

Cod fără manipulare a erorilor

Iată un exemplu simplu din tavan. Dacă apelați Example_00 , va funcționa bine fără erori și va returna:

codul

Funcția GetCalories primește un șir cu un fel de mâncare și ar trebui să returneze conținutul de calorii prin referire la tabelul din A1:B7.

codul

Să căutăm punctele slabe în acest cod. Primul lucru care ar trebui să ne vină în minte este dacă căutăm, ce se va întâmpla dacă nu îl găsim? Și, desigur, va exista o eroare. Este inițiată prin metoda Match.

macro-uri

Un alt punct slab al acestei subrutine:funcția returnează un tip real Double și chiar dacă căutarea a avut succes, atunci Cells(intRow, 2) poate conține accidental un șir de text și, prin urmare, atunci când încercați să atribuiți un tip de șir unui tip numeric, o eroare va apar. Și, dacă puteți evita a doua eroare din cauza unei instrucțiuni if ​​suplimentare cu o verificare prin IsNumber (), atunci nu puteți evita prima eroare în acest fel. Ce să fac? Aici intervin operatorii de tratare a erorilor.

Există două abordări pentru tratarea erorilor: abordarea offline și abordarea la distanță. Am inventat acești termeni pentru a fi mai ușor de discutat.

Abordare autonomă

Sensul abordării autonome este de a nu spăla lenjeria murdară în public. Dacă apare o eroare în subrutină, atunci trebuie să ne asumăm unde va apărea și să o așteptăm acolo cu un club. Eroarea, în acest caz, este de obicei tratată de către operator imediat după locul potențial periculos. Să vedem cum ar putea arăta:

Deci, ce se face aici:

Am declarat variabile. Variabilele nedeclarate sunt de tip Variant și au o valoare implicită Empty . Variabilele declarate de tipuri numerice sunt inițializate la zero, variabilele șir sunt inițializate la un șir gol, adică știu dinainte ce conțin, ceea ce este bun pentru tratarea erorilor.

La apelarea metodei WorksheetFunction.Match, obținem o eroare, deoarece tabelul nu conține valoarea necesară. Și acesta, apropo, era operatorul de atribuire ( = ). Înainte ca partea stângă a operatorului de atribuire ( intRow ) să i se atribuie ceva, partea dreaptă a operatorului de atribuire ( WorksheetFunction.Match . ) trebuie evaluată și, deoarece apare o eroare în timpul acestei evaluări, variabila intRow rămâne așa cum a fost! Și, așa cum am spus, VBA îl inițializează automat cu zero toînceperea execuției subrutinei. Se pare că, dacă apare o eroare în acest operator, atunci intRow va fi zero. Dacă nu apar erori în timpul căutării, atunci nu va fi zero în niciun caz, deoarece liniile de pe foaie sunt numerotate de la unu.

Și controlăm acest zero adăugând o instrucțiune If. Dacă intRow este mai mare decât zero, atunci WorksheetFunction.Match a funcționat corect, iar dacă nu, atunci munca subrutinei trebuie întreruptă, dar mai multe despre asta mai târziu.

În continuare, ne amintim că Cells(intRow, 2) ar putea returna teoretic o valoare șir, care ar provoca o eroare de tip nepotrivire atunci când este atribuită unei variabile de tip Double ( GetCalories_v1 ), așa că am pus o verificare suplimentară asupra variabilei intermediare varTemp care este numeric. Și dacă da, atunci atribuim GetCalories_v1 valoarea din varTemp .

În cazul oricărei erori în interiorul GetCalories_v1, va returna pur și simplu zero. De ce zero? Pentru că și variabila GetCalories_v1 este inițializată la zero și nu trebuie să vă faceți griji pentru aceasta, iar în cazul unei erori va rămâne intactă.

În consecință, codul părinte (în cazul nostru, procedura Example_01 își joacă rolul) ar trebui să verifice dacă GetCalories_v1 returnează zero și să fie pregătit pentru această situație.

Și acum un punct subtil pe care nu toată lumea îl înțelege. De ce am folosit variabilele intermediare intRow și varTemp? Se pare că există un răspuns evident - să nu se calculeze valoarea expresiilor cu Potrivire și celule de 2 ori. Acest lucru este parțial adevărat, desigur. Dar acesta, în acest caz, nu este motivul principal. Motivul principal este că acest cod

va face programul să se comporte prost. Dacă Match-ul nostru aruncă o excepție, atunci VBA va transfera controlul instrucțiunii NEXT, iar următoarea instrucțiune în acest caz este cea care vine după Then - assignmentvalori variabile varTemp. Astfel, verificarea noastră de erori va funcționa exact invers, transferând controlul către partea din cod care ar trebui protejată de situația în care Match nu a găsit un rând în tabel. De aceea este important să nu existe nimic în instrucțiunea If care ar putea cauza o eroare.

După cum puteți vedea, în această abordare, de multe ori nici nu am nevoie să verific obiectul Err pentru a ști că a apărut o eroare, deoarece mă bazez pe variabilele intermediare lăsate neinițializate, ceea ce este un indiciu că a apărut o eroare.

Abordare de la distanță

Această metodă se bazează pe faptul că, atunci când apare o eroare, VBA transferă controlul către o secțiune specială de cod - un handler de erori, care este de obicei plasat la sfârșitul subrutinei. Ar putea arăta astfel:

Rețineți că:

Declarația on error acum, în cazul unei erori, indică transferul controlului către eticheta ErrorHandler, care este declarată la sfârșitul codului procedurii GetCalories_v2

În cod, nu ne pasă de niciun fel de verificări. A apărut o eroare? Mergeți la marcaj - își vor da seama.

Dacă nu apare nicio eroare, atunci pentru a împiedica programul să execute linii destinate tratării erorilor, eticheta ErrorHandler este de obicei precedată de o instrucțiune Exit Sub sau Exit Function (în funcție de tipul de subrutină).

Punctul fundamental este prezența instrucțiunii On Error Resume Next imediat după eticheta ErrorHandler. Faptul este că, după ce ați trecut la eticheta ErrorHandler, este foarte periculos să aveți activ operatorul On Error GoTo ErrorHandler, deoarece dacă apare vreo eroare în gestionarea erorilor, atunci controlul va fi transferat înapoi la etichetă și, pe măsură ce este ușor de înțeles, se formează o buclă infinită. Prin urmare, imediat după etichetă avem ocaziaapariţia ciclului este eliminată de operator On Error Resume Next .

Ce metodă este mai bine să utilizați depinde de preferințele dvs. și de situațiile specifice. Gestionarea competentă a erorilor poate fi făcută într-un fel sau altul. Iată câteva idei despre avantajele și dezavantajele acestor abordări: