W drugiej części tematu, związanego z obsługą błędów, miałem pisać głównie o metodach obiektu Err (tj. Clear i Raise). Zagadnienie jest jednak na tyle ciekawe i rozbudowane, że chciałbym bardziej wnikliwie omówić polecenia, które opisałem w pierwszej części notki – zwłaszcza w kontekście różnic z bardzo mało znaną instrukcją On Error GoTo -1.
Polecenie On Error GoTo -1 nie jest tym samym, co Err.Clear czy On Error GoTo 0, chociaż instrukcje te mają ze sobą wiele wspólnego. Każda z nich czyści obiekt Err (zostają wtedy utracone wszystkie informacje dotyczące ostatniego błędu). Instrukcje Err.Clear i On Error GoTo -1 dodatkowo nie wyłączają obsługi błędów użytkownika. On Error GoTo 0 wyłącza ją w obrębie danej procedury (lub mówiąc inaczej – przywraca domyślny dla VBA sposób obsługiwania błędów). W stosunku do Err.Clear, On Error GoTo -1 potrafi natomiast zrobić jeszcze jedną ważną rzecz, której nie potrafi żadna inna instrukcja. Umieszczenie On Error GoTo -1 w obsłudze błędów, pozwala korzystać z innych instrukcji typu On Error… właśnie w obsłudze błędów.
Brzmi jak groch z kapustą? Spróbujmy przeanalizować to na przykładach.
Poniżej wkleiłem dwa makra – makro główne i podprocedurę.
Public Sub MakroGlowne() Dim wksArkusz As Worksheet Const sPROC As String = "MakroGlowne" 'Aktywuj obsługę błędów na starcie 1 On Error GoTo ObslugaBledu 'Tą zmienną będziemy czyścić w etykiecie "Wyjscie" 2 Set wksArkusz = ActiveSheet 'Wywołaj podprocedurę generującą sztuczne błędy 3 Podprocedura 'Przejdź do etykiety "Wyjscie" jeśli wystąpił błąd 4 If Err.Number <> 0 Then Err.Clear: GoTo Wyjscie 'Czy to się wykona ???? 5 MsgBox "sssssss" Wyjscie: 6 Set wksArkusz = Nothing 7 Exit Sub ObslugaBledu: 8 MsgBox "Wystąpił błąd nr " & Err.Number & " (" & Err.Description & ")." & _ vbCr & vbCr & "Linia kodu nr " & Erl & " w procedurze " & _ "'" & sPROC & "' modułu '" & msMODUL & "'.", vbInformation, "BŁĄD!" 9 Resume Wyjscie End Sub
Private Sub Podprocedura() Dim wkbPlik As Workbook Const sPROC = "Podprocedura" 'Aktywuj obsługę błędów na starcie 1 On Error GoTo ObslugaBledu 'Tą zmienną będziemy czyścić w etykiecie "Wyjscie" 2 Set wkbPlik = ActiveWorkbook 'Wywołaj błąd nr 11 3 Err.Raise 11 Wyjscie: 4 Set wkbPlik = Nothing 5 Exit Sub ObslugaBledu: 6 MsgBox "Wystąpił błąd nr " & Err.Number & " (" & Err.Description & ")." & _ vbCr & vbCr & "Linia kodu nr " & Erl & " w procedurze " & _ "'" & sPROC & "' modułu '" & msMODUL & "'.", vbInformation, "BŁĄD!" 7 On Error GoTo -1 'Zezwól na nową obsługę błędów 8 On Error Resume Next 'Ignoruj błędy, które się pojawią 9 Err.Raise 13 'Wywołaj błąd aby został zapamiętany 10 GoTo Wyjscie 'Posprzątaj podprocedurę End Sub
Całość ma działać w następujący sposób:
1. Makro główne wprowadza swoje ustawienia na starcie (tutaj jest to przypisanie zmiennej wksArkusz do aktywnego arkusza, ale równie dobrze może to być ustawienie przeliczania na ręczne, wyłączenie odświeżania ekranu, wyłączenie zdarzeń, zmiana wyglądu kursora myszki itp.)
2. Makro wyzwala podprocedurę, która w sposób sztuczny generuje błąd 11
3. Po wystąpieniu błędu w podprocedurze:
a) kod wyświetla MsgBox z informacją na temat błędu
b) kod „sprząta” podprocedurę (zeruje zmienne obiektowe, przywraca ustawienia domyślne dla VBA itp.)
c) kod zamyka podprocedurę
d) kod wraca do makra głównego aby sprawdzić czy wystąpił błąd w podprocedurze
– jeżeli nie wystąpił – makro główne działa dalej
– jeżeli wystąpił – kod „sprząta” makro główne (zeruje zmienne obiektowe, przywraca ustawienia domyślne dla VBA itp.) i kończy bieg.
Te dwie procedury typu Sub (Makro główne i podprocedura) realizują nasz cel.
Wprowadźmy jednak drobne zmiany w kodzie, ale uzyskać potwierdzenie dla naszych tez zawartych we wstępie notki.
Uwaga! Uruchamiamy tylko podprocedurę.
Wniosek nr 1. Instrukcje On Error GoTo 0 i On Error Resume Next nie działają w trybie obsługi błędów
Wystarczy zamienić w linii kodu nr 7, On Error GoTo -1 na On Error GoTo 0, aby zobaczyć, że makro się „wywali” w linii nr 9.
Taki sam rezultat otrzymamy, gdy usuniemy linię kodu nr 7 (On Error Resume Next użyte w obsłudze błędów nie zadziała – błędy nie będą ignorowane). Podobnie nie będzie działać instrukcja On Error GoTo Etykieta. Aby „odblokować te wszystkie instrukcje, należy je poprzedzić poleceniem On Error GoTo -1.
Private Sub Podprocedura()
Dim wkbPlik As Workbook Const sPROC = "Podprocedura" 'Aktywuj obsługę błędów na starcie 1 On Error GoTo ObslugaBledu 'Tą zmienną będziemy czyścić w etykiecie "Wyjscie" 2 Set wkbPlik = ActiveWorkbook 'Wywołaj błąd nr 11 3 Err.Raise 11 Wyjscie: 4 Set wkbPlik = Nothing 5 Exit Sub ObslugaBledu: 6 MsgBox "Wystąpił błąd nr " & Err.Number & " (" & Err.Description & ")." & _ vbCr & vbCr & "Linia kodu nr " & Erl & " w procedurze " & _ "'" & sPROC & "' modułu '" & msMODUL & "'.", vbInformation, "BŁĄD!" 7 On Error GoTo 0 'Spróbuj wyłączyć obsługę błędów użytkownika 8 On Error Resume Next 'Ignoruj błędy, które się pojawią 9 Err.Raise 13 'Wywołaj błąd aby został zapamiętany 10 GoTo Wyjscie 'Posprzątaj podprocedurę End Sub
Wniosek nr 2. On Error GoTo -1 nie wyłącza aktywnej obsługi błędów użytkownika
Jakkolwiek mogłoby się wydawać, że to polecenie (podobnie jak On Error GoTo 0) wyłącza obsługę błędów, to jednak tak nie jest. Aby się o tym przekonać wystarczy wykomentować linię kodu nr 8. W takiej sytuacji korzystamy z polecenia On Error GoTo -1, ale nie ustanawiamy nowego sposobu obsługiwania błędów. W efekcie makro wraca z linii nr 9, do linii nr 6 (i biegnie tak w kółko) – czyli zapamiętuje aktywną obsługę błędów On Error GoTo ObslugaBledu. Gdyby instrukcja wyłączała obsługę błędu makro znów by się „wysypało” – tak się dzieje, gdy użyjemy w tym kontekście On Error GoTo 0).
Private Sub Podprocedura() Dim wkbPlik As Workbook Const sPROC = "Podprocedura" 'Aktywuj obsługę błędów na starcie 1 On Error GoTo ObslugaBledu 'Tą zmienną będziemy czyścić w etykiecie "Wyjscie" 2 Set wkbPlik = ActiveWorkbook 'Wywołaj błąd nr 11 3 Err.Raise 11 Wyjscie: 4 Set wkbPlik = Nothing 5 Exit Sub ObslugaBledu: 6 MsgBox "Wystąpił błąd nr " & Err.Number & " (" & Err.Description & ")." & _ vbCr & vbCr & "Linia kodu nr " & Erl & " w procedurze " & _ "'" & sPROC & "' modułu '" & msMODUL & "'.", vbInformation, "BŁĄD!" 7 On Error GoTo -1 'Zezwól na nową obsługę błędów 8 'On Error Resume Next 'Ignoruj błędy, które się pojawią 9 Err.Raise 13 'Wywołaj błąd aby został zapamiętany 10 GoTo Wyjscie 'Posprzątaj podprocedurę End Sub
Wniosek 3. Błędy w obsłudze błędów procedury mogą być obsłużone z poziomu innego makra
Wróćmy teraz do obu makr wyjściowych. W podprocedurze zmieńmy tylko jedną linię kodu (nr 7), tak jak we wniosku nr 1. Gdy uruchamialiśmy tylko podprocedurę, makro wyrzucało błąd – instrukcje On Error … nie działały bowiem w aktywnej obsłudze błędów procedury. Uruchamianie zaczynamy teraz jednak od makra głównego. W efekcie otrzymujemy dwa komunikaty MsgBox (błąd nr 11 i 13), makro się jednak „nie wywali”. Wynika to z faktu, że drugi błąd (linia kodu nr 9) zostaje obsłużony już z poziomu makra głównego (On Error GoTo ObslugaBledu). Jest to rzecz, o której warto wiedzieć, ale należy pamiętać, że nie realizuje ona naszego zamysłu. Naszą intencją była emulacja błędu nr 13, tylko po to aby przenieść informację o błędzie do makra głównego (nie chcieliśmy wyświetlać żadnej informacji odnośnie błędu nr 13). W takim układzie podprocedura nie jest również „sprzątana”, ponieważ po wykryciu błędu, kod przeskakuje od razu do makra głównego.
Wniosek 4. Polecenia typu On Error … i Err.Clear czyszczą obiekt Err
Do okienka Watches w edytorze VB dodajmy sobie obiekt Err. Po wykonaniu instrukcji On Error GoTo -1 lub Err.Clear zobaczymy, że obiekt Err zostanie wyczyszczony. Oczywiście taki sam efekt zobaczymy po wykonaniu instrukcji On Error GoTo 0 lub On Error GoTo Etykieta
Ufff… To wszystko na dziś. Zachęcam do komentowania tematu 🙂
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.