プロシージャをCallする際、親子どちらでOnErrorを設定したかによって、
どのように処理内容が変化するかをまとめました。
エラー処理を行う際の参考にして下さい。
※ 便宜上Callと書いていますが、Functionの戻り値を変数に入れるなど、
プロシージャを呼び出す処理すべて同じ挙動となります。
- 子だけOn Error Resume Next
- 親だけOn Error Resume Next
- 親子どちらにもOn Error Resume Next
- 親子どちらにもOnErrorGoTo
- 親のOnErrorGoTo vs 子のOn Error Resume Next
- 親のOn Error Resume Next vs 子のOnErrorGoTo
- Errオブジェクトの中身は共有
子だけOn Error Resume Next
OnErrorとCallの関係を知る上で、最も単純かつ重要なコードがこちらです。
Sub 親プロシージャ() Call 子プロシージャ Cells(0, 0).Select ' エラー発生 End Sub Sub 子プロシージャ() On Error Resume Next Cells(0, 0).Select ' エラー発生せず End Sub
まずはこのコードをしっかり読んで、「OnError○○はプロシージャごとの設定」ということを理解して読み進めてください。
On Error Resume Nextを実行した後ずっとエラーがスキップされるわけではなく
On Error Resume Nextを実行したSubがEndした時点でスキップされなくなる
というのが重要なポイントです。
親だけOn Error Resume Next
続いて親だけにエラー処理を書いた場合です。
Sub 親プロシージャ() On Error Resume Next Call 子プロシージャ Cells(0, 0).Select ' エラー発生せず End Sub Sub 子プロシージャ() Cells(0, 0).Select ' エラー発生せず MsgBox "このコードは実行されない" End Sub
親でOn Error Resume Nextを指定した場合は、
子でエラーが起きた時もエラー自体はスキップされます。
ただし、このMsgBoxが表示されないことからわかる通り、
子でエラーが発生した時点でCall元の親に戻るという挙動に注意してください。
「エラーが起きたら次の行に進む」といった場合のこの「次」は、
On Error Resume Nextを実行した親にとっての次ということですね。
エラーが起きたのでCallの次の行に飛んだと理解するとわかりやすいですね。
親子どちらにもOn Error Resume Next
Sub 親プロシージャ() On Error Resume Next Call 子プロシージャ Cells(0, 0).Select ' エラー発生せず End Sub Sub 子プロシージャ() On Error Resume Next Cells(0, 0).Select ' エラー発生せず MsgBox "このコードは実行される" End Sub
親子どちらにもOn Error Resume Nextを書いた場合は、
素直にどちらのエラーもスキップされます。
親子どちらにもOnErrorGoTo
続いてより詳しく調べるためにGoToでジャンプしてみます。
Sub 親プロシージャ() On Error GoTo エラー時 Call 子プロシージャ Exit Sub エラー時: MsgBox "このコードは実行されない" End Sub Sub 子プロシージャ() On Error GoTo エラー時 Cells(0, 0).Select Exit Sub エラー時: MsgBox "このコードは実行される" End Sub
このコードを実行してみるとわかりますが、
親側のOnErrorは「エラー時」にジャンプしていません。
つまり親側ではエラーが起きたという扱いにはなっておらず、
「子のOnErrorで処理されたエラーは親にとってはエラーでない」
と解釈することができるということです。
子で想定していたことなので想定外(例外)ではない
と捉えるとわかりやすいでしょうか。
そしてもう一つ、親側でOnErrorGoToを書いていますが、
子で発生したエラーはちゃんと子のOnErrorGoToで処理されます。
親のGotoに従って子プロシージャから離脱してしまうことはありません。
このように、親子どちらにもOnError処理を書いた場合は、
双方のOnErrorは独立してちゃんと動いてくれます。
ありがたい仕様ですね。
親のOnErrorGoTo vs 子のOn Error Resume Next
Sub 親プロシージャ() On Error GoTo エラー時 Call 子プロシージャ MsgBox "このコードは実行される" Cells(0, 0).Select Exit Sub エラー時: MsgBox "このコードは実行される" End Sub Sub 子プロシージャ() On Error Resume Next Cells(0, 0).Select End Sub
ここまでの仕様を理解していれば復習みたいなものですが、
子のOn Error Resume Nextによって親のOnErrorが切れたりはしません。
子プロシージャのCall(中でのエラースキップ)は問題なく行われ、
そのあとの親プロシージャのエラーはしっかりGoToでジャンプされます。
親のOn Error Resume Next vs 子のOnErrorGoTo
Sub 親プロシージャ() On Error Resume Next Call 子プロシージャ End Sub Sub 子プロシージャ() On Error GoTo エラー時 Cells(0, 0).Select Exit Sub エラー時: MsgBox "このコードは実行される" Stop End Sub
こちらも同じく復習みたいなものですが、
親でOn Error Resume Nextとしていたとしても、
子でOn Error GoToを指定すればちゃんとジャンプします。
試しに「Stop」も書いてみましたが、こうすることにより、
いかに親がOn Error Resume Nextしようとも実行を止める
なんてエラー対応を書くことも可能です。
Errオブジェクトの中身は共有
さてここまで「OnErrorの設定はプロシージャごとのもの」と書いてきましたが、
Errオブジェクトの中身だけは例外になります。
Sub 親プロシージャ() On Error Resume Next Call 子プロシージャ If Err.Number > 0 Then MsgBox Err.Description End If End Sub Sub 子プロシージャ() On Error Resume Next Cells(0, 0).Select End Sub
このコードを実行すると、ちゃんと
| アプリケーション定義またはオブジェクト定義のエラーです。 |
というMsgBoxが表示されます。
子プロシージャで想定していないエラーだけをキャッチしたい
ような場合はErr.Numberによる判定方法は使えません。
素直にOnErrorGoToで分岐を書いてください。
エラー処理はプロシージャごとに独立と思っていると、
If Err.Number > 0 Then
この判定が独立でない罠にはまりますのでご注意ください。
以上でCallとOnErrorの親子関係による挙動の変化の解説を終わります。
- 親のOnErrorは子のOnErrorに干渉しない
- 子のOnErrorで捉えたエラーは親のOnErrorにはかからない
- 子で捉えなかったエラーを親のOnErrorが捕まえた場合は、子のそれ以降のコードは実行されない
この3つを押さえておけばいいんじゃないかと思います。
いずれも知っていればとても扱いやすい仕様ですので、
便利に活用していきましょう。