メインプロシージャ内からSubプロシージャをCALLしたり、Function関数を呼び出す場合のエラー処理についてはどのようにすればよいのでしょうか。
はじめに
On Error GoTo ステートメントが設定してあるメインプロシージャ内から、呼び出したプロシージャ内でエラーが発生した場合はどのような挙動になるのでしょうか。
いろいろなパターンでコードを設定してテストを行ってみたいと思います。
【この記事でわかることは】
・ネストしている複数のプロシージャでエラーが発生した場合の処理方法
・エラー後の終了処理にラベルを使う方法
エラー処理がメインだけの場合
はじめに On Error GoTo を呼び出し元のメインプロシージャだけに設置している例の挙動を見てみましょう。
エラーは呼び出し元に返ってくる
次の例は複数の関数が入れ子になっている状態で、一番深いところでエラーを発生させています。
Sub mainProc()
On Error GoTo ErrorHandler
Dim i As Long
i = subProc1()
MsgBox "値は'" & i & "'です「正常終了しました。」"
GoTo ProcExit
'エラー処理
ErrorHandler:
With Err 'Errオブジェクト
MsgBox "「異常終了です!」" & _
vbCrLf & "エラー番号" & .Number & ":" & .Description _
, vbCritical, "エラー通知"
End With
'終了処理
ProcExit:
On Error GoTo 0
End Sub
Function subProc1()
subProc1 = subProc2() / 0
End Function
Function subProc2()
subProc2 = subProc3 / 0
End Function
Function subProc3()
subProc3 = 100 / 0
End Function
・エラー発生時には、呼び出し元のエラー処理ルーチンが実行されます。
・ただし、その場合どの場所でエラーが発生したのか全くわかりません。
それぞれのプロシージャにエラー処理を組み込む
呼び出し先のプロシージャにもエラー処理を組み込んだ場合どうなるのでしょうか。
エラー発生後の処理に注意が必要
先ほどのコードのサブプロシージャは一つに絞ってエラー処理を組み込んでみます。
Sub mainProc()
On Error GoTo ErrorHandler
Dim i As Long
i = subProc()
MsgBox "値は'" & i & "'です「正常終了しました。」"
Err.Raise 9 '←ここでわざとエラーを発生させています。
GoTo ProcExit
'エラー処理
ErrorHandler:
With Err 'Errオブジェクト
MsgBox "「異常終了です!」" & _
vbCrLf & "エラー番号" & .Number & ":" & .Description _
, vbCritical, "エラー通知"
End With
'終了処理
ProcExit:
On Error GoTo 0
End Sub
Function subProc()
On Error GoTo ErrorHandler
subProc = 100 / 0
GoTo ProcExit
'エラー処理
ErrorHandler:
With Err 'Errオブジェクト
MsgBox "subProc内で「異常終了です!」" & _
vbCrLf & "エラー番号" & .Number & ":" & .Description _
, vbCritical, "エラー通知"
End With
'エラー処理
ProcExit:
On Error GoTo 0
End Function
・エラーが発生したプロシージャ内でエラー処理が実行されます。
発生場所がわかるようにプロシージャ名をメッセージに表示するようにしています。
・その後、メインに戻り次のステップ(5行目)が実行されてしまいます。
エラーなのに「正常終了しました。」と表示され、おかしなことになってしまいます。
・さらに6行目でわざとエラーを発生させると、メインのエラー処理が実行されます。
メインのエラー処理(On Error GoTo)はまだ生きていたことがわかります。
・このように致命的なエラーが発生しているのに処理が止まらずに進んでしまってはダメですね。
・こうならないようにしっかり対策する必要があります。
サブルーチンのエラー状況をメインでキャッチする
エラー処理内で Err.Raise を使って分岐先のエラーをメインに返すようにする方法です。
Sub mainProc()
On Error GoTo ErrorHandler
Dim i As Long
i = subProcX()
MsgBox "値は'" & i & "'です「正常終了しました。」"
GoTo ProcExit
'エラー処理
ErrorHandler:
With Err 'Errオブジェクト
MsgBox .Source & "内で「異常終了です!」" & _
vbCrLf & "エラー番号" & .Number & ":" & .Description _
, vbCritical, "エラー通知"
End With
'終了処理
ProcExit:
On Error GoTo 0
End Sub
Function subProcX()
On Error GoTo ErrorHandler
subProcX = 100 / 0
GoTo ProcExit
'エラー処理
ErrorHandler:
'エラー番号にプロシージャ名を付加してErr.Raise
Err.Raise Err.Number, "subProcX"
'終了処理
ProcExit:
On Error GoTo 0
End Function
・24~26行目のエラー処理内では Err.Raise メソッドを使って処理します。
・26行目は、Err.Raise の引数に発生したエラー番号(Err.Number)とプロシージャ名(“subProcX”)を付加しています。
・エラー処理内で Err.Raise したことで、メインのエラー処理(8行目)に処理が移ります。
・10行目の .Source で、26行目で付加したプロシージャ名(“subProcX”)を追加しています。
こうすることで、メインの処理でも発生した場所がどこなのかを確認することができます。
On Error Goto の記述後にエラーが発生してエラー処理に移った時点で On Error はリセットされます。呼び出し先のサブプロシージャに On Error Goto の記述がある場合、エラー処理内で新たに発生したエラーは、呼び出し元の On Error Goto 範囲に属するため、エラー処理は呼び出し元で行われます。
終了処理ラベルの利用
エラー処理の飛び先行ラベル「ErrorHandler:」以降は、正常時には動作させないようにするため、直前で処理を抜けるよう通常は「Exit Sub」や「Exit Function」を記述します。
でも今回の例では、「終了処理」ラベルを使ってエラー処理を無効に戻す「On Error GoTo 0」を入れるようにしています。
終了処理が必要な事例
先ほどの事例では「終了処理ラベル」に「On Error GoTo 0」だけを入れましたが、次のような例では「終了処理」ラベルの使用が便利です。
Sub mainProc()
On Error GoTo ErrorHandler
Dim i As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
i = subProcX()
MsgBox "値は'" & i & "'です「正常終了しました。」"
GoTo ProcExit
'エラー処理
ErrorHandler:
With Err 'Errオブジェクト
MsgBox .Source & "内で「異常終了です!」" & _
vbCrLf & "エラー番号" & .Number & ":" & .Description _
, vbCritical, "エラー通知"
End With
'終了処理
ProcExit:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
On Error GoTo 0
End Sub
・VBAの高速化によく使う Application.ScreenUpdating = False と Application.Calculation = xlCalculationManual を冒頭に入れている事例です。
・エラーで異常終了した場合、解除しておかないとエクセルが「画面の描画停止」「手動計算」の状態のままとなってしまいます。
・「終了処理」ラベルを使えば、その中に解除を記述しておくだけで、正常終了時でもエラー発生時でも終了前に解除させることができます。
・「終了処理」ラベルを使わず「Exit Sub」で終了させる場合、「Exit Sub」と「EndSub」両方の直前に Application.ScreenUpdating = True と Application.Calculation = xlCalculationAutomatic をそれぞれ記述する必要があります。
・このように「終了処理」ラベルを使えばVBAのコーディングが少し楽になります。
まとめ(おわりに)
今回の記事はいかがでしたか? 少しでもお役に立てたなら幸いです(^^;
On Error GoTo ステートメントがメインプロシージャーだけに設置されている場合、呼び出し先のサブプロシージャ内で発生した実行時エラーは全てメインに返ってきます。
呼び出し先のプロシージャにも On Error GoTo を設置する場合は、エラー処理内でキャッチしているエラーにプロシージャ名を付加して再び Err.Raise することで、メインのエラー処理でエラー情報を捉えることができます。
エラー発生時にそのまま終了させてはいけない場合は「終了処理ラベル」を使うことで適切に効率的な記述が可能です。
★★★ ブログランキング参加中! クリックしてね(^^)/ ★★★
過去記事のサンプルファイルをダウンロードできます
この記事で使用したサンプルの登録はありません。
過去の記事で使用したサンプルファイルをダウンロードできるようにページを設置していますので、こちら(このリンク先)からご利用ください