サーチ…


エラー状態の回避

ランタイムエラーが発生した場合、良いコードがそれを処理する必要があります。最適なエラー処理戦略は、エラー状態をチェックし、実行時エラーを引き起こすコードの実行を単純に回避するコードを記述することです。

ランタイムエラーを減らすための重要な要素の1つは、小さな手続きを書くことです。手続きが失敗する理由が少なくなればなるほど、コード全体がデバッグされやすくなります。


実行時エラーの回避91 - Objectまたは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リファレンスが割り当てられていない場合、未割り当てリファレンスは使用されず、エラーは発生しません。

1つ以上のパラメータが有効でないときにプロシージャを早期に終了するこの方法は、 ガード句と呼ばれます。


実行時エラーの回避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 GoToステートメントは、実行時に予期しないエラーが発生するたびにVBAにラインラベルにジャンプし、「エラー処理モード」と入力するよう指示します。エラーを処理した後、コードが使用する「通常」の実行に戻って再開できるResumeキーワードを。

行のラベルサブルーチンを表します:サブルーチンは従来のBASICコードに由来し、 GoToGoSubジャンプとReturnステートメントを使用して「main」ルーチンにジャンプするため、厳密に構造化されていない場合は難しいスパゲッティコードを記述するのはかなり簡単です。このような理由から、

  • プロシージャには1つのみのエラー処理サブルーチンがあります
  • エラー処理サブルーチンはエラー状態でしか実行されません

これは、そのエラーを処理するプロシージャで、次のように構成する必要があります。

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 hiddenプロパティは、最後のエラーを発生させた行番号を識別するために使用できます。行番号を使用していない場合、 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精度しか持たず、黙ってオーバーフローすることにErlください。これは、 整数の範囲外の行番号が間違った結果をもたらすことを意味します。

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ステートメントは、特定のプロシージャ内に1つだけ存在する必要があります。

しかし、 時にはエラー状態を避けることができない場合があります。エラー処理のサブルーチンにジャンプするだけで、 Resume Nextが正しく動作しません。この特定の場合、2つの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パラメータもErr.Raiseできます。このため、各カスタムエラーの説明を保持する定数を定義することをお勧めします。

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代わりに使用することもできますが、廃止または廃止されました。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow