VBA: как определить, использовался ли vbObjectError?

#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 «, что означало бы, что вы можете добавить еще много значений в перечисление без необходимости редактировать функцию.