수색…


오류 조건 방지

런타임 오류가 발생하면 좋은 코드가이를 처리해야합니다. 가장 좋은 오류 처리 전략은 오류 조건을 검사하고 런타임 오류를 발생시키는 코드 실행을 피하는 코드를 작성하는 것입니다.

런타임 오류를 줄이기위한 핵심 요소 중 하나는 작은 작업을 작성하는 입니다. 프로 시저가 실패해야하는 이유가 적을수록 코드 전체가 디버그되기 쉬워집니다.


런타임 오류 91 방지 - 객체 또는 With 블록 변수가 설정되지 않음 :

이 오류는 참조가 지정되기 전에 오브젝트가 사용될 때 발생합니다. 객체 매개 변수를받는 프로 시저가있을 수 있습니다.

Private Sub DoSomething(ByVal target As Worksheet)
    Debug.Print target.Name
End Sub

target 에 참조가 할당되지 않은 경우 위 코드는 객체에 실제 객체 참조가 있는지 확인하여 쉽게 피할 수있는 오류가 발생합니다.

Private Sub DoSomething(ByVal target As Worksheet)
    If target Is Nothing Then Exit Sub
    Debug.Print target.Name
End Sub

target 에 참조가 지정되지 않으면 할당되지 않은 참조가 사용되지 않으며 오류가 발생하지 않습니다.

하나 이상의 매개 변수가 유효하지 않은 경우 프로 시저를 조기에 종료하는 이런 방법을 보호 절 이라고합니다.


런타임 오류 방지 9 - 아래 첨자가 범위를 벗어남 :

이 오류는 배열 외부로 액세스 할 때 발생합니다.

Private Sub DoSomething(ByVal index As Integer)
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

ActiveWorkbook 에있는 워크 시트 수보다 큰 인덱스가 있으면 위의 코드는 런타임 오류를 발생시킵니다. 간단한 가드 절은 다음을 피할 수 있습니다 :

Private Sub DoSomething(ByVal index As Integer)
    If index > ActiveWorkbook.Worksheets.Count Or index <= 0 Then Exit Sub
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

대부분의 런타임 오류는 우리가 사용 하기 전에 사용하고있는 값을 신중하게 검증하고 간단한 If 문을 사용하여 다른 실행 경로를 따라 분기함으로써 피할 수 있습니다. 가젯은 프로 ​​시저의 매개 변수를 검증하거나 프로 시저의 매개 변수를 검증하는 가드 절 또는 큰 절차의 시체.

On Error 문

심지어 가드 조항으로, 하나는 현실적으로 항상 프로 시저의 몸에서 제기 될 수있는 모든 가능한 오류 조건을 설명 할 수 없다. On Error GoTo 문은 런타임시 예기치 않은 오류가 발생할 때마다 VBA가 줄 레이블 로 점프하고 "오류 처리 모드"를 입력하도록 지시합니다. 오류를 처리 한 후 Resume 키워드를 사용하여 코드를 "정상"실행으로 다시 시작할 수 있습니다.

줄 레이블은 서브 루틴을 나타냅니다. 서브 루틴은 레거시 BASIC 코드에서 시작하고 GoToGoSub 점프 및 Return 문을 사용하여 "main"루틴으로 Return 때문에 엄격하게 구조화되지 않은 경우에는 따르기 힘든 스파게티 코드를 작성 하는 것이 매우 쉽습니다. . 이런 이유로, 그것은 그것의 최상이다 :

  • 프로 시저에는 하나의 오류 처리 서브 루틴 만 있습니다.
  • 오류 처리 서브 루틴 은 오류 상태에서만 실행됩니다.

즉, 오류를 처리하는 프로 시저를 다음과 같이 구조화해야합니다.

Private Sub DoSomething()
    On Error GoTo CleanFail

    'procedure code here

CleanExit:
    'cleanup code here
    Exit Sub

CleanFail:
    'error-handling code here
    Resume CleanExit
End Sub

오류 처리 전략

때로는 다른 작업으로 여러 가지 오류를 처리하려고합니다. 이 경우 발생 된 오류에 대한 정보가 포함 된 전역 Err 개체를 검사하여 그에 따라 작동합니다.

CleanExit:
    Exit Sub

CleanFail:
    Select Case Err.Number
        Case 9
            MsgBox "Specified number doesn't exist. Please try again.", vbExclamation
            Resume
        Case 91
            'woah there, this shouldn't be happening.
            Stop 'execution will break here
            Resume 'hit F8 to jump to the line that raised the error
        Case Else
            MsgBox "An unexpected error has occurred:" & vbNewLine & Err.Description, vbCritical
            Resume CleanExit
    End Select
End Sub

일반적인 지침으로 전체 서브 루틴 또는 함수에 대한 오류 처리를 켜고 해당 범위 내에서 발생할 수있는 모든 오류를 처리하는 것이 좋습니다. 코드의 작은 섹션 섹션에서만 오류를 처리해야하는 경우 오류 처리를 같은 수준으로 설정 및 해제합니다.

Private Sub DoSomething(CheckValue as Long)

    If CheckValue = 0 Then
        On Error GoTo ErrorHandler   ' turn error handling on
        ' code that may result in error
        On Error GoTo 0              ' turn error handling off - same level
    End If

CleanExit:
    Exit Sub

ErrorHandler:
    ' error handling code here
    ' do not turn off error handling here
    Resume

End Sub

줄 번호

VBA는 레거시 스타일 (예 : QBASIC) 줄 번호를 지원합니다. Erl 숨김 속성을 사용하여 마지막 오류가 발생한 줄 번호를 식별 할 수 있습니다. 줄 번호를 사용하지 않으면 Erl 은 0 만 반환합니다.

Sub DoSomething()
10 On Error GoTo 50
20 Debug.Print 42 / 0
30 Exit Sub
40
50 Debug.Print "Error raised on line " & Erl ' returns 20
End Sub

당신이 아니라 지속적으로 줄 번호를 사용하는 경우, Erl 오류를 발생시킨 명령어 전에 마지막 행 번호를 반환합니다.

Sub DoSomething()
10 On Error GoTo 50
   Debug.Print 42 / 0
30 Exit Sub

50 Debug.Print "Error raised on line " & Erl 'returns 10
End Sub

ErlInteger 정밀도 만 가지고 있으며 자동으로 오버플로됩니다. 즉, 정수 범위를 벗어나는 행 번호는 잘못된 결과를 나타냅니다.

Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
      Debug.Print Erl   'Prints 34462
End Sub

줄 번호는 오류의 원인이 된 문구만큼 중요하지 않으며 번호 매기기 줄이 빠르게 지루하고 유지 관리가 쉽지 않습니다.

키워드 재개

오류 처리 서브 루틴은 다음 중 하나입니다.

  • 프로 시저의 끝까지 실행합니다.이 경우 호출 프로 시저에서 실행이 다시 시작됩니다.
  • Resume 키워드를 사용하여 동일한 절차 내에서 실행을 재개 하십시오.

Resume 키워드는 오류 처리 서브 루틴 내에서만 사용되어야합니다. VBA가 오류 상태에 있지 않고 Resume 을 만나면 런타임 오류 20 "오류없이 다시 시작"이 발생하기 때문입니다.

오류 처리 서브 루틴이 Resume 키워드를 사용할 수있는 몇 가지 방법이 있습니다.

  • 혼자서 Resume 하면 오류가 발생한 명령문 에서 실행이 계속 됩니다 . 오류가 실제로 처리되기 전에 처리되지 않으면 동일한 오류가 다시 발생하고 실행이 무한 루프에 들어갈 수 있습니다.
  • Resume Next 는 오류를 일으킨 명령문 바로 다음 명령문에서 계속 실행합니다. 오류가 실제로 처리되기 전에 처리되지 않으면 잠재적으로 유효하지 않은 데이터로 실행이 계속 허용되며 이로 인해 논리적 오류 및 예기치 않은 동작이 발생할 수 있습니다.
  • Resume [line label]지정된 라인 라벨 (또는 기존 스타일의 라인 번호를 사용하는 경우 라인 번호) 에서 계속 실행 됩니다 . 일반적으로 호출자에게 반환되기 전에 데이터베이스 연결이 닫히는 지 확인하는 등의 절차를 정상적으로 종료하기 전에 일부 정리 코드를 실행할 수 있습니다.

오류시 다음 재개시

On Error 문 자체는 Resume 키워드를 사용하여 VBA 런타임이 모든 오류 를 효과적으로 무시 하도록 지시 할 수 있습니다.

오류가 실제로 처리 되기 전에 처리 되지 않으면 잠재적으로 유효하지 않은 데이터로 실행이 계속 허용되며 이로 인해 논리적 오류 및 예기치 않은 동작이 발생할 수 있습니다 .

위의 강조는 충분히 강조 될 수 없습니다. On Error Resume Next 는 모든 오류를 효과적으로 무시하고 카펫 밑으로 밀어냅니다 . 유효하지 않은 입력이 주어지면 런타임 오류로 인해 불어 나오는 프로그램은 알려지지 않은 / 의도하지 않은 데이터로 계속 실행되는 프로그램보다 더 나은 프로그램입니다. 이는 버그가 훨씬 쉽게 식별되기 때문입니다. On Error Resume Next버그를 쉽게 숨길 수 있습니다.

On Error 문은 프로 시저 범위입니다 - 일반적으로 하나의 같은 하나,이 있어야 이유입니다 On Error 주어진 절차에 문.

그러나 때로는 오류 조건을 피할 수 없으며 Resume Next 에 대한 오류 처리 서브 루틴으로 점프하는 것이 옳다고 생각하지 않습니다. 이 특정 경우에는 알려진 On Error 수있는 문을 두 개의 On Error 문 사이에 래핑 할 수 있습니다.

On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0

On Error GoTo 0 명령은 현재 프로 시저에서 오류 처리를 재설정하므로 런타임 오류를 유발하는 다른 명령 은 해당 프로 시저에서 처리되지 않고 대신 활성 오류 처리기에서 캐치 될 때까지 호출 스택을 전달합니다. 호출 스택에 활성 오류 처리기가 없으면 처리되지 않은 예외로 처리됩니다.

Public Sub Caller()
    On Error GoTo Handler
    
    Callee
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Caller."
End Sub

Public Sub Callee()
    On Error GoTo Handler
    
    Err.Raise 1     'This will be handled by the Callee handler.
    On Error GoTo 0 'After this statement, errors are passed up the stack.
    Err.Raise 2     'This will be handled by the Caller handler.    
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Callee."
    Resume Next
End Sub

사용자 정의 오류

특수 클래스를 작성하는 경우 종종 고유 한 오류를 발생시키고 사용자 / 호출 코드가 이러한 사용자 정의 오류를 처리 할 수있는 확실한 방법을 원할 것입니다. 이를 달성하기위한 깔끔한 방법은 전용 Enum 형을 정의하는 것입니다.

Option Explicit
Public Enum FoobarError
    Err_FooWasNotBarred = vbObjectError + 1024
    Err_BarNotInitialized
    Err_SomethingElseHappened
End Enum

vbObjectError 기본 제공 상수를 사용하면 사용자 지정 오류 코드가 예약 된 / 기존 오류 코드와 겹치지 않도록 할 수 있습니다. 첫 번째 열거 형 값만 명시 적으로 지정해야합니다. 각 Enum 멤버의 기본 값은 이전 멤버보다 1 이므로 Err_BarNotInitialized 의 기본 값은 암시 적으로 vbObjectError + 1025 입니다.

나만의 런타임 오류 발생

런타임 오류는 Err.Raise 문을 사용하여 발생할 수 있으므로 다음과 같이 사용자 지정 Err_FooWasNotBarred 오류를 발생시킬 수 있습니다.

Err.Raise Err_FooWasNotBarred

Err.Raise 메서드는 사용자 지정 DescriptionSource 매개 변수를 사용할 수도 있습니다. 이러한 이유로 각 사용자 지정 오류 설명을 보관하는 상수를 정의하는 것이 좋습니다.

Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."

그런 다음 각 오류를 발생시키는 전용 개인 메서드를 만듭니다.

Private Sub OnFooWasNotBarredError(ByVal source As String)
    Err.Raise Err_FooWasNotBarred, source, Msg_FooWasNotBarred
End Sub

Private Sub OnBarNotInitializedError(ByVal source As String)
    Err.Raise Err_BarNotInitialized, source, Msg_BarNotInitialized
End Sub

클래스의 구현은 오류를 제기하기 위해 다음과 같은 특수화 된 프로 시저를 호출 할 수 있습니다.

Public Sub DoSomething()
    'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
    If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
    '...
End Sub

클라이언트 코드는 Err_BarNotInitialized 를 자체 오류 처리 서브 루틴 내에서 다른 오류처럼 처리 할 수 ​​있습니다.


참고 : 기존의 Error 키워드는 Err.Raise 대신 사용할 수도 있지만 더 이상 사용되지 Err.Raise 더 이상 사용되지 않습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow