#excel #vba #error-handling #custom-errors #custom-error-handling
#excel #vba #обработка ошибок #пользовательские ошибки #пользовательская обработка ошибок
Вопрос:
Я пытаюсь создать централизованный обработчик ошибок. Я использую vbObjectError в соответствии с рекомендациями. Но я не могу понять, как составить условие, которое может определить, является ли моя ошибка родной или пользовательской.
Желаемый IsCustomErr должен работать без изменений, даже если я позже добавлю дополнительные пользовательские ошибки в перечисление.
Enum CustomEr
LaidEgg = vbObjectError
UserEaten
Paused
Cancelled
End Enum
Sub Test
On Error Goto HANDLER
Err.Raise LaidEgg
Exit Sub
HANDLER:
GlobalHandler
End Sub
Sub GlobalHandler
If IsCustomErr Then MsgBox "Custom"
End Sub
Function IsCustomErr()As Boolean
' ONE OF THESE?
With Err
IsCustomErr = .Number < 0
IsCustomErr = .Number >= vbObjectError
IsCustomErr = (.Number >= vbObjectError) And (.Number < 0)
IsCustomErr = .Number Or vbObjectError
IsCustomErr = TwosComplement(.Number) Or TwosComplement(vbObjectError)
End With
End Function
Комментарии:
1. Может быть полезно добавить пример того, как вы создаете свои пользовательские ошибки
2. @TimWilliams готово.
3. Почему бы и нет
IsCustomErr = (.Number = CustomEr.LaidEgg)
?4. @TimWilliams Спасибо, но вы жестко кодируете только одну ошибку в свой код. Функция должна работать со всеми пользовательскими ошибками (даже если позже будут добавлены новые).
5. Хорошо, тогда, возможно, проверьте здесь в разделе «перечисление перечисления VBA» — analystcave.com/vba-enum-using-enumerations-in-vba
Ответ №1:
Это кажется жизнеспособным:
Enum CustomEr
[_First]
laidegg
UserEaten
paused
Cancelled
[_Last]
End Enum
Sub Test1()
Debug.Print "Test1"
On Error GoTo HANDLER
Err.Raise vbObjectError laidegg
Exit Sub
HANDLER:
GlobalHandler
End Sub
Sub Test2()
Debug.Print "Test2"
On Error GoTo HANDLER
Debug.Print 1 / 0
Exit Sub
HANDLER:
GlobalHandler
End Sub
Sub GlobalHandler()
Debug.Print "Custom?", IsCustomErr()
End Sub
Function IsCustomErr() As Boolean
Dim v As Long
For v = CustomEr.[_First] To CustomEr.[_Last]
If (Err.Number - vbObjectError) = v Then
IsCustomErr = True
Exit For
End If
Next v
End Function
Комментарии:
1. Умно! Вы используете
[_Last}
трюк. Приятно! Я бы сказал, что это мой запасной вариант (и жизнеспособный). Но все еще надеясь на решение, которое учитывает собственные диапазоны чисел.2. @johnywhy что вы подразумеваете под собственными диапазонами чисел? вы имеете в виду, что хотите проверить, является ли
Err.Number
это одним из номеров ошибок Microsoft? В этом сообщении, которое вы связали со мной, вы уже распечатали все существующие номера ошибок. Просто установите в своем перечислении ряд чисел, которые еще не заявлены. Ответ Тима Уильямса — это именно то, что вы ищете.3. @Toddleson «Собственные диапазоны чисел?» — Microsoft создала механизм для изоляции пользовательских ошибок от собственных ошибок. «добавьте свой код ошибки в vbObjectError, чтобы убедиться, что ваш номер не противоречит встроенным номерам ошибок» tinyurl.com/ycku4t6f . Я хочу использовать это. tinyurl.com/2p8neau3 , tinyurl.com/4s57jp72 . «Просто установите в своем перечислении числа, которые не указаны». — я думаю, мы не можем предположить, что Microsoft никогда не добавит больше ошибок в VB, Excel или Windows. Кроме того, я не уверен, что мой код со списком ошибок находит все. — мне неясен собственный диапазон. Возможно, потребуется дополнение 2.
Ответ №2:
Слишком много, чтобы уместиться в качестве комментария, но это работает для меня:
Enum CustomEr
LaidEgg = vbObjectError
UserEaten
Paused
Cancelled
End Enum
Function IsCustomErr() As Boolean
IsCustomErr = Err.Number >= CustomEr.LaidEgg And Err.Number <= CustomEr.Cancelled
End Function
Sub Example()
On Error Resume Next
Err.Raise CustomEr.UserEaten
Debug.Print IsCustomErr
'True
Err.Clear
Err.Raise 1
Debug.Print IsCustomErr
'False
End Sub
Возможной причиной того, что у вас это не работает, может быть то, что вы завершаете выполнение перед переходом к обработчику. Из статьи об объекте Err:
Свойства объекта Err сбрасываются до нуля или строк нулевой длины («») после команды Exit Sub, Exit Function, Exit Property или Resume Next в процедуре обработки ошибок.
Еще одна потенциальная проблема заключается в том, что vbObjectError -2147221504
таков, что, проверяя Err.Number >= vbObjectError
, вы будете возвращать True
для каждого числа, большего, чем vbObjectError
в основном все из них.
Комментарии:
1. Спасибо! но моя ошибка не устраняется. Проблема не в этом. Я не заканчиваю выполнение перед обработчиком.
2. Пожалуйста, попробуйте с перечислением, как в моем примере, и протестируйте с одной из пользовательских ошибок, отличной от той, которая равна vbObjectError.
3. @johnywhy я обновил свой ответ, чтобы использовать ваше перечисление. Я все еще могу правильно вернуть
True
иFalse
. Возможно, есть проблема с кодом, который создает ошибку, добавьте это в свой пост, и я постараюсь найти любые проблемы.4. Умный обходной путь, но если я позже решу добавить дополнительные пользовательские ошибки, ваш код сломается. Вы предоставляете
IsCustomErr
знания, которых у него не было бы.>= CustomEr.LaidEgg And Err.Number <= CustomEr.Cancelled
требуется, чтобыIsCustomErr
вы знали последний номер пользовательской ошибки. Он не может знать о последнем, поскольку последний может измениться.5. @johnywhy Да, я не смог придумать хорошего решения для приведения значений к значениям в Перечислении, не ссылаясь на Перечисление. Я полагаю, вы могли бы смело сказать что-то вроде «Больше или равно
LaidEgg
и меньшеLaidEgg 1000
«, что означало бы, что вы можете добавить еще много значений в перечисление без необходимости редактировать функцию.