VBA 오류 처리를위한 좋은 패턴
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) ...
두 오류를 모두 처리하고 오류가 발생할 수있는 코드 이후에 실행을 재개하고 싶습니다. 또한 마지막에있는 finally 코드는 이전에 어떤 예외가 발생하더라도 항상 실행되어야합니다. 이 결과를 어떻게 얻을 수 있습니까?
VBA의 오류 처리
On Error Goto
ErrorHandlerLabelResume
(Next
| ErrorHandlerLabel )On Error Goto 0
(현재 오류 핸들러 비활성화)Err
목적
Err
개체의 속성은 일반적으로 0 개 또는 오류 처리 루틴의 길이가 0 인 문자열로 재설정됩니다뿐만 아니라 명시 적으로 수행 할 수 있습니다 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
또한 다음을 추가합니다.
- 전역
Err
개체는 예외 개체에 가장 가깝습니다. - 다음과 같이 효과적으로 "예외를 던질"수 있습니다.
Err.Raise
그리고 재미로 :
On Error Resume Next
악마는 화신하고 피해야합니다. 조용히 오류를 감추기 때문입니다.
그래서 당신은 이렇게 할 수 있습니다
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를 사용하여 필요에 따라 방법의 흐름을 변경합니다.
다음은 내 표준 구현입니다. 나는 레이블이 자기 설명적인 것을 좋아합니다.
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
Professional Excel Development 는 오류 처리 체계가 매우 우수 합니다 . VBA에서 시간을 보내려면 책을 구하는 것이 좋습니다. VBA가 부족한 영역이 많이 있으며이 책에는 이러한 영역을 관리하기위한 좋은 제안이 있습니다.
PED는 두 가지 오류 처리 방법을 설명합니다. 주된 것은 모든 진입 점 프로 시저가 하위 프로 시저이고 다른 모든 프로 시저가 부울을 반환하는 함수 인 시스템입니다.
진입 점 프로시 저는 On Error 문을 사용하여 설계된대로 오류를 캡처합니다. 비 진입 지점 프로시 저는 오류가 없으면 True를 반환하고 오류가 있으면 False를 반환합니다. 비 진입 지점 프로시 저도 On Error를 사용합니다.
두 가지 유형의 절차 모두 중앙 오류 처리 절차를 사용하여 오류를 상태로 유지하고 오류를 기록합니다.
나는 내가 직접 개발 한 코드를 사용하는데 그것은 내 코드에 꽤 좋다.
함수 또는 하위의 시작 부분에서 다음을 정의합니다.
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
여기 꽤 괜찮은 패턴이 있습니다.
디버깅 : 오류가 발생하면 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 오류를 모두 트랩합니다. 특정 Err 번호를 트랩하려는 경우 VBA 오류 섹션에 Select Case를 넣을 수 있습니다.
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
아래 코드는 하위 / 기능에 대한 출구 지점이 하나만 있는지 확인하는 대안을 보여줍니다.
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
토론과 관련이있는 것은 상대적으로 알려지지 않은 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
함수는 가장 최근에 발견 된 숫자 라인 레이블을 반환합니다. 위의 예에서 런타임 오류가 label 이후에 발생 1200:
하지만 이전에 발생 1300:
하면 Erl
함수 1200
는를 반환합니다 . 그 이유는 가장 성공적으로 라인 레이블을 발견했기 때문입니다. 오류 처리 블록 바로 위에 줄 레이블을 배치하는 것이 좋습니다. 나는 절차 9999
의 주요 부분이 예상되는 결론에 도달했음을 나타 내기 위해 일반적으로 사용 합니다.
메모:
라인 레이블은 양의 정수
MadeItHere:
여야합니다Erl
. 같은 레이블 은 .라인 레이블은의 실제 라인 번호와 완전히 관련이 없습니다
VBIDE CodeModule
. 원하는 순서대로 원하는 양수를 사용할 수 있습니다. 위의 예에서는 25 줄 정도의 코드 줄만 있지만 줄 레이블 번호는에서 시작합니다1000
. 에서 사용되는 편집기 줄 번호와 줄 레이블 번호 간에는 관계가 없습니다Erl
.라인 레이블 번호는 오름차순이 아닌 하향식 순서가 아니더라도 효능과 이점
Erl
이 크게 감소하지만Erl
올바른 번호를보고 하더라도 특정 순서 일 필요는 없습니다 .라인 레이블은 표시되는 절차에 따라 다릅니다. 프로 시저
ProcA
가 procedure를 호출ProcB
하고ProcB
제어를 다시로 전달 하는 오류가 발생ProcA
하면Erl
(inProcA
)은ProcA
호출하기 전에 가장 최근에 발견 된 라인 레이블 번호를 반환 합니다ProcB
. 내부ProcA
에서는에 나타날 수있는 라인 레이블 번호를 가져올 수 없습니다ProcB
.
루프 내에 줄 번호 레이블을 넣을 때주의하십시오. 예를 들면
For X = 1 To 100
500:
' some code that causes an error
600:
Next X
라인 레이블을 따르는 코드가 500
이전 600
에 오류 를 일으키고 해당 오류가 루프의 20 번째 반복에서 발생하면 루프 의 이전 19 개 상호 작용에서 성공적으로 발생 했더라도 Erl
를 반환 합니다.500
600
절차 내에서 라인 레이블을 적절하게 배치 Erl
하는 것은 진정으로 의미있는 정보를 얻기 위해 함수를 사용하는 데 중요합니다 .
절차에 숫자 줄 레이블을 자동으로 삽입하는 무료 유틸리티가 네트워크에 많이 있으므로 개발 및 디버깅 중에 세분화 된 오류 정보를 얻은 다음 코드가 활성화되면 해당 레이블을 제거합니다.
예상치 못한 오류가 발생하면 코드가 최종 사용자에게 오류 정보를 표시하는 경우 Erl
해당 정보 의 값을 제공하면의 값 Erl
이보고되지 않는 경우보다 훨씬 간단하게 문제를 찾고 수정할 수 있습니다 .
코끼리 덫을 조심하세요.
나는이 토론에서 이것에 대한 언급을 보지 못했습니다. [액세스 2010]
ACCESS / VBA가 CLASS 개체의 오류를 처리하는 방법은 구성 가능한 옵션에 의해 결정됩니다.
VBA 코드 편집기> 도구> 옵션> 일반> 오류 트래핑 :
중앙 오류 처리 방식이라고하는 다음이 가장 잘 작동한다는 것을 알았습니다.
혜택
애플리케이션 실행에는 디버그 및 프로덕션 의 두 가지 모드가 있습니다 . 에서 디버그 모드, 코드는 예기치 않은 오류로 중단되고 당신은 두 번 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
이 스레드의 앞부분에서 작성한 진술에 대한 나의 개인적인 견해 :
그리고 재미로 :
On Error Resume Next는 악마의 화신으로, 조용히 오류를 숨기므로 피해야합니다.
나는 On Error Resume Next
오류가 내 작업을 중단하지 않고 어떤 진술이 이전 진술의 결과에 의존하지 않는 곳 에서 on 절차를 사용하고 있습니다.
나는이 일을 해요 때 나는 글로벌 변수를 추가 debugModeOn
하고 난으로 설정합니다 True
. 그런 다음 이렇게 사용합니다.
If not debugModeOn Then On Error Resume Next
작업을 전달할 때 변수를 false로 설정하여 사용자에게만 오류를 숨기고 테스트 중에 표시합니다.
또한 비어있을 수있는 ListObject의 DataBodyRange를 호출하는 것과 같이 실패 할 수있는 작업을 수행 할 때도 사용합니다.
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)
참고 URL : https://stackoverflow.com/questions/1038006/good-patterns-for-vba-error-handling
'developer tip' 카테고리의 다른 글
ASP.NET Core 웹 API 인증 (0) | 2020.10.31 |
---|---|
Ruby on Rails 기능 테스트에서 JSON 결과를 테스트하는 방법은 무엇입니까? (0) | 2020.10.31 |
SQL SELECT 다중 열 INTO 다중 변수 (0) | 2020.10.31 |
사전에 키, 값 쌍을 추가하는 방법은 무엇입니까? (0) | 2020.10.31 |
JsDoc에서 void를 반환하는 방법은 무엇입니까? (0) | 2020.10.31 |