Хороші зразки для обробки помилок VBA


74

Які хороші моделі для обробки помилок у VBA?

Зокрема, що мені робити в цій ситуації:

... some code ...
... some code where an error might occur ...
... some code ...
... some other code where a different error might occur ...
... some other code ...
... some code that must always be run (like a finally block) ...

Я хочу обробляти як помилки, так і відновити виконання після коду, де помилка може статися. Крім того, нарешті код в кінці повинен ЗАВЖДИ запускатися - незалежно від того, які винятки були раніше. Як я можу досягти такого результату?

Відповіді:


101

Обробка помилок у VBA


  • On Error Goto ErrorHandlerLabel
  • Resume( Next| ErrorHandlerLabel )
  • On Error Goto 0 (вимикає поточний обробник помилок)
  • Err об'єкт

У Errвластивості об'єкта , як правило , скидається в нуль або рядок нульової довжини в підпрограмі обробки помилок, але вона також може бути зроблена явно з Err.Clear.

Помилки в процедурі обробки помилок припиняються.

Діапазон 513-65535 доступний для помилок користувача. Для помилок власного класу ви додаєте vbObjectErrorномер помилки. Див. Документацію про MS Err.Raiseта список номерів помилок .

Для нереалізованих членів інтерфейсу у похідному класі слід використовувати константу E_NOTIMPL = &H80004001.


Option Explicit

Sub HandleError()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0

  Debug.Print "This line won't be executed."

DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub RaiseAndHandleError()
  On Error GoTo errMyErrorHandler
    ' The range 513-65535 is available for user errors.
    ' For class errors, you add vbObjectError to the error number.
    Err.Raise vbObjectError + 513, "Module1::Test()", "My custom error."
  On Error GoTo 0

  Debug.Print "This line will be executed."

Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
  Err.Clear
Resume Next
End Sub

Sub FailInErrorHandler()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0

  Debug.Print "This line won't be executed."

DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  a = 7 / 0 ' <== Terminating error!
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub DontDoThis()

  ' Any error will go unnoticed!
  On Error Resume Next
  ' Some complex code that fails here.
End Sub

Sub DoThisIfYouMust()

  On Error Resume Next
  ' Some code that can fail but you don't care.
  On Error GoTo 0

  ' More code here
End Sub

1
це чудово, але чи є місце, де перелічені всі помилки, щоб я міг знати, чи існує моя раніше, чи мені потрібно її створити?
PsychoData

3
@PsychoData, ось список кодів помилок support.microsoft.com/kb/146864
Еліас,

Як слід змінити наведений вище код, щоб увійти до входу та кожного виходу викликаного коду (процедура, функція, метод тощо)?
Олексій Ф.

36

Я б також додав:

  • Глобальний Err об'єкт є найближчим до вас об'єктом виключення
  • Ви можете ефективно "кинути виняток" за допомогою Err.Raise

І просто для розваги:

  • On Error Resume Next є втіленим дияволом, якого слід уникати, оскільки він мовчки приховує помилки

11
+1 для попередження про On Eror Resume Next. Ймовірно, одна з причин номер один, чому програми VB, як правило, такі повні помилок.
Makis

14
Неправда. При правильному використанні При помилці Відновити Далі - це еквівалент спроби / лову. Правильне використання просто вимагає перевірки або збереження стану помилки після кожного рядка . Це робить складну перевірку помилок набагато менш детальною. ОДНО, неправильно використане, застосовується все вищезазначене.
Ben McIntyre

3
Думаю, усі погодяться, що On Error - це еквівалент Try / Catch так ... але щодо Error Resume Next? Це спричиняє зникнення всіх помилок - у тому числі тих, яких ми ніколи не передбачали. Краще дозволити помилкам випливати, аніж тисками тижнями, чому відбувається щось дивне [це траплялося зі мною під час налагодження чужого коду]. Я використовую його лише в дуже особливих обставинах, у вузьких дрібних функціях, коли якось дивно змушує вас помилитися (наприклад, чи існує цей предмет у колекції).
Джоел Гудвін,

2
Якщо ви помістите занадто багато коду в errMyErrorHandler: ви ризикуєте помилкою, яка трапиться у вашому обробнику помилок, що створює нескінченний цикл. Якщо ви ставите On Error Resume Next перед тим, як обробити помилку в errMyErrorHandler, вона скидає об'єкт Err, і ви втрачаєте інформацію про помилку. Я переміщую свою обробку помилок у підкатегорію і передаю err.num та опис як параметри, щоб потім я міг використовувати On Error Resume Next, коли я скидаю все, наприклад, оновлення екрана, курсор тощо, і показую помилку, використовуючи значення параметра ...Call mdl_val.usr_sub_handle_error(Err.Source, Err.Description)
DWiener

2
"якого слід уникати" - це не зовсім так. Є багато випадків, що вимагають On Error Resume Next. Загальним принципом цих випадків є повернення деяких результатів шляхом видалення винятку. Найчастіше це доступ до Collectionоб'єкта за допомогою рядкового ключа: у цьому випадку абонент не може знати, чи є в Collectionоб'єкті елемент із цим ключем .
Олексій Ф.

18

Тож ти міг зробити щось подібне

Function Errorthingy(pParam)
On Error GoTo HandleErr

 ' your code here

    ExitHere:
    ' your finally code
    Exit Function

    HandleErr:
        Select Case Err.Number
        ' different error handling here'
        Case Else
            MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "ErrorThingy"
        End Select


   Resume ExitHere

End Function

Якщо ви хочете випікати в спеціальних винятках. (наприклад, ті, що порушують бізнес-правила) використовують приклад вище, але використовують goto, щоб змінити потік методу за необхідності.


1
Це майже те, як ми обробляли помилки у великій програмі VB6 ще в ті часи. Працював відносно добре і був простим у користуванні. IIRC, у нас був клас обробки помилок, який викликали замість того, щоб мати код помилки у функції. Так було набагато легше змінити і поведінку.
Makis

Зазвичай рекомендується поставити "On Error GoTo 0" після блоку коду там, де потрібно обробляти помилки. Крім того, будь-яка помилка в коді обробки помилок припиняється.
guillermooo

4
Не знаю, чи це ідіоматична VBA, але для розробників .NET, якщо ви перейменуєте "HandleErr" на "Catch", а "ExitHere" на "Нарешті" і
скосяться

@ user1454265 ... тоді ви можете легко пропустити, Resume ExitHereщо значно впливає на дві парадигми.
AntoineL

12

Ось моя стандартна реалізація. Мені подобається, щоб ярлики були самоописовими.

Public Sub DoSomething()

    On Error GoTo Catch ' Try
    ' normal code here

    Exit Sub
Catch:

    'error code: you can get the specific error by checking Err.Number

End Sub

Або з Finallyблоком:

Public Sub DoSomething()

    On Error GoTo Catch ' Try

    ' normal code here

    GoTo Finally
Catch:

    'error code

Finally:

    'cleanup code

End Sub

1
Що станеться, якщо після цього буде застосовано виняток Finally:? Тож On Error GoTo 0відразу після, Finally:можливо, потрібно було виправити небажану рекурсію.
Олексій Ф.

2
Якщо після Finallyблоку є помилка , вона просто викине помилку. Він не повторюватиме цикл назад до Finallyблоку. (Спробуйте, ви побачите.) Якщо ви хочете обробити помилку після блоку «Нарешті», вам потрібно буде додати іншу On Error GoTo, але, можливо, з іншою міткою, наприклад Catch2. Але тут ми починаємо заглиблюватися в методологію Clean Code -> чистому методу знадобиться лише один обробник помилок (і навіть повинен мати власний виділений метод для
виявлення

1
@LimaNightHawk: Я вважаю, що те, що відбудеться після, Finally:залежить від того, введете ви його після перенаправлення на Catch:(тоді так, це просто викине) ... чи ні ! І в цьому останньому випадку, тобто проходження GoTo Finallyбуде мати дію On Error GoTo Catchдосі, тому контроль перенаправляється в бік Catch:(може бути добре), а потім Finally:вводиться повторно, можливо, не те, що ви спочатку очікували.
AntoineL

Якщо ви додасте інший On Error GoTo Catch2у Finally:коді, це буде ефективним у цьому останньому випадку, але не якщо ви проходили це Catch:раніше, тому що ні, On Error GoTo -1ні жодного Resume; додавання першого віддаляє нас настільки далеко від звичайного, try catch finallyщо можна було б подумати про припинення сумнівної аналогії до цього моменту.
AntoineL

@AntoineL Так! Погодьтеся з обома, великим спостереженням та роз'ясненнями.
LimaNightHawk

4

Професійна програма розробки Excel має досить гарну схему обробки помилок . Якщо ви збираєтеся провести будь-який час у VBA, можливо, варто взяти книгу. Існує низка областей, де бракує VBA, і ця книга має хороші поради щодо управління цими сферами.

PED описує два методи обробки помилок. Основною є система, де всі процедури точки входу є підпроцедурами, а всі інші процедури - це функції, що повертають логічні значення.

Процедура точки входу використовує оператори On Error, щоб фіксувати помилки майже так, як було розроблено. Процедури, що не мають точки входу, повертають True, якщо помилок не було, та False, якщо помилки були. Процедури, що не входять до точки входу, також використовують On Error.

Обидва типи процедур використовують центральну процедуру обробки помилок, щоб підтримувати помилку в стані та реєструвати помилку.


3

Я використовую шматок коду, який сам розробив, і це досить добре для моїх кодів:

На початку функції або підпрограми я визначаю:

On error Goto ErrorCatcher:

а потім я обробляю можливі помилки

ErrorCatcher:
Select Case Err.Number

Case 0 'exit the code when no error was raised
    On Error GoTo 0
    Exit Function
Case 1 'Error on definition of object
    'do stuff
Case... 'little description here
    'do stuff
Case Else
    Debug.Print "###ERROR"
    Debug.Print "   • Number  :", Err.Number
    Debug.Print "   • Descrip :", Err.Description
    Debug.Print "   • Source  :", Err.Source
    Debug.Print "   • HelpCont:", Err.HelpContext
    Debug.Print "   • LastDLL :", Err.LastDllError
    Stop
    Err.Clear
    Resume
End Select

3

Ось досить пристойний шаблон.

Для налагодження: Коли виникає помилка, натисніть Ctrl-Break (або Ctrl-Pause), перетягніть маркер розриву (або те, що він називається) вниз до рядка Resume, натисніть F8, і ви перейдете до рядка, який "кинув" помилка.

ExitHandler - це ваш "нарешті".

Пісочний годинник будуть вбивати щоразу. Текст рядка стану буде очищатися кожного разу.

Public Sub ErrorHandlerExample()
    Dim dbs As DAO.Database
    Dim rst As DAO.Recordset

    On Error GoTo ErrHandler
    Dim varRetVal As Variant

    Set dbs = CurrentDb
    Set rst = dbs.OpenRecordset("SomeTable", dbOpenDynaset, dbSeeChanges + dbFailOnError)

    Call DoCmd.Hourglass(True)

    'Do something with the RecordSet and close it.

    Call DoCmd.Hourglass(False)

ExitHandler:
    Set rst = Nothing
    Set dbs = Nothing
    Exit Sub

ErrHandler:
    Call DoCmd.Hourglass(False)
    Call DoCmd.SetWarnings(True)
    varRetVal = SysCmd(acSysCmdClearStatus)

    Dim errX As DAO.Error
    If Errors.Count > 1 Then
       For Each errX In DAO.Errors
          MsgBox "ODBC Error " & errX.Number & vbCrLf & errX.Description
       Next errX
    Else
        MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical
    End If

    Resume ExitHandler
    Resume

End Sub



    Select Case Err.Number
        Case 3326 'This Recordset is not updateable
            'Do something about it. Or not...
        Case Else
            MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical
    End Select

Він також фіксує як помилки DAO, так і VBA. Ви можете поставити Select Case у розділі помилок VBA, якщо ви хочете зафіксувати певні числа помилок.

Select Case Err.Number
    Case 3326 'This Recordset is not updateable
        'Do something about it. Or not...
    Case Else
        MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical
End Select

3

У наведеному нижче коді показано альтернативу, яка гарантує наявність лише однієї точки виходу для підфункції.

sub something()
    on error goto errHandler

    ' start of code
    ....
    ....
    'end of code

    ' 1. not needed but signals to any other developer that looks at this
    ' code that you are skipping over the error handler...
    ' see point 1...
    err.clear

errHandler:
    if err.number <> 0 then
        ' error handling code
    end if
end sub

3

Також важливою для обговорення є відносно невідома Erlфункція. Якщо у вашій процедурі коду є числові мітки, наприклад,

Sub AAA()
On Error Goto ErrorHandler

1000:
' code
1100:
' more code
1200:
' even more code that causes an error
1300:
' yet more code
9999: ' end of main part of procedure
ErrorHandler:
If Err.Number <> 0 Then
   Debug.Print "Error: " + CStr(Err.Number), Err.Descrption, _
      "Last Successful Line: " + CStr(Erl)
End If   
End Sub 

ErlФункція повертає останнє зустрічається Numberic ярлик лінії. У наведеному вище прикладі, якщо помилка під час виконання виникає після мітки, 1200:але раніше 1300:, Erlфункція повернеться 1200, оскільки це найбільш рецензована мітка рядка. Я вважаю за хорошу практику розміщувати мітку рядка безпосередньо над вашим блоком обробки помилок. Я зазвичай використовую9999 щоб зазначити, що основна частина прокуратури досягла очікуваного струсу.

ПРИМІТКИ:

  • Мітки рядків ПОВИННІ бути позитивними цілими числами - мітка типу MadeItHere:не визначена Erl.

  • Мітки рядків абсолютно не пов'язані з фактичними номерами рядків a VBIDE CodeModule. Ви можете використовувати будь-які позитивні числа, які ви хочете, у будь-якому порядку, який ви хочете. У наведеному вище прикладі код містить лише 25 рядків, але номера міток рядків починаються з 1000. Немає зв’язку між номерами рядків редактора та номерами міток рядків, що використовуються з Erl.

  • Номери рядкових міток не повинні бути в якомусь конкретному порядку, хоча, якщо вони не в порядку зростання, зверху вниз, ефективність та користь від Erlцього значно зменшуються, але Erlвсе одно повідомлятимуть правильний номер.

  • Мітки рядків специфічні для процедури, в якій вони з'являються. Якщо процедура ProcAвикликає процедуру ProcBі виникає помилка, ProcBяка повертає керування назад до ProcA, Erl(in ProcA) поверне номер останньої позначеної мітки рядка, ProcAперш ніж він викличе ProcB. Зсередини ProcAви не можете отримати номери міток рядків, які можуть відображатися в ProcB.

Будьте обережні, розміщуючи мітки номерів рядків у циклі. Наприклад,

For X = 1 To 100
500:
' some code that causes an error
600:
Next X

Якщо код, наступний за міткою рядка, 500але раніше 600викликає помилку, і ця помилка виникає на 20-ій ітерації циклу, Erlповернеться 500, навіть незважаючи на те, 600що була успішно зустрічана за попередні 19 взаємодій циклу.

Правильне розміщення міток рядків у процедурі має вирішальне значення для використання Erlфункції для отримання справді значущої інформації.

У мережі є будь-яка безкоштовна утиліта, яка автоматично вставляє мітку числових рядків у процедуру, тому ви маєте детальну інформацію про помилки під час розробки та налагодження, а потім видаляйте ці мітки після того, як код з’явиться.

Якщо ваш код відображає інформацію про помилку кінцевому користувачеві, якщо трапляється несподівана помилка, надання значення Erlв цій інформації може спростити пошук та виправлення проблеми НАДРІЗНО, ніж якщо значення Erlне повідомляється.


3

Я вважаю, що найкраще працює наступне, що називається центральним підходом до обробки помилок.

Переваги

У вас є 2 режими запуску програми: налагодження та виробництво . У режимі налагодження код зупиниться при будь-якій несподіваній помилці і дозволить вам легко налагодити, перескочивши до рядка, де це сталося, двічі натиснувши клавішу F8. У виробничому режимі користувачеві відображатиметься значуще повідомлення про помилку.

Ви можете викидати навмисні помилки, як це, що зупинить виконання коду з повідомленням користувачеві:

Err.Raise vbObjectError, gsNO_DEBUG, "Some meaningful error message to the user"

Err.Raise vbObjectError, gsUSER_MESSAGE, "Some meaningful non-error message to the user"

'Or to exit in the middle of a call stack without a message:
Err.Raise vbObjectError, gsSILENT

Впровадження

Вам потрібно «обернути» всі підпрограми та функції будь-якою значною кількістю коду наступними колонтитулами та колонтитулами, обов’язково вказавши ehCallTypeEntryPointвсі точки входу. Зверніть увагу також на msModuleконстанту, яку потрібно помістити у всі модулі.

Option Explicit
Const msModule As String = "<Your Module Name>"

' This is an entry point 
Public Sub AnEntryPoint()
    Const sSOURCE As String = "AnEntryPoint"
    On Error GoTo ErrorHandler

    'Your code

ErrorExit:
    Exit Sub

ErrorHandler:
    If CentralErrorHandler(Err, ThisWorkbook, msModule, sSOURCE, ehCallTypeEntryPoint) Then
        Stop
        Resume
    Else
        Resume ErrorExit
    End If
End Sub

' This is any other subroutine or function that isn't an entry point
Sub AnyOtherSub()
    Const sSOURCE As String = "AnyOtherSub"
    On Error GoTo ErrorHandler

    'Your code

ErrorExit:
    Exit Sub

ErrorHandler:
    If CentralErrorHandler(Err, ThisWorkbook, msModule, sSOURCE) Then
        Stop
        Resume
    Else
        Resume ErrorExit
    End If
End Sub

Вміст центрального модуля обробки помилок такий:

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Comments: Error handler code.
'
'           Run SetDebugMode True to use debug mode (Dev mode)
'           It will be False by default (Production mode)
'
' Author:   Igor Popov
' Date:     13 Feb 2014
' Licence:  MIT
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit
Option Private Module

Private Const msModule As String = "MErrorHandler"

Public Const gsAPP_NAME As String = "<You Application Name>"

Public Const gsSILENT As String = "UserCancel"  'A silent error is when the user aborts an action, no message should be displayed
Public Const gsNO_DEBUG As String = "NoDebug"   'This type of error will display a specific message to the user in situation of an expected (provided-for) error.
Public Const gsUSER_MESSAGE As String = "UserMessage" 'Use this type of error to display an information message to the user

Private Const msDEBUG_MODE_COMPANY = "<Your Company>"
Private Const msDEBUG_MODE_SECTION = "<Your Team>"
Private Const msDEBUG_MODE_VALUE = "DEBUG_MODE"

Public Enum ECallType
    ehCallTypeRegular = 0
    ehCallTypeEntryPoint
End Enum

Public Function DebugMode() As Boolean
    DebugMode = CBool(GetSetting(msDEBUG_MODE_COMPANY, msDEBUG_MODE_SECTION, msDEBUG_MODE_VALUE, 0))
End Function

Public Sub SetDebugMode(Optional bMode As Boolean = True)
    SaveSetting msDEBUG_MODE_COMPANY, msDEBUG_MODE_SECTION, msDEBUG_MODE_VALUE, IIf(bMode, 1, 0)
End Sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Comments: The central error handler for all functions
'           Displays errors to the user at the entry point level, or, if we're below the entry point, rethrows it upwards until the entry point is reached
'
'           Returns True to stop and debug unexpected errors in debug mode.
'
'           The function can be enhanced to log errors.
'
' Date          Developer           TDID    Comment
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 13 Feb 2014   Igor Popov                  Created

Public Function CentralErrorHandler(ErrObj As ErrObject, Wbk As Workbook, ByVal sModule As String, ByVal sSOURCE As String, _
                                    Optional enCallType As ECallType = ehCallTypeRegular, Optional ByVal bRethrowError As Boolean = True) As Boolean

    Static ssModule As String, ssSource As String
    If Len(ssModule) = 0 And Len(ssSource) = 0 Then
        'Remember the module and the source of the first call to CentralErrorHandler
        ssModule = sModule
        ssSource = sSOURCE
    End If
    CentralErrorHandler = DebugMode And ErrObj.Source <> gsNO_DEBUG And ErrObj.Source <> gsUSER_MESSAGE And ErrObj.Source <> gsSILENT
    If CentralErrorHandler Then
        'If it's an unexpected error and we're going to stop in the debug mode, just write the error message to the immediate window for debugging
        Debug.Print "#Err: " & Err.Description
    ElseIf enCallType = ehCallTypeEntryPoint Then
        'If we have reached the entry point and it's not a silent error, display the message to the user in an error box
        If ErrObj.Source <> gsSILENT Then
            Dim sMsg As String: sMsg = ErrObj.Description
            If ErrObj.Source <> gsNO_DEBUG And ErrObj.Source <> gsUSER_MESSAGE Then sMsg = "Unexpected VBA error in workbook '" & Wbk.Name & "', module '" & ssModule & "', call '" & ssSource & "':" & vbCrLf & vbCrLf & sMsg
            MsgBox sMsg, vbOKOnly + IIf(ErrObj.Source = gsUSER_MESSAGE, vbInformation, vbCritical), gsAPP_NAME
        End If
    ElseIf bRethrowError Then
        'Rethrow the error to the next level up if bRethrowError is True (by Default).
        'Otherwise, do nothing as the calling function must be having special logic for handling errors.
        Err.Raise ErrObj.Number, ErrObj.Source, ErrObj.Description
    End If
End Function

Щоб перейти в режим налагодження , у вікні негайного запустіть наступне:

SetDebugMode True

2

Остерігайтеся слонової пастки:

Я не бачив жодної згадки про це в цій дискусії. [Access 2010]

Як ACCESS / VBA обробляє помилки в об’єктах CLASS, визначається налаштованим параметром:

Редактор коду VBA> Інструменти> Параметри> Загальне> Відстеження помилок:

введіть тут опис зображення


1

Мій особистий погляд на заяву, зроблену раніше в цій темі:

І просто для розваги:

У розділі "Помилка відновлення" наступним є втілений диявол, якого слід уникати, оскільки він мовчки приховує помилки.

Я використовую On Error Resume Nextпроцедури on, де я не хочу, щоб помилка зупиняла мою роботу і де будь-яке твердження не залежить від результату попередніх тверджень.

Коли я роблю це, я додаю глобальну змінну debugModeOnі встановлюю для неї значення True. Тоді я використовую це таким чином:

If not debugModeOn Then On Error Resume Next

Коли я доставляю свою роботу, я встановлюю для змінної значення false, таким чином приховуючи помилки лише для користувача та показуючи їх під час тестування.

Також використовуючи його, роблячи щось, що може призвести до збою, як-от виклик DataBodyRange ListObject, який може бути порожнім:

On Error Resume Next
Sheet1.ListObjects(1).DataBodyRange.Delete
On Error Goto 0

Замість:

If Sheet1.ListObjects(1).ListRows.Count > 0 Then 
    Sheet1.ListObjects(1).DataBodyRange.Delete
End If

Або перевірка наявності предмета в колекції:

On Error Resume Next
Err.Clear
Set auxiliarVar = collection(key)

' Check existence (if you try to retrieve a nonexistant key you get error number 5)
exists = (Err.Number <> 5)

> If not debugModeOn Then On Error Resume NextУ цьому випадку краще використовувати умовну компіляцію, як #If Hide_Errors > 0 Then On Error Resume Nextі встановити Hide_Errorsу властивостях проекту VBA Conditional Complication Argumentsвідповідно.
Олексій Ф.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.