VBA
Błędy czasu wykonania VBA
Szukaj…
Wprowadzenie
Kod, który się kompiluje, może nadal napotykać błędy w czasie wykonywania. W tym temacie wymieniono najczęstsze, ich przyczyny i sposoby ich unikania.
Błąd w czasie wykonywania „3”: Return bez GoSub
Błędny kod
Sub DoSomething()
GoSub DoThis
DoThis:
Debug.Print "Hi!"
Return
End Sub
Dlaczego to nie działa?
Wykonanie wchodzi do procedury DoSomething
, DoThis
etykiety DoThis
, drukuje „Cześć!” na wyjściu debugowania wraca do instrukcji natychmiast po wywołaniu GoSub
, wypisuje „Cześć!” ponownie, a następnie napotyka instrukcję Return
, ale nie ma do czego wrócić , ponieważ nie dotarliśmy tutaj z instrukcją GoSub
.
Poprawny kod
Sub DoSomething()
GoSub DoThis
Exit Sub
DoThis:
Debug.Print "Hi!"
Return
End Sub
Dlaczego to działa?
Poprzez wprowadzenie Exit Sub
dyspozycję przed DoThis
linii etykiety, mamy posegregowane na DoThis
podprogram od reszty ciała zabiegu - to jedyny sposób, aby wykonać DoThis
podprogram jest przez GoSub
skoku.
Inne notatki
GoSub
/ Return
jest przestarzałe i należy go unikać na rzecz rzeczywistych wywołań procedur. Procedura nie powinna zawierać podprogramów innych niż procedury obsługi błędów.
Jest to bardzo podobne do błędu czasu wykonania „20”: Wznów bez błędu ; w obu sytuacjach rozwiązaniem jest upewnienie się, że normalna ścieżka wykonania nie może wejść do podprogramu (identyfikowanego przez etykietę linii) bez wyraźnego skoku (zakładając, że On Error GoTo
jest uważany za skok jawny ).
Błąd w czasie wykonywania „6”: Przepełnienie
błędny kod
Sub DoSomething()
Dim row As Integer
For row = 1 To 100000
'do stuff
Next
End Sub
Dlaczego to nie działa?
Typ danych Integer
to 16-bitowa liczba całkowita ze znakiem o maksymalnej wartości 32 767; przypisanie go do dowolnego większego elementu spowoduje przepełnienie typu i podniesienie tego błędu.
Poprawny kod
Sub DoSomething()
Dim row As Long
For row = 1 To 100000
'do stuff
Next
End Sub
Dlaczego to działa?
Używając Long
(32-bitowej) liczby całkowitej, możemy teraz utworzyć pętlę, która iteruje ponad 32 767 razy bez przepełnienia typu zmiennej licznika.
Inne notatki
Aby uzyskać więcej informacji, zobacz Typy danych i limity .
Błąd czasu wykonania „9”: Indeks dolny poza zakresem
błędny kod
Sub DoSomething()
Dim foo(1 To 10)
Dim i As Long
For i = 1 To 100
foo(i) = i
Next
End Sub
Dlaczego to nie działa?
foo
to tablica zawierająca 10 elementów. Kiedy licznik pętli i
osiągnie wartość 11, foo(i)
jest poza zakresem . Ten błąd występuje, gdy dostęp do tablicy lub kolekcji jest uzyskiwany za pomocą indeksu, który nie istnieje w tej tablicy lub kolekcji.
Poprawny kod
Sub DoSomething()
Dim foo(1 To 10)
Dim i As Long
For i = LBound(foo) To UBound(foo)
foo(i) = i
Next
End Sub
Dlaczego to działa?
Użyj funkcji LBound
i UBound
aby określić odpowiednio dolną i górną granicę tablicy.
Inne notatki
Gdy indeks jest łańcuchem, np. ThisWorkbook.Worksheets("I don't exist")
, ten błąd oznacza, że podana nazwa nie istnieje w kolekcji, której dotyczy zapytanie.
Rzeczywisty błąd zależy jednak od implementacji; Collection
spowoduje zgłoszenie błędu czasu wykonania 5 „Niepoprawne wywołanie procedury lub argument”:
Sub RaisesRunTimeError5()
Dim foo As New Collection
foo.Add "foo", "foo"
Debug.Print foo("bar")
End Sub
Błąd w czasie wykonywania „13”: Niezgodność typu
błędny kod
Public Sub DoSomething()
DoSomethingElse "42?"
End Sub
Private Sub DoSomethingElse(foo As Date)
' Debug.Print MonthName(Month(foo))
End Sub
Dlaczego to nie działa?
VBA próbuje naprawdę przekonwertować "42?"
argument na wartość Date
. Gdy się nie powiedzie, wywołanie DoSomethingElse
nie może zostać wykonane, ponieważ VBA nie wie, która data ma zostać przekazana, dlatego podnosi niezgodność typu błędu 13 w czasie wykonywania, ponieważ typ argumentu nie pasuje do oczekiwanego typu (i może też nie może być pośrednio przekonwertowany).
Poprawny kod
Public Sub DoSomething()
DoSomethingElse Now
End Sub
Private Sub DoSomethingElse(foo As Date)
' Debug.Print MonthName(Month(foo))
End Sub
Dlaczego to działa?
Przekazując argument Date
do procedury, która oczekuje parametru Date
, wywołanie może się powieść.
Błąd czasu wykonania „91”: zmienna obiektowa lub zmienna blokowa nie została ustawiona
błędny kod
Sub DoSomething()
Dim foo As Collection
With foo
.Add "ABC"
.Add "XYZ"
End With
End Sub
Dlaczego to nie działa?
Zmienne obiektowe zawierają odniesienie , a odniesienia należy ustawić za pomocą słowa kluczowego Set
. Ten błąd występuje za każdym razem, gdy wywołanie elementu jest wykonywane na obiekcie, do którego odwołanie ma wartość Nothing
. W tym przypadku foo
jest odwołaniem do Collection
, ale nie zostało zainicjowane, więc odwołanie Nothing
zawiera Nothing
- i nie możemy wywoływać .Add
na Nothing
.
Poprawny kod
Sub DoSomething()
Dim foo As Collection
Set foo = New Collection
With foo
.Add "ABC"
.Add "XYZ"
End With
End Sub
Dlaczego to działa?
Przypisując zmienną obiektową prawidłowe odwołanie za pomocą słowa kluczowego Set
, wywołania .Add
powodzeniem.
Inne notatki
Często funkcja lub właściwość może zwrócić odwołanie do obiektu - częstym przykładem jest metoda Excel Range.Find
, która zwraca obiekt Range
:
Dim resultRow As Long
resultRow = SomeSheet.Cells.Find("Something").Row
Jednak funkcja może bardzo dobrze zwracać Nothing
(jeśli nie znaleziono .Row
terminu), więc prawdopodobne jest, że połączenie łańcuchowe członka .Row
nie powiedzie się.
Przed wywołaniem elementów obiektu sprawdź, czy odwołanie jest ustawione z warunkiem If Not xxxx Is Nothing
:
Dim result As Range
Set result = SomeSheet.Cells.Find("Something")
Dim resultRow As Long
If Not result Is Nothing Then resultRow = result.Row
Błąd w czasie wykonywania „20”: Wznów bez błędu
błędny kod
Sub DoSomething()
On Error GoTo CleanFail
DoSomethingElse
CleanFail:
Debug.Print Err.Number
Resume Next
End Sub
Dlaczego to nie działa?
Jeśli procedura DoSomethingElse
CleanFail
błąd, wykonanie przeskakuje do etykiety linii CleanFail
, drukuje numer błędu, a instrukcja Resume Next
przeskakuje z powrotem do instrukcji bezpośrednio po linii, w której wystąpił błąd, w tym przypadku jest to Debug.Print
instrukcja: podprogram obsługi błędów jest wykonywany bez kontekstu błędów, a po osiągnięciu instrukcji Resume Next
pojawia się błąd 20 w czasie wykonywania, ponieważ nie ma gdzie wznowić.
Poprawny kod
Sub DoSomething()
On Error GoTo CleanFail
DoSomethingElse
Exit Sub
CleanFail:
Debug.Print Err.Number
Resume Next
End Sub
Dlaczego to działa?
Wprowadzając instrukcję Exit Sub
przed CleanFail
linii CleanFail
, oddzieliliśmy podprogram obsługi błędów CleanFail
od reszty treści procedury - jedynym sposobem wykonania podprogramu obsługi On Error
skok po On Error
; dlatego żadna ścieżka wykonania nie dociera do instrukcji Resume
poza kontekstem błędu, co pozwala uniknąć błędu w czasie wykonywania 20.
Inne notatki
Jest to bardzo podobne do błędu w czasie wykonywania „3”: Return bez GoSub ; w obu sytuacjach rozwiązaniem jest upewnienie się, że normalna ścieżka wykonania nie może wejść do podprogramu (identyfikowanego przez etykietę linii) bez wyraźnego skoku (zakładając, że On Error GoTo
jest uważany za skok jawny ).