VBAにおけるTry-Catch-Finallyの実装
にメンテナンス済み
一般的にエラー処理は、各関数では最低限行い、後は呼び出し元に Throw するのが一般的かと思いますが、VBA ではそれを簡単には実装できません。
エラーハンドラを作ってしまうとエラーを握りつぶしてしまい、そこから再度 Err.Raise しようとすると、その関数内でエラーを拾い無限ループになるからです。
具体的な例を挙げると、
- Open 関数でファイルを開いた後、エラーになった場合でも Close は発生させたい。
- その上で、エラーを呼び出し元に伝達したい。
といった場合です。
On Error GoTo 0
Call Err.Raise(Err.Number)
解決には上記のコードを使います。解説とサンプルを紹介致します。
On Error GoTo 0
On Error GoTo 0 を使用すると、使用した以前に定義していた On Error を打ち消すことができます。
通常、エラーハンドラで Err.Raise を書いてしまうと、
- エラーを拾う
- エラーハンドラに飛ぶ
- エラーを投げる
- 1 に戻る
上記のループになってしまいますが、On Error GoTo 0 を入れることで、
- エラーを拾う
- エラーハンドラに飛ぶ
- エラーハンドラの打ち消し
- エラーを呼び出し元にスロー
とすることができます。これにより、他のプログラム言語で言うところの、catch に throw を含むような Try-Catch-Finally を再現できます。
コードサンプル
エラー処理 → Throw だけでいい場合
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print(Err.Number)
End Sub
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
' エラーの起こり得る処理を書く
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
' ここにエラー発生後の処理を書く
' 呼び出し元にエラーを伝達します
Call Err.Raise(errNumber)
End Sub
Try-Catch-Finally を再現する
結構長い記述が必要です。
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print(Err.Number)
End Sub
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
' エラーの起こり得る処理を書く
FINALLY:
If errNumber <> 0 then
Err.Raise(errNumber)
End if
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
' ここにエラー発生後の処理を書く
Resume FINALLY
End Sub
実例サンプル
Try-Catch のみの場合
'------------------------------------------------
'
' 関数を呼ぶ関数
'
'------------------------------------------------
Public Sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print "呼び出し元 : Catch"
End Sub
'------------------------------------------------
'
' 呼び出される(Throwを行う)関数
'
'------------------------------------------------
Public Sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
Dim n As Long: n = 1 / 0
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
Debug.Print "呼び出し先 : Catch"
' 呼び出し元にエラーを伝達します
Call Err.Raise(errNumber)
End Sub
実行結果
呼び出し先 : Catch
呼び出し元 : Catch
Try-Catch-Finally を再現した場合
'------------------------------------------------
'
' 関数を呼ぶ関数
'
'------------------------------------------------
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print "呼び出し元 : Catch"
End Sub
'------------------------------------------------
'
' 呼び出される(Throwを行う)関数
'
'------------------------------------------------
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as long
Dim n as long: n = 1 / 0
FINALLY:
Debug.Print "呼び出し先 : Finally"
If errNumber <> 0 then
Err.Raise(errNumber)
End if
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
Debug.Print "呼び出し先 : Catch"
Resume FINALLY
End Sub
実行結果
呼び出し先 : Catch
呼び出し先 : Finally
呼び出し元 : Catch
#Excel
#VBA