VBA
Ошибки времени выполнения VBA
Поиск…
Вступление
Код, который компилирует, может по-прежнему сталкиваться с ошибками во время выполнения. В этом разделе перечислены наиболее распространенные из них, их причины и способы их устранения.
Ошибка времени выполнения «3»: возврат без GoSub
Неверный код
Sub DoSomething()
GoSub DoThis
DoThis:
Debug.Print "Hi!"
Return
End Sub
Почему это не работает?
Выполнение входит в процедуру DoSomething
, переходит на метку DoThis
, печатает «Привет!». на выход отладки, возвращается к инструкции сразу после вызова GoSub
, печатает «Привет!». снова, а затем встречает оператор Return
, но до сих пор некуда возвращаться , потому что мы не попали сюда с инструкцией GoSub
.
Правильный код
Sub DoSomething()
GoSub DoThis
Exit Sub
DoThis:
Debug.Print "Hi!"
Return
End Sub
Почему это работает?
Представляя инструкцию Exit Sub
перед DoThis
линии DoThis
, мы DoThis
подпрограмму DoThis
от остальной части тела процедуры - единственный способ выполнить подпрограмму DoThis
- через прыжок GoSub
.
Другие примечания
GoSub
/ Return
устарел, и его следует избегать в пользу фактических вызовов процедур. Процедура не должна содержать подпрограмм, кроме обработчиков ошибок.
Это очень похоже на ошибку времени выполнения «20»: возобновить без ошибок ; в обеих ситуациях решение состоит в том, чтобы гарантировать, что нормальный путь выполнения не может войти в подпрограмму (обозначенную меткой строки) без явного перехода (предполагая, что On Error GoTo
считается явным скачком ).
Ошибка времени выполнения «6»: переполнение
неверный код
Sub DoSomething()
Dim row As Integer
For row = 1 To 100000
'do stuff
Next
End Sub
Почему это не работает?
Тип данных Integer
- это 16-разрядное целое число со знаком с максимальным значением 32 767; присваивая его чему-либо большему, чем это приведет к переполнению типа и повышению этой ошибки.
Правильный код
Sub DoSomething()
Dim row As Long
For row = 1 To 100000
'do stuff
Next
End Sub
Почему это работает?
Вместо этого вместо этого используется Long
(32-разрядное) целое число, теперь мы можем создать цикл, который выполняет итерацию более 32 767 раз, не переполняя тип переменной счетчика.
Другие примечания
Дополнительные сведения см. В разделе Типы данных и лимиты .
Ошибка времени выполнения «9»: подстрочный индекс
неверный код
Sub DoSomething()
Dim foo(1 To 10)
Dim i As Long
For i = 1 To 100
foo(i) = i
Next
End Sub
Почему это не работает?
foo
- массив, содержащий 10 элементов. Когда счетчик циклов i
достигает значения 11, foo(i)
выходит за пределы диапазона . Эта ошибка возникает всякий раз, когда доступ к массиву или коллекции осуществляется с индексом, который не существует в этом массиве или коллекции.
Правильный код
Sub DoSomething()
Dim foo(1 To 10)
Dim i As Long
For i = LBound(foo) To UBound(foo)
foo(i) = i
Next
End Sub
Почему это работает?
Используйте функции LBound
и UBound
для определения нижней и верхней границ массива, соответственно.
Другие примечания
Когда индекс является строкой, например ThisWorkbook.Worksheets("I don't exist")
, эта ошибка означает, что предоставленное имя не существует в запрошенной коллекции.
Фактическая ошибка является специфичной для реализации; Collection
будет повышена ошибка во время выполнения 5 «Неверный вызов или аргумент процедуры»:
Sub RaisesRunTimeError5()
Dim foo As New Collection
foo.Add "foo", "foo"
Debug.Print foo("bar")
End Sub
Ошибка времени выполнения «13»: несоответствие типов
неверный код
Public Sub DoSomething()
DoSomethingElse "42?"
End Sub
Private Sub DoSomethingElse(foo As Date)
' Debug.Print MonthName(Month(foo))
End Sub
Почему это не работает?
VBA пытается очень тяжело преобразовать "42?"
аргумент в значение Date
. Когда он терпит неудачу, вызов DoSomethingElse
не может быть выполнен, потому что VBA не знает, какую дату передать, поэтому он вызывает несоответствие типа ошибки 13 во время выполнения, поскольку тип аргумента не соответствует ожидаемому типу (и может 'также неявно преобразуется).
Правильный код
Public Sub DoSomething()
DoSomethingElse Now
End Sub
Private Sub DoSomethingElse(foo As Date)
' Debug.Print MonthName(Month(foo))
End Sub
Почему это работает?
Date
аргумент Date
процедуре, которая ожидает параметр Date
, вызов может быть успешным.
Ошибка времени выполнения '91': переменная объекта или с не заданной переменной блока
неверный код
Sub DoSomething()
Dim foo As Collection
With foo
.Add "ABC"
.Add "XYZ"
End With
End Sub
Почему это не работает?
Переменные объекта содержат ссылку , а ссылки должны быть заданы с помощью ключевого слова « Set
. Эта ошибка возникает , когда вызов элемента выполнен на объекте, ссылка не является Nothing
. В этом случае foo
является ссылкой Collection
, но он не инициализирован, поэтому ссылка содержит Nothing
- и мы не можем вызвать .Add
on Nothing
.
Правильный код
Sub DoSomething()
Dim foo As Collection
Set foo = New Collection
With foo
.Add "ABC"
.Add "XYZ"
End With
End Sub
Почему это работает?
.Add
объектной переменной допустимую ссылку с помощью ключевого слова Set
, вызовы .Add
успешными.
Другие примечания
Часто функция или свойство может возвращать ссылку на объект - распространенным примером является метод Excel Range.Find
, который возвращает объект Range
:
Dim resultRow As Long
resultRow = SomeSheet.Cells.Find("Something").Row
Однако функция может очень хорошо вернуть Nothing
(если поисковый .Row
не найден), поэтому вполне вероятно, что .Row
вызова .Row
не выполняется.
Перед вызовом членов объекта убедитесь, что ссылка задана с условием 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
Ошибка времени выполнения «20»: возобновить без ошибок
неверный код
Sub DoSomething()
On Error GoTo CleanFail
DoSomethingElse
CleanFail:
Debug.Print Err.Number
Resume Next
End Sub
Почему это не работает?
Если процедура DoSomethingElse
вызывает ошибку, выполнение переходит к CleanFail
линии CleanFail
, печатает номер ошибки, а команда Resume Next
возвращается к инструкции, которая сразу же следует за строкой, в которой произошла ошибка, которая в этом случае является Debug.Print
инструкция: подпрограмма обработки ошибок выполняется без контекста ошибки, и когда достигается команда Resume Next
, возникает ошибка 20 времени выполнения, потому что возобновить ее некуда.
Правильный код
Sub DoSomething()
On Error GoTo CleanFail
DoSomethingElse
Exit Sub
CleanFail:
Debug.Print Err.Number
Resume Next
End Sub
Почему это работает?
Представляя инструкцию Exit Sub
перед CleanFail
линии CleanFail
, мы CleanFail
подпрограмму обработки ошибок CleanFail
от остальной части тела процедуры - единственный способ выполнить подпрограмму обработки ошибок - с помощью скачка On Error
; поэтому путь выполнения не доходит до инструкции Resume
за пределами контекста ошибки, что позволяет избежать ошибки времени выполнения 20.
Другие примечания
Это очень похоже на ошибку времени выполнения «3»: возврат без GoSub ; в обеих ситуациях решение состоит в том, чтобы гарантировать, что нормальный путь выполнения не может войти в подпрограмму (обозначенную меткой строки) без явного перехода (предполагая, что On Error GoTo
считается явным скачком ).