VBA/VBScript開発者必見!「On Error GoTo 0」の正しい使い方と注意点
VBAやVBScriptは、Microsoft Officeアプリケーションの自動化や、Windows環境でのスクリプト処理において、今なお多くの開発者に利用されています。これらの言語でアプリケーションやツールを開発する上で、避けて通れないのが「エラーハンドリング」、すなわち実行中に発生する可能性のあるエラーにどう対処するかという問題です。適切にエラーを処理できなければ、作成したプログラムは予期せず停止し、ユーザーに不便をかけたり、データ損失を引き起こしたりするリスクがあります。
VBA/VBScriptにおけるエラーハンドリングの根幹をなすステートメントの一つにOn Errorがあります。そして、このOn Errorステートメントの中でも、特にその役割が重要でありながら、しばしば誤解されたり不適切に使用されたりするのが、On Error GoTo 0です。
「エラーを無視するためのもの」「とりあえず書いておくもの」といった曖昧な理解で使用していると、プログラムの安定性を損なったり、デバッグを困難にしたりする可能性があります。しかし、その真の役割と適切な使用法を理解すれば、より堅牢で保守しやすいコードを書くための強力なツールとなります。
この記事では、VBA/VBScript開発者の方々がOn Error GoTo 0を正しく理解し、効果的に活用できるよう、その詳細な使い方、他のOn Errorステートメントとの連携、そして重要な注意点について、徹底的に解説していきます。約5000語のボリュームで、基本から応用まで網羅することを目標とします。
1. はじめに:エラーハンドリングの重要性
ソフトウェア開発において、エラーはつきものです。ユーザーの入力ミス、ファイルの読み書き権限の問題、ネットワーク接続の不具合、予期しないデータ形式、メモリ不足など、プログラムの実行中に様々な問題が発生する可能性があります。VBAやVBScriptで記述されたプログラムも例外ではありません。
エラーが発生した際に、何も対策が講じられていない場合、VBA/VBScriptのデフォルトの動作として、エラーが発生した行でプログラムの実行は停止し、エラーメッセージが表示されます。これは開発者にとってはデバッグの手がかりとなりますが、エンドユーザーにとっては非常に不親切で、プログラムの信頼性を著しく損なう行為です。
エラーハンドリングとは、このように予期せぬエラーが発生した場合に、プログラムが一方的に停止するのではなく、開発者が意図した処理(エラーのログ記録、ユーザーへの通知、代替処理の実行、安全な終了処理など)を実行できるようにするための仕組みです。適切にエラーハンドリングを行うことで、以下のメリットが得られます。
- プログラムの安定性向上: 予期せぬ停止を防ぎ、可能な限り処理を継続させたり、安全に終了させたりすることができます。
- ユーザー体験の向上: 難解なエラーメッセージではなく、ユーザーが理解できる形で状況を伝えたり、問題を回避するためのガイダンスを提供したりできます。
- 保守性の向上: エラー発生時の状況を記録することで、問題の原因特定やデバッグが容易になります。
- 堅牢性の確保: 様々な状況下でプログラムが破綻しないように設計できます。
VBA/VBScriptにおけるエラーハンドリングは、主にOn Errorステートメントを使用して行います。On Errorステートメントにはいくつかの種類があり、それぞれ異なるエラー発生時の挙動を定義します。その中で、今回焦点を当てるOn Error GoTo 0は、これらのエラーハンドリング設定を「無効にする」という重要な役割を担っています。
この記事を通じて、On Error GoTo 0の機能と、それがなぜ他のOn Errorステートメントと組み合わせて使われることが多いのかを深く理解し、VBA/VBScriptによる開発におけるエラーハンドリングのスキルを向上させましょう。
2. VBA/VBScriptにおけるエラーハンドリングの基本
On Errorステートメントに入る前に、VBA/VBScriptにおけるエラーの種類とデフォルトの動作について確認しておきましょう。
VBA/VBScriptで遭遇する可能性のあるエラーは、大別すると以下の3種類です。
- コンパイルエラー (Compile Error): コードの記述ミス(構文エラー、スペルミス、変数宣言漏れなど)によって発生するエラーです。コードの実行前にVBE (Visual Basic Editor) やスクリプトエンジンが検出します。多くの場合、コード入力中や実行前に表示されるため、比較的容易に修正できます。
On Errorステートメントはコンパイルエラーには効果がありません。 - 実行時エラー (Runtime Error): プログラムの実行中に発生するエラーです。例えば、存在しないファイルを開こうとする、ゼロで除算する、オブジェクトがNothingであるのにそのメソッドを呼び出す、といった状況で発生します。
On Errorステートメントが対処するのは、主にこの実行時エラーです。 - 論理エラー (Logic Error): プログラムは文法的に正しく実行されますが、意図した結果が得られないエラーです。計算ロジックの間違いや、期待と異なるデータ処理などによって発生します。これはコード自体の欠陥であり、
On Errorステートメントでは捕捉できません。デバッグやテストによって発見・修正する必要があります。
On Errorステートメントは実行時エラーにのみ適用されます。デフォルトでは、実行時エラーが発生すると、プログラムはその場で停止し、エラーの詳細(エラー番号、説明など)を示すダイアログボックスが表示されます。
このデフォルトの動作を変更し、開発者が定義した処理を実行させるためにOn Errorステートメントを使用します。On Errorステートメントには主に以下の3つの形式があります。
On Error GoTo line: エラーが発生した場合、指定されたラベル(line:)にプログラムの実行をジャンプさせます。これにより、開発者がエラーハンドリングルーチンと呼ばれるコードブロックでエラーを処理できるようになります。On Error Resume Next: エラーが発生した場合、そのエラーが発生した行をスキップし、直後の行からプログラムの実行を継続します。エラーを無視して先に進むため、注意深く使用する必要があります。On Error GoTo 0: エラーハンドリングを無効にし、デフォルトのエラー処理(プログラムの停止)に戻します。これが今回の主役です。
これらのOn Errorステートメントは、コード内で複数回使用することで、プログラムの異なる部分で異なるエラーハンドリングを設定することができます。そして、On Error GoTo 0は、設定したエラーハンドリングを「終了」させる役割を担うのです。
3. On Error GoTo 0 とは何か?
On Error GoTo 0ステートメントは、現在有効になっているエラーハンドリングを無効化し、プログラムの実行時エラー処理をデフォルトの状態に戻すためのものです。デフォルトの状態とは、エラーが発生するとプログラムの実行が即座に停止し、エラーメッセージが表示される状態です。
なぜ「GoTo 0」という構文なのでしょうか? これはVBA/VBScriptの内部的なエラー処理の仕組みに由来すると言われています。エラーハンドリングが設定されていない状態を、エラーハンドリングルーチンへのジャンプ先アドレスがゼロであると解釈している、といったイメージです。開発者が直接アドレスを指定するわけではありませんが、「GoTo 0」というキーワードによって、エラーハンドリング設定を初期状態(デフォルト)に戻すことを意味します。
On Error GoTo 0ステートメントを単独で実行した場合、その効果は以下のようになります。
- それまで有効だった
On Error GoTo lineまたはOn Error Resume Nextの設定が無効になります。 - 以降の実行時エラーは、デフォルトの挙動(プログラム停止、エラーメッセージ表示)に従って処理されます。
- 既に発生しているエラー(
Errオブジェクトに格納されている情報)はクリアされません。Errオブジェクトをクリアするには、別途Err.Clearメソッドを呼び出す必要があります。これは非常に重要な注意点です。On Error GoTo 0は「これから発生するエラー」に対するハンドリング設定を変更するものであり、「既に発生してErrオブジェクトに記録されているエラー」の状態をリセットするものではない、と理解してください。
例えば、ある処理ブロックの先頭でOn Error Resume Nextを設定し、そのブロックの終わりにOn Error GoTo 0を設定するというパターンはよく見られます。
“`vba
Sub ProcessFile(filePath As String)
Dim fileNum As Integer
‘ ファイルオープン中にエラーが発生しても停止しないようにする
On Error Resume Next
fileNum = FreeFile
Open filePath For Input As #fileNum
On Error GoTo 0 ‘ ここでエラーハンドリングをデフォルトに戻す
‘ ここでエラーが発生したかチェックする
If Err.Number <> 0 Then
MsgBox “ファイルのオープンに失敗しました: ” & Err.Description
Err.Clear ‘ エラー情報をクリアする
Exit Sub ‘ 処理を中断
End If
‘ ここから先は、もし別のエラーが発生した場合、デフォルト動作で停止する
‘ (またはこのプロシージャの先頭などで別のハンドラが設定されていれば、そちらへジャンプする)
‘ ファイル読み込みなどの処理…
Close #fileNum
MsgBox “ファイル処理が完了しました。”
End Sub
``Open
この例では、ステートメントの前にOn Error Resume Nextを設定することで、ファイルが見つからないなどのエラーが発生してもプログラムが停止せず次の行(On Error GoTo 0)に進みます。Openステートメントの直後にOn Error GoTo 0を設定することで、限定的なエラー処理が必要な箇所(ファイルオープン)だけOn Error Resume Nextを有効にし、それ以降のコードではデフォルトのエラー処理に戻しています。そして、エラーが発生したかどうかをErr.Number`で確認し、必要に応じてエラー処理(メッセージ表示とクリア)を行っています。
このように、On Error GoTo 0は、設定したエラーハンドリングの範囲を限定したり、特定のハンドリング状態を終了させたりするために使用されます。単独でコードの冒頭に記述されることは稀で、通常は他のOn Errorステートメントとセットで使用されます。
4. On Error GoTo line と On Error GoTo 0 の連携
VBA/VBScriptにおける最も一般的で構造化されたエラーハンドリング手法は、On Error GoTo lineを使用することです。この手法では、エラーが発生した場合にプログラムの実行を指定されたラベルにジャンプさせ、そこでエラーハンドリングルーチンと呼ばれるコードブロックを実行します。On Error GoTo 0は、このエラーハンドリングルーチンと組み合わせて使用されることが非常に多いです。
4.1 On Error GoTo line を使用したエラーハンドリングの基本構造
“`vba
Sub ExampleWithErrorHandling()
‘— エラーハンドリングの設定 —
On Error GoTo ErrHandler ‘ 以降でエラーが発生したらErrHandlerラベルにジャンプ
‘— 通常の処理 —
‘ ここにエラーが発生する可能性のあるコードを書く
Dim x As Integer
Dim y As Integer
Dim z As Integer
x = 10
y = 0
z = x / y ‘ ここでゼロ除算エラー(実行時エラー 11)が発生する
MsgBox “処理が正常に完了しました。” ‘ エラーが発生しなければここに到達する
‘— 正常終了時の後処理 —
‘ エラーが発生しなかった場合にのみ実行されるコード
‘ エラーハンドリングルーチンへのジャンプを避けるためにExit Sub/Functionを配置する
Exit Sub ‘ エラーハンドリングルーチンへのジャンプを防ぐ
‘— エラーハンドリングルーチン —
ErrHandler: ‘ エラーが発生した場合にジャンプしてくる場所
‘— エラー発生時の処理 —
‘ Errオブジェクトを使ってエラー情報を取得・処理する
MsgBox “エラーが発生しました!” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description & vbCrLf & _
“発生箇所: ” & Err.Source
‘— エラーハンドリングルーチン内での重要な処理 —
‘ 1. エラーハンドリング設定のリセット (On Error GoTo 0 または別の設定)
On Error GoTo 0 ‘ * ここが重要 *
‘ これをしないと、エラーハンドリングルーチン内でさらにエラーが発生した場合に
‘ デフォルトのハンドリング(プログラム停止)に戻らない可能性がある
‘ (正確には、VBAではErrHandler内で発生したエラーは未処理エラーとなることが多いが、
‘ 意図を明確にするため、またVBScript等での挙動の違いを考慮すると設定が推奨される)
‘ 2. エラー情報のクリア
‘ Err.Clear ‘ 必要に応じてエラー情報をクリアする
‘ 3. 処理の再開、終了、またはエラーの再発生
‘ Resume: エラーが発生した行を再実行 (通常はエラーを修正してから)
‘ Resume Next: エラーが発生した行の次から実行
‘ Resume label: 指定したラベルから実行
‘ Exit Sub / Exit Function: プロシージャを終了
‘ Err.Raise: 呼び出し元にエラーを伝える
‘ 例: エラーが発生した行の次から処理を再開
‘ Resume Next ‘ この例ではゼロ除算が修正されないので、再度エラーになる可能性が高い
‘ 例: プロシージャを終了
Exit Sub ‘ 多くの場合、エラー発生時はプロシージャを終了するのが安全
End Sub
“`
この構造において、Exit Sub (または Exit Function) は非常に重要です。通常処理が正常に完了した場合、そのままエラーハンドリングルーチン(ErrHandler:以降)に流れ込んでしまうのを防ぐ役割があります。
4.2 エラーハンドリングルーチン内での On Error GoTo 0
エラーハンドリングルーチン(ErrHandler:以降) の冒頭でOn Error GoTo 0を設定するスタイルは、多くの開発者に推奨されています。これはなぜでしょうか?
もしエラーハンドリングルーチン内でさらに別のエラーが発生した場合、どうなるでしょうか? 例えば、エラーメッセージを表示しようとしたが、何らかの理由で表示に失敗した、エラーログファイルに書き込もうとしたが、その際にエラーが発生した、といったケースです。
On Error GoTo lineで設定されたエラーハンドリングは、そのエラーハンドリングルーチン自体には適用されません。つまり、ErrHandler:というラベルにジャンプした後で発生したエラーは、「未処理エラー (Untrapped Error)」となり、プログラムは即座に停止し、デフォルトのエラーメッセージが表示されます。
これは、せっかくエラーハンドリングを設定したにも関わらず、その処理中にプログラムが停止してしまうことを意味します。このような事態を防ぎ、エラーハンドリングルーチン内での予期せぬ停止を回避するために、ルーチンの冒頭でOn Error GoTo 0を設定するのです。
“`vba
ErrHandler:
‘— エラー発生時の処理 —
‘ IMPORTANT: エラーハンドリングルーチン内でエラーが発生した場合に備える
On Error GoTo 0 ‘ これにより、ルーチン内でエラーが発生したらデフォルト動作に戻る
‘ Errオブジェクトを使ってエラー情報を取得
Dim errNum As Long
Dim errMsg As String
errNum = Err.Number
errMsg = Err.Description
‘ Err.Source, Err.LastDllError なども取得可能
‘ 例: エラー情報をログファイルに書き込む (この処理自体でエラーが発生する可能性あり)
‘ 例えば、ログファイルのパスが不正だったり、書き込み権限がなかったりする場合
‘ もし On Error GoTo 0 が無ければ、ログ書き込み失敗時にプログラムが停止する
‘ On Error GoTo 0 があれば、ログ書き込み失敗時にデフォルト動作(停止)に戻る
‘ On Error GoTo 0 が設定されているため、
‘ ログ書き込み失敗などのエラーはここで捕捉されない(デフォルト動作になる)
‘ 必要であれば、ルーチン内でのエラー処理用に別途 On Error ステートメントを設定することも可能だが、
‘ 一般的にはシンプルに On Error GoTo 0 に戻し、ルーチン内のエラーはデフォルトで停止させる方が
‘ デバッグしやすいとされる
Debug.Print “エラーログ: ” & errNum & “, ” & errMsg ‘ VBEのイミディエイトウィンドウに出力
‘ エラーメッセージ表示 (これも環境によってはエラーになりうる)
MsgBox “処理中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & errNum & vbCrLf & _
“内容: ” & errMsg, vbCritical
‘ エラー情報のクリア (エラーハンドリングルーチン内で明示的に行うのが良い習慣)
Err.Clear
‘ 処理の再開、終了などを決定する
‘ 例: ユーザーに再試行を促す、代替処理を行う、など
‘ 多くの場合、重大なエラーであればここで Exit Sub/Function となる
‘ Resume Next ‘ 例外的な処理フロー
Exit Sub ‘ 通常はここで終了
End Sub
“`
このOn Error GoTo 0をエラーハンドリングルーチンの冒頭に配置するスタイルは、ルーチン内での予期せぬエラー発生によるプログラムの完全停止を防ぐための「保険」のようなものです。ルーチン内でエラーが発生した場合、それはもはやエラーハンドリングできる状態ではなく、デフォルトの停止動作に戻すことで、少なくともエラー発生箇所を明確に特定しやすくするという意図があります。
4.3 エラーハンドリングルーチンからの復帰と On Error の状態
エラーハンドリングルーチンでエラー処理を行った後、プログラムの実行を再開する方法はいくつかあります。
Resume: エラーが発生した行を再度実行します。エラーの原因が一時的なものであったり、エラーハンドリングルーチン内で原因を取り除いたりした場合に使用します。Resume Next: エラーが発生した行の次の行から実行を継続します。エラーが発生した行をスキップしたい場合に使用します。Resume label: 指定したラベルから実行を継続します。特定の箇所に戻って処理を再開したい場合に使用します。Exit Sub/Exit Function: 現在のプロシージャ(SubまたはFunction)の実行を終了し、呼び出し元に戻ります。エラーが深刻で処理を継続できない場合や、エラー処理自体がプロシージャの目的である場合に使用します。
これらのResume系のステートメントでエラーハンドリングルーチンを抜けて処理を再開した場合、エラーハンドリングの状態はどうなるでしょうか?
VBAの場合: VBAでは、ResumeまたはResume Nextでエラーハンドリングルーチンを抜けてプロシージャ内の通常のコードに戻ると、そのプロシージャの冒頭で設定されていたOn Errorステートメントの状態が自動的に復元されます。エラーハンドリングルーチンの冒頭でOn Error GoTo 0を設定していたとしても、Resume系でルーチンを抜ければ、ルーチンに入る直前のOn Error GoTo lineの状態に戻ります。
VBScriptの場合: VBScriptでは、エラーハンドリングのスコープはVBAほど厳密にプロシージャに限定されない場合があります。特にモジュールレベルでOn Error Resume Nextが設定されているような環境では、Resume系でルーチンを抜けても、呼び出し元のエラーハンドリング状態が必ずしも自動的に復元されるとは限りません。
共通の安全なプラクティスとして:
VBA、VBScriptのどちらにおいても、エラーハンドリングルーチンを抜ける直前に明示的にOn Error GoTo 0または次の処理に必要なOn Errorステートメントを設定し直すスタイルは、コードの意図を明確にし、予期せぬ挙動を防ぐ上で有効です。特にExit Sub/Exit Functionでプロシージャを終了する場合、ルーチン内でOn Error GoTo 0を実行しておけば、プロシージャ終了時にそのハンドラ設定がクリアされることが保証されます。
“`vba
ErrHandler:
On Error GoTo 0 ‘ ルーチン内でのエラーに備える
‘ エラー処理…
MsgBox “エラー発生: ” & Err.Description
‘ エラー情報のクリア
Err.Clear
‘ 復帰方法を決定
‘ Resume Next ‘ 次の行から再開する場合
‘ Resume ‘ エラー行を再実行する場合 (原因を取り除いた後など)
‘ Exit Sub ‘ プロシージャを終了する場合
‘ どの方法で抜けるにせよ、エラーハンドリングの状態を制御する
‘ (VBAではResume/Resume Nextで戻ると自動復元されるが、明示的な設定も検討)
‘ 例: Resume Next の直前に再度ハンドラを設定 (VBAでは不要な場合が多いが、意図を明確にできる)
‘ On Error GoTo ErrHandler ‘ エラーハンドリングを再度有効にする (この例では元の状態に戻す)
‘ Resume Next
‘ 例: Exit Sub で抜ける前に On Error GoTo 0 を再確認 (重要)
On Error GoTo 0 ‘ プロシージャ終了時のエラーハンドリングをデフォルトに戻す
Exit Sub
“`
最も一般的なパターンは、「エラーハンドリングルーチンの冒頭でOn Error GoTo 0を設定し、エラー処理を行った後、Err.Clearでエラー情報をクリアし、Exit Sub/Exit Functionでプロシージャを終了する」というものです。このパターンであれば、エラーハンドリングルーチン内でのエラーを防ぎ、プロシージャ終了時にはエラーハンドリング状態がクリーンにリセットされます。
5. On Error Resume Next と On Error GoTo 0 の連携
On Error Resume Nextは、エラーが発生した行を単にスキップして次の行から実行を継続させるステートメントです。これは非常に手軽なエラー処理方法ですが、エラーを「無視する」ことにつながるため、安易な多用は危険です。しかし、特定の予期されるエラーを回避したり、処理の続行を優先したりする場合には有用です。
On Error GoTo 0は、On Error Resume Nextと組み合わせて使用することで、その影響範囲を限定する役割を果たします。
5.1 On Error Resume Next の挙動と注意点
On Error Resume Nextが有効な状態では、実行時エラーが発生してもプログラムは停止しません。エラーが発生したかどうかは、ErrオブジェクトのNumberプロパティがゼロ以外になっているかどうかを確認することで判断できます。
“`vba
On Error Resume Next ‘ 以降でエラーが発生しても次の行に進む
Dim result As Variant
result = 10 / 0 ‘ ゼロ除算エラーが発生するが停止しない
If Err.Number <> 0 Then
MsgBox “エラーが発生しました: ” & Err.Description
Err.Clear ‘ エラー情報をクリアしないと、以降のErr.Numberチェックに影響する
Else
MsgBox “結果: ” & result ‘ ここには到達しない (ゼロ除算で結果が不定のため)
End If
‘ ここに別のエラーが発生する可能性のあるコードがあった場合、
‘ On Error Resume Next が引き続き有効であることに注意
“`
On Error Resume Nextの最も危険な点は、「エラーを見過ごしてしまう可能性がある」ことです。エラーが発生したことをチェックせず、そのまま処理を続行してしまうと、そのエラーが後続の処理に悪影響を及ぼしたり、最終的に意図しない結果を生み出したりする可能性があります。また、エラーが発生した箇所が特定しにくくなるため、デバッグも困難になります。
そのため、On Error Resume Nextを使用する場合は、以下の点を強く意識する必要があります。
- 使用範囲を限定する: プログラム全体で
On Error Resume Nextを有効にしっぱなしにしない。 - エラーが発生したか必ずチェックする:
On Error Resume Nextが有効なコードブロックの直後で、If Err.Number <> 0 Then ...のようにエラー発生をチェックする。 - チェック後はエラー情報をクリアする:
Err.Clearを呼び出し、次のエラーチェックに備える。
5.2 On Error GoTo 0 を使用した On Error Resume Next の範囲限定
On Error GoTo 0は、On Error Resume Nextの効果を特定のコードブロックに限定するために非常に役立ちます。
“`vba
Sub LimitedResumeNextExample()
‘ このプロシージャ全体ではデフォルトのエラー処理(停止)が有効 (または呼び出し元の設定)
‘ On Error GoTo SomeHandler ‘ もし他のハンドラが有効ならそれ
Debug.Print “処理開始”
‘ — 特定の処理ブロックだけ Resume Next を有効にする —
On Error Resume Next ‘ この行以降でエラーが発生しても次の行に進む
‘ ここにエラーが発生する可能性のある処理を書く
‘ 例: ファイル操作、外部オブジェクト呼び出しなど、失敗が許容される可能性のある処理
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”) ‘ エラーの可能性あり (環境依存など)
‘ 失敗したかどうかチェックする
If Err.Number <> 0 Then
MsgBox “FileSystemObjectの作成に失敗しました: ” & Err.Description
Err.Clear ‘ エラー情報をクリア
‘ 以降の処理はファイル操作を行わない前提で進めるか、ここで終了するか判断
Set fso = Nothing ‘ オブジェクト変数を解放
‘ Exit Sub ‘ 例: 失敗したら終了
Else
‘ オブジェクトが正常に作成された場合の処理
MsgBox “FileSystemObjectを正常に作成しました。”
‘ ファイル操作などを続行
‘ …
End If
‘ — Resume Next の効果を終了させる —
On Error GoTo 0 ‘ ここでエラーハンドリングをデフォルトに戻す (または元の設定に戻す)
‘ ここから先は、もし別のエラーが発生した場合、デフォルト動作で停止する
‘ (On Error GoTo 0 に戻したので、ErrHandlerがあればそちらへはジャンプしない)
Debug.Print “処理終了”
‘ — 以降のコード —
‘ ここで発生するエラーは、On Error GoTo 0 の効果によりデフォルト動作となる
‘ Dim test As Integer
‘ test = 1 / 0 ‘ ここでエラーが発生すると、この行で停止する
End Sub
“`
このパターンでは、On Error Resume NextのスコープがOn Error Resume NextステートメントからOn Error GoTo 0ステートメントまでの間に限定されます。これにより、コードの他の部分で発生する可能性のある重要なエラーまで見落としてしまうリスクを減らすことができます。
また、サブルーチンや関数の中で一時的にOn Error Resume Nextを使用し、そのプロシージャの終了前にOn Error GoTo 0でリセットするという使い方も一般的です。
“`vba
Function TryGetValue(key As String) As Variant
‘ 辞書オブジェクトからの値取得など、キーが存在しない場合にエラーになる可能性のある処理
‘ 呼び出し元のエラーハンドリング状態を一時的に上書き
On Error Resume Next
Dim dict As Object
Set dict = CreateObject(“Scripting.Dictionary”)
dict.Add “A”, 1
dict.Add “B”, 2
‘ キーが存在しない場合、ここでエラーが発生するが停止しない
TryGetValue = dict.Item(key)
‘ Resume Next の効果を終了
On Error GoTo 0 ‘ IMPORTANT!
‘ エラーが発生したかどうかチェック
If Err.Number <> 0 Then
Debug.Print “キー ‘” & key & “‘ が見つかりませんでした: ” & Err.Description
Err.Clear
‘ エラー発生時の戻り値を設定 (例: Nothing, Empty, 特定のエラー値など)
‘ TryGetValue = Empty ‘ キーが見つからなかったことを示す
End If
Set dict = Nothing
End Function
Sub UseTryGetValue()
‘ ここではデフォルトのエラー処理が有効 (または別のハンドラ)
‘ On Error GoTo MainErrorHandler
Dim valueA As Variant
Dim valueC As Variant
valueA = TryGetValue(“A”) ‘ 成功
If Not IsEmpty(valueA) Then Debug.Print “Value A: ” & valueA
valueC = TryGetValue(“C”) ‘ 失敗 (TryGetValue内でResume Nextで処理される)
If Not IsEmpty(valueC) Then Debug.Print “Value C: ” & valueC
‘ もし TryGetValue の中で On Error GoTo 0 が無かったら、
‘ Resume Next の状態がここに引き継がれてしまう可能性がある (特にVBScriptで注意)
‘ VBAでは関数を抜けると自動的に元のハンドラに戻る可能性が高いが、明示的なリセットは安全
‘ 正常終了
‘ Exit Sub
‘MainErrorHandler:
‘ MsgBox “MainErrorHandler でエラー: ” & Err.Description
‘ On Error GoTo 0
‘ Err.Clear
‘ Exit Sub
End Sub
“`
TryGetValue 関数内でOn Error Resume Nextを使用し、失敗した場合でもプログラムが停止しないようにしています。関数の最後でOn Error GoTo 0を実行することで、その関数内で設定したOn Error Resume Nextの効果が呼び出し元 (UseTryGetValue サブプロシージャ) に引き継がれないようにしています。これにより、呼び出し元のプロシージャは自身のエラーハンドリング設定(この例ではデフォルト動作)を維持できます。
6. On Error GoTo 0 を使用する「正しい」状況と理由
これまでの説明を踏まえ、On Error GoTo 0を具体的にどのような状況で使用するのが適切なのか、その理由とともにまとめます。
- エラーハンドリングルーチンから抜ける前:
- 理由: エラーハンドリングルーチン内でエラーが発生する可能性に備えるため、およびルーチンを抜けた後のエラー処理状態を明確にするため。VBAでは
Resume系で抜けた場合は自動復元される可能性が高いですが、Exit Sub/Functionで抜ける場合は、ルーチン内で設定したOn Error GoTo 0が確実に適用されます。特にVBScriptでは、プロシージャ終了時のエラーハンドリング状態のリセットがより重要になる場合があります。 - 使用例:
vba
ErrHandler:
On Error GoTo 0 ' ルーチン内エラー対策
' ... エラー処理 ...
Err.Clear
On Error GoTo 0 ' プロシージャ終了時のハンドラをデフォルトに戻す
Exit Sub
- 理由: エラーハンドリングルーチン内でエラーが発生する可能性に備えるため、およびルーチンを抜けた後のエラー処理状態を明確にするため。VBAでは
- 限定的な
On Error Resume Nextブロックの終了:- 理由:
On Error Resume Nextの効果を特定のコードブロックに限定し、それ以外の場所で発生する重要なエラーの見落としを防ぐため。 - 使用例:
vba
On Error Resume Next
' 特定の処理 (エラーが予期される、無視しても良いもの)
On Error GoTo 0
' エラーチェック (If Err.Number <> 0 Then...)
' Err.Clear
' 以降の処理 (ここでエラーが発生したらデフォルト動作)
- 理由:
- デバッグ時:
- 理由: デフォルトのエラー停止動作に戻すことで、エラーがどこで、なぜ発生したかを正確に把握しやすくなります。開発中は積極的に
On Error GoTo 0を使用したり、コードの特定の箇所で一時的に有効にしたりすると便利です。 - 使用例: 問題の発生箇所を絞り込みたいコードの冒頭に一時的に挿入する。
vba
Sub DebuggingPhase()
On Error GoTo 0 ' デバッグ中はエラー発生で停止させる
' ... 怪しい処理 ...
' デバッグが終わったら On Error GoTo SomeHandler に戻す
End Sub
- 理由: デフォルトのエラー停止動作に戻すことで、エラーがどこで、なぜ発生したかを正確に把握しやすくなります。開発中は積極的に
- サブルーチン/関数の終了時:
- 理由: 呼び出し元のプロシージャで設定されているエラーハンドリングの状態に影響を与えないようにするため。関数/サブルーチン内で一時的に独自のエラーハンドリング(
On Error GoTo lineやOn Error Resume Next)を設定した場合、そのプロシージャが終了する前にOn Error GoTo 0でリセットすることで、呼び出し元のエラーハンドリング設定が意図せず変更されたままになることを防ぎます。VBAではプロシージャスコープのため自動リセットされる傾向がありますが、VBScriptなどではそうでない場合があるため、明示的なリセットは安全策となります。 - 使用例:
vba
Sub SomeTask()
On Error GoTo TaskErrorHandler
' ... 処理 ...
On Error GoTo 0 ' プロシージャ終了前のクリーンアップ
Exit Sub
TaskErrorHandler:
On Error GoTo 0 ' ルーチン内エラー対策
' ... エラー処理 ...
Err.Clear
On Error GoTo 0 ' プロシージャ終了前のクリーンアップ
Exit Sub
End Sub
特に、Resume系のステートメントを使わずにExit Sub/Functionでプロシージャを終了する場合、プロシージャ終了時のエラーハンドリング状態は、Exit Sub/Functionが実行された時点でのOn Errorステートメントの設定に依存します。そのため、エラーハンドリングルーチンを抜ける際、あるいは正常終了するパスでも、プロシージャの終了直前にOn Error GoTo 0を設定しておくと、常にデフォルトの状態に戻ってから呼び出し元に戻るという統一された挙動になり、安全です。
- 理由: 呼び出し元のプロシージャで設定されているエラーハンドリングの状態に影響を与えないようにするため。関数/サブルーチン内で一時的に独自のエラーハンドリング(
7. On Error GoTo 0 の「注意点」と「避けるべき」使い方
On Error GoTo 0は強力なツールですが、その性質を理解しないと問題を引き起こします。
-
無計画な使用:
- 問題点: コードの冒頭で安易に
On Error GoTo 0を設定すると、以降発生する全てのエラーがデフォルトの停止動作に戻ってしまいます。これにより、意図したエラーハンドリングルーチンが実行されず、プログラムが予期せず停止する可能性があります。また、On Error GoTo lineを設定した後に、エラーハンドリングルーチンに入る前にOn Error GoTo 0を実行してしまうと、エラーハンドリングが無効になり、エラー発生時にデフォルト停止となります。 -
避けるべき例:
“`vba
‘ BAD PRACTICE
Sub ProblematicExample()
On Error GoTo 0 ‘ 安易にここでデフォルトに戻してしまうと…
On Error GoTo MyErrorHandler ‘ … この設定が無効になる!Dim x As Integer
x = 1 / 0 ‘ ここでエラーが発生しても MyErrorHandler にはジャンプしないExit Sub
MyErrorHandler:
‘ このコードは実行されない
MsgBox “エラー!”
End Sub
``On Error
* **正しい考え方:**ステートメントは、その行以降のコードに適用されます。On Error GoTo 0は、それまで設定されていたOn Errorステートメントを無効化します。したがって、**有効にしたいOn Errorステートメントよりも後ろにOn Error GoTo 0を配置する必要があります。**On Error Resume Next
2. **エラーの見落とし:**
* **問題点:**とOn Error GoTo 0を組み合わせたブロックで、エラー発生時のチェック (If Err.Number <> 0 Then … Err.Clear) を怠ると、エラーが発生したことを見落とし、その後の処理に影響を及ぼす可能性があります。On Error Resume Next
* **注意点:**を使用する際は、必ずセットでエラーチェックとErr.Clearを記述することを習慣づけましょう。On Error GoTo 0は単にハンドリング設定をデフォルトに戻すだけで、エラーが発生したという事実やErrオブジェクトの状態はクリアしません。On Error Resume Next
3. **呼び出し元のエラーハンドリングを無効化する可能性 (特にVBScript):**
* **問題点:** 関数やサブルーチン内で一時的に設定したエラーハンドリング(特に)をプロシージャ終了時にOn Error GoTo 0でリセットしないまま抜けると、呼び出し元のプロシージャで設定されていたエラーハンドリング状態が上書きされたままになる可能性があります。VBAではプロシージャスコープのおかげでこの問題は起きにくいですが、VBScriptではスクリプト/モジュールレベルのハンドラ設定が影響する場合があります。Exit Sub/Function
* **対策:** 前述の「正しい使い方」の項で説明したように、プロシージャの終了直前(の直前)で明示的にOn Error GoTo 0を設定するスタイルを推奨します。これにより、常にクリーンな状態で呼び出し元に戻ることができます。Err
4. **オブジェクトの状態を混同する:**On Error GoTo 0
* **問題点:**を実行しても、Errオブジェクトに格納されている直前のエラー情報はクリアされません。エラーが発生したかどうかを判断するためにErr.Numberをチェックする場合、前回の処理で発生したエラー情報が残っていると、誤った判断をしてしまう可能性があります。On Error Resume Next
* **対策:** エラーハンドリングルーチンでエラーを処理した後、またはでエラーチェックを行った後は、必ずErr.Clearメソッドを呼び出してErr`オブジェクトをリセットしましょう。
“`vba
‘ BAD PRACTICE
On Error Resume Next
Dim x As Integer
x = 1 / 0 ‘ エラー発生 (Err.Number = 11)
On Error GoTo 0 ‘ ハンドラ無効化 (Err.Number は 11 のまま)‘ … 何らかの処理 …
‘ 次の処理でErr.Numberをチェックしたいが、前のエラー情報が残っている
Dim y As Integer
y = 20 / 2 ‘ この行ではエラーは発生しないIf Err.Number <> 0 Then ‘ ここで Err.Number が 11 なので真になってしまう!
MsgBox “何かエラーが発生しました。”
‘ Err.Clear が呼ばれていないため、誤検知となる
End If
* **正しい使い方:**vba
On Error Resume Next
Dim x As Integer
x = 1 / 0 ‘ エラー発生 (Err.Number = 11)
On Error GoTo 0 ‘ ハンドラ無効化If Err.Number <> 0 Then ‘ エラーを検知
MsgBox “ゼロ除算エラーが発生しました。”
Err.Clear ‘ エラー情報をクリア!
End If‘ … 何らかの処理 …
Dim y As Integer
y = 20 / 2 ‘ この行ではエラーは発生しない (Err.Number は 0)If Err.Number <> 0 Then ‘ ここでは Err.Number が 0 なので偽となり、正しい
MsgBox “何かエラーが発生しました。”
End If
``Err.Clearは、エラーハンドリングのステータス (On Error GoTo 0などで設定) とは独立して、発生したエラーに関する情報(エラー番号、説明など)を保持するErr`オブジェクトの状態をリセットするものです。この二つを混同しないことが重要です。 - 問題点: コードの冒頭で安易に
8. 実践的な On Error GoTo 0 の使い方パターン
これまでの説明を踏まえ、実際のコードでよく見られる、あるいは推奨されるOn Error GoTo 0の使い方パターンをいくつか紹介します。
パターン1:標準的な On Error GoTo line との組み合わせ
最も基本的で安全なパターンです。エラーが発生したら特定のエラーハンドリングルーチンにジャンプし、そこでエラーを処理した後にプロシージャを終了します。
“`vba
Sub ProcessDataFromFile(filePath As String)
‘ プロシージャ全体のDidEnterrorハンドリングを設定
On Error GoTo ProcessDataError
Dim fileNum As Integer
Dim lineData As String
fileNum = FreeFile
Open filePath For Input As #fileNum ‘ ファイルオープンエラーの可能性
‘ ファイルからデータを読み込む処理
Do While Not EOF(fileNum)
Line Input #fileNum, lineData
‘ lineData を処理するコード (ここで別のエラーが発生する可能性)
‘ 例: CInt(lineData) ‘ 文字列を数値に変換する際にエラーになる可能性
Dim numValue As Integer
numValue = CInt(lineData) ‘ 型変換エラー(実行時エラー 13)の可能性
' ... 処理 ...
Debug.Print "Processed: " & numValue
Loop
Close #fileNum ‘ ファイルクローズエラーの可能性
MsgBox “データ処理が正常に完了しました。”
‘ 正常終了時の処理 (エラーハンドリングルーチンへのジャンプを防ぐ)
On Error GoTo 0 ‘ 正常終了時もハンドラをリセットする (安全策)
Exit Sub
ProcessDataError: ‘ エラーハンドリングルーチン
‘ — エラーハンドリングルーチン開始 —
‘ IMPORTANT: ルーチン内でのエラーに備える
On Error GoTo 0
‘ エラー情報の取得
Dim errNumber As Long
Dim errDescription As String
Dim errSource As String
errNumber = Err.Number
errDescription = Err.Description
errSource = Err.Source
‘ エラー情報のログ記録や表示
Debug.Print “— Error Detected —”
Debug.Print “Number: ” & errNumber
Debug.Print “Description: ” & errDescription
Debug.Print “Source: ” & errSource
Debug.Print “———————“
MsgBox “データ処理中にエラーが発生しました。” & vbCrLf & _
“内容: ” & errDescription & vbCrLf & _
“エラー番号: ” & errNumber, vbCritical
‘ 開いているファイルがあれば閉じるなどの後処理を行う (エラー発生箇所に関わらず)
If fileNum <> 0 And Not EOF(fileNum) Then ‘ EOFでエラーになる可能性もあるので注意
‘ ここでも On Error Resume Next などでエラーを回避するケースもある
On Error Resume Next
Close #fileNum
On Error GoTo 0 ‘ Resume Nextの効果をここで終わらせる
End If
‘ エラー情報のクリア
Err.Clear
‘ ルーチン終了。多くの場合、ここでプロシージャを終了する。
‘ NOTE: Resume系の使用は、エラーの原因を取り除ける場合に限る
‘ Resume Next ‘ 例外的なケース、処理をスキップして続行
On Error GoTo 0 ‘ プロシージャ終了時のハンドラをリセットする
Exit Sub ‘ 通常はここで終了
End Sub
``On Error GoTo ProcessDataError
このパターンでは、で設定されたハンドラは、エラーハンドリングルーチンに入るとその効果が一時的に停止し、ルーチン内のOn Error GoTo 0が有効になります。ルーチン内でのエラー処理が完了し、Exit Subでプロシージャを終了する前に再度On Error GoTo 0を設定することで、確実にデフォルトのエラー処理状態に戻ってから呼び出し元に戻ります。正常終了のパスでもExit Subの直前にOn Error GoTo 0`を設定しておくと、コードのどの終了パスを通ってもエラーハンドリング状態がリセットされるという一貫性を持たせられます。
パターン2:限定的な On Error Resume Next ブロック
特定の、失敗する可能性のある処理を単独で実行し、その成否をチェックしたい場合に有用です。
“`vba
Sub SafeObjectCreation()
‘ デフォルトのエラー処理が有効 (または他のハンドラ)
Debug.Print “Trying to create object…”
Dim objShell As Object
‘ CreateObject(“WScript.Shell”) は常に成功するとは限らない (権限、OSなど)
‘ このブロックだけ Resume Next を有効にする
On Error Resume Next
Set objShell = CreateObject(“WScript.Shell”)
On Error GoTo 0 ‘ Resume Next の効果を終了
‘ オブジェクト作成が成功したかチェック
If Err.Number <> 0 Then
Debug.Print “Error creating WScript.Shell: ” & Err.Description
MsgBox “システムオブジェクトの作成に失敗しました。”, vbExclamation
Err.Clear ‘ エラー情報をクリア
Set objShell = Nothing ‘ オブジェクト変数を開放
Else
Debug.Print “WScript.Shell object created successfully.”
‘ オブジェクトを使用した処理
objShell.Run “notepad.exe”
Set objShell = Nothing ‘ オブジェクト変数を開放
End If
Debug.Print “Processing continued after object creation attempt.”
‘ ここで別のエラーが発生した場合、デフォルト動作で停止する
‘ Dim x As Integer
‘ x = 1 / 0 ‘ この行で停止する
End Sub
``CreateObjectのように、実行環境によって成功したり失敗したりする可能性のある処理に対して、このパターンは有効です。On Error Resume NextとOn Error GoTo 0で挟むことで、その処理中にエラーが発生してもプログラムは停止せず、直後のIf Err.Number <> 0`ブロックでエラーを検知し、代替処理やエラー通知を行えます。このブロックを抜けるとエラーハンドリングはデフォルトに戻るため、以降のコードでの予期せぬエラーは通常通り検出されます。
パターン3:サブルーチン/関数内での一時的なエラーハンドリングとリセット
プロシージャ内で局所的にエラーハンドリングを設定し、それが呼び出し元のハンドラに影響を与えないようにするパターンです。
“`vba
Sub MainProcedure()
‘ メインのエラーハンドリングを設定
On Error GoTo MainErrorHandler
Debug.Print “MainProcedure started.”
‘ エラーが発生する可能性のあるサブルーチンを呼び出す
Call SubProcedureWithHandling
Debug.Print “MainProcedure continued after SubProcedure.”
‘ MainProcedure内で別途エラーが発生した場合
‘ Dim y As Integer
‘ y = 20 / 0 ‘ このエラーは MainErrorHandler で捕捉される
Debug.Print “MainProcedure finished normally.”
‘ 正常終了時のハンドラリセット
On Error GoTo 0
Exit Sub
MainErrorHandler:
On Error GoTo 0 ‘ ルーチン内エラー対策
MsgBox “MainProcedureでエラー: ” & Err.Description
Err.Clear
On Error GoTo 0 ‘ 終了時のハンドラリセット
Exit Sub
End Sub
Sub SubProcedureWithHandling()
‘ サブプロシージャ内のエラーハンドリングを設定 (呼び出し元とは独立)
On Error GoTo SubErrorHandler
Debug.Print “SubProcedureWithHandling started.”
‘ ここでエラーが発生する
Dim x As Integer
x = 10 / 0 ‘ ゼロ除算エラー発生
Debug.Print “SubProcedureWithHandling finished normally.” ‘ エラー発生時は到達しない
‘ 正常終了時のハンドラリセット
On Error GoTo 0 ‘ IMPORTANT: サブプロシージャ終了前にハンドラをリセットする
Exit Sub
SubErrorHandler:
‘ サブプロシージャのエラー処理
On Error GoTo 0 ‘ ルーチン内エラー対策
Debug.Print “SubProcedureWithHandlingでエラー: ” & Err.Description
MsgBox “サブ処理でエラーが発生しました。”, vbExclamation
‘ エラー情報のクリア
Err.Clear
‘ IMPORTANT: サブプロシージャ終了前にハンドラをリセットする
On Error GoTo 0
Exit Sub ‘ サブプロシージャを終了
End Sub
``MainProcedure
この例では、とSubProcedureWithHandlingはそれぞれ独立したエラーハンドリング設定を持っています。SubProcedureWithHandling内で発生したエラーはSubErrorHandlerで捕捉され処理されます。SubErrorHandlerの終了時およびSubProcedureWithHandlingの正常終了直前でOn Error GoTo 0を実行することで、SubProcedureWithHandlingの処理が完了してMainProcedureに戻った際に、MainProcedureで設定されていたOn Error GoTo MainErrorHandler`が有効な状態に戻ります。これにより、各プロシージャが独立してエラーを管理できるようになります。
特にVBScriptでは、プロシージャの終了時にエラーハンドリングの状態が自動的にリセットされる保証がない場合があります。したがって、VBScriptにおいては、プロシージャの終了直前にOn Error GoTo 0を設定する習慣はさらに重要になります。
9. 高度なトピックと関連事項
9.1 VBAとVBScriptにおけるエラーハンドリングのスコープ
前述の通り、VBAとVBScriptではエラーハンドリングのスコープに違いがあります。
- VBA:
On Errorステートメントの効果は、原則としてそれが記述されたプロシージャ内のみに限定されます。あるプロシージャ内でOn Error GoTo lineを設定しても、そのプロシージャが別のプロシージャを呼び出した場合、呼び出し先のプロシージャは独自のOn Error設定を持つことができます。呼び出し先プロシージャの実行が終了して呼び出し元に戻ると、呼び出し元プロシージャで設定されていたOn Error状態が自動的に復元されます。エラーハンドリングルーチンからResumeやResume Nextで抜けた場合も、プロシージャ内の通常コードに戻れば元のOn Error設定が復元されます。 - VBScript: VBScriptでは、
On Errorステートメントの効果は、それが記述されたスクリプト、またはクラスやモジュールのスコープに及びます。プロシージャ内で設定されたOn Errorステートメントは、そのプロシージャ内だけでなく、そのプロシージャが呼び出す別のプロシージャにも影響を与える可能性があります。また、プロシージャが終了しても、そのプロシージャ内で設定されたOn Error状態がそのまま引き継がれてしまうことがあります。このため、VBScriptでは、関数やサブルーチンの開始時に独自のエラーハンドリングを設定し、終了時にOn Error GoTo 0で明示的にリセットすることが、呼び出し元の意図しない挙動を防ぐ上で非常に重要になります。ASPなど、環境によってはデフォルトでOn Error Resume Nextが有効になっている場合もあります。
どちらの言語を使うにしても、プロシージャ内で一時的にエラーハンドリング設定を変更した場合、プロシージャの終了前にOn Error GoTo 0でリセットするという習慣は、コードの可読性と保守性を高め、潜在的な問題を回避するための良いプラクティスと言えます。
9.2 エラーハンドリングルーチン内でのエラー再発生 (Err.Raise)
エラーハンドリングルーチンでエラーを捕捉した後、そのエラーを完全に隠蔽するのではなく、呼び出し元にエラーが発生したことを伝えたい場合があります。これは、エラー処理の一部を呼び出し元に委ねたり、より上位のレベルで一元的なエラー処理を行いたい場合に有効です。
Err.Raiseメソッドを使用すると、指定したエラー番号と説明で新たな(または元と同じ)エラーを発生させることができます。
“`vba
Sub OuterProcedure()
On Error GoTo OuterErrorHandler
Debug.Print “Outer started.”
Call InnerProcedure ‘ InnerProcedure でエラーが発生する
Debug.Print “Outer finished.”
On Error GoTo 0
Exit Sub
OuterErrorHandler:
On Error GoTo 0 ‘ ルーチン内対策
MsgBox “OuterProcedure caught error: ” & Err.Description
Err.Clear
On Error GoTo 0
Exit Sub
End Sub
Sub InnerProcedure()
On Error GoTo InnerErrorHandler
Debug.Print “Inner started.”
Dim z As Integer
z = 10 / 0 ‘ エラー発生
Debug.Print “Inner finished.” ‘ 到達しない
On Error GoTo 0
Exit Sub
InnerErrorHandler:
On Error GoTo 0 ‘ ルーチン内対策
Debug.Print “InnerProcedure caught error: ” & Err.Description
‘ エラーを処理したが、呼び出し元にも伝える必要がある場合
‘ 元のエラー情報を再発生させる
Err.Raise Err.Number, Err.Source, Err.Description ‘ Err.Sourceは適切に設定する必要がある場合あり
‘ IMPORTANT: Err.Raise を実行すると、その時点で InnerProcedure のエラーハンドリングは終了し、
‘ 呼び出し元 (OuterProcedure) にエラーが伝播する。
‘ OuterProcedure は自身に設定された On Error GoTo OuterErrorHandler に従って処理を行う。
‘ Err.Clear は通常、Err.Raise の後には行わない (呼び出し元でエラー情報が必要なため)
‘ On Error GoTo 0 ‘ これは不要 (Err.Raiseでハンドリング状態はリセットされる)
‘ Exit Sub ‘ unreachable code if Err.Raise is successful
End Sub
``InnerErrorHandler内でErr.Raiseを実行すると、InnerProcedureの実行は事実上中断され、発生したエラーは呼び出し元のOuterProcedureに伝播します。OuterProcedureは、そのエラーを自身のOn Error GoTo OuterErrorHandler`設定に従って捕捉し、処理します。
Err.Raiseを実行する際には、その時点で現在有効なエラーハンドリング(InnerErrorHandler内のOn Error GoTo 0を含む)が終了し、エラーが上位に伝播するという挙動を理解しておくことが重要です。
9.3 エラーロギングとの組み合わせ
実践的なアプリケーションでは、エラー発生時にユーザーにメッセージを表示するだけでなく、エラーの詳細をファイルなどに記録(ロギング)することが非常に重要です。これにより、ユーザーからの問い合わせがあった際や、定期的な保守作業で、発生した問題を正確に把握し、原因究明や対策を行うことが可能になります。
エラーハンドリングルーチン内でErrオブジェクトからエラー情報を取得し、それをログファイルに書き込むコードを実装します。
“`vba
‘ このログ書き込み関数自体もエラーが発生する可能性があるので注意
‘ (ファイルパス不正、書き込み権限なしなど)
‘ ログ関数内では On Error Resume Next + Err.Clear を使用するか、
‘ シンプルに On Error GoTo 0 でデフォルト停止に戻すのが良い
Sub WriteErrorLog(errNum As Long, errMsg As String, errSource As String)
On Error Resume Next ‘ ログ書き込み自体のエラーは見過ごす場合
Dim fso As Object
Dim logFile As Object
Dim logFilePath As String
‘ ログファイルのパスを設定 (必要に応じて調整)
logFilePath = Environ(“TEMP”) & “\error_log_” & Format(Date, “yyyymmdd”) & “.log”
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルが存在しない場合は作成、存在すれば追記
If fso.FileExists(logFilePath) Then
Set logFile = fso.OpenTextFile(logFilePath, 8) ‘ 8 = ForAppending
Else
Set logFile = fso.CreateTextFile(logFilePath, True)
End If
‘ ログエントリーを作成
Dim logEntry As String
logEntry = Format(Now, “yyyy-mm-dd hh:nn:ss”) & _
” [Error ” & errNum & “] Source: ” & errSource & _
“, Description: ” & errMsg
logFile.WriteLine logEntry
logFile.Close
Set logFile = Nothing
Set fso = Nothing
‘ ログ書き込みでエラーが発生した場合のチェック (Resume Next の場合)
If Err.Number <> 0 Then
‘ ログ書き込み自体のエラーは深刻でないと判断し、コンソールに出力するなど
Debug.Print “Error writing log file: ” & Err.Description
Err.Clear
End If
On Error GoTo 0 ‘ Resume Next の効果を終了 (関数の終了前にリセット)
End Sub
Sub ExampleWithErrorLogging()
On Error GoTo MyErrorHandler
Debug.Print “Processing with logging…”
Dim data As Variant
‘ エラーが発生する可能性のある処理
‘ data = ReadDataFromDatabase() ‘ DB接続エラーなど
data = 10 / 0 ‘ ゼロ除算エラー
Debug.Print “Processing finished.”
On Error GoTo 0
Exit Sub
MyErrorHandler:
On Error GoTo 0 ‘ ルーチン内エラー対策
‘ エラー情報の取得
Dim errNumber As Long
Dim errDescription As String
Dim errSource As String
errNumber = Err.Number
errDescription = Err.Description
errSource = Err.Source ‘ Err.SourceはVBAコード上のエラーではプロシージャ名など
‘ エラーをログに記録
Call WriteErrorLog(errNumber, errDescription, errSource)
‘ ユーザーへの通知
MsgBox “予期せぬエラーが発生しました。詳細はログファイルを確認してください。”, vbCritical
‘ エラー情報のクリア
Err.Clear
On Error GoTo 0 ‘ 終了時のハンドラリセット
Exit Sub
End Sub
``WriteErrorLog
この例のように、エラーハンドリングルーチン内でログ記録関数を呼び出すことで、発生した全てのエラーをファイルに記録できます。関数自体もエラーが発生する可能性があるので、その関数内でも適切なエラー処理(この例ではOn Error Resume NextとErr.Clearを使用)を行うことが重要です。そして、ログ記録関数の終了時にもOn Error GoTo 0でハンドリング状態をリセットすることで、呼び出し元(MyErrorHandler`)のハンドリング状態に影響を与えないようにしています。
10. 代替手法とベストプラクティス
VBA/VBScriptの非構造化エラーハンドリング(On Error GoTo)は、柔軟性がある一方で、コードのジャンプが多くなり、複雑なエラー回復ロジックを実装しようとするとコードが読みにくく、保守しにくくなるという欠点があります。また、エラーハンドリングルーチンにジャンプした時点では、エラーが発生した具体的な行番号や完全なコールスタック(どのプロシージャを辿ってエラー発生箇所に至ったか)を取得することが困難という限界もあります。
このような限界を踏まえ、エラーハンドリングだけに頼るのではなく、コード全体の堅牢性を高めるためのベストプラクティスも重要です。
-
事前の検証: 処理を実行する前に、必要なファイルが存在するか、入力データは正しい形式か、オブジェクト参照は有効かなどを事前にチェックします。予期される失敗はエラーハンドリングではなく、条件分岐(
If...Then)で処理する方がコードは読みやすくなります。
“`vba
‘ BAD (エラーハンドリングに頼る)
On Error Resume Next
Set obj = GetObject(“NonExistentObject”)
If Err.Number <> 0 Then
MsgBox “エラー”
Err.Clear
End If
On Error GoTo 0‘ GOOD (事前チェック)
Set obj = Nothing
On Error Resume Next ‘ GetObject自体でエラーになる可能性も考慮するなら必要
Set obj = GetObject(“NonExistentObject”)
On Error GoTo 0
If Err.Number <> 0 Then
‘ GetObjectに失敗した場合の処理
MsgBox “オブジェクトを取得できませんでした。”
Err.Clear
ElseIf obj Is Nothing Then ‘ GetObjectがエラーにならなかったがオブジェクトが取得できなかった場合
MsgBox “オブジェクトを取得できませんでした (GetObjectはエラーにならなかったが)。”
Else
‘ オブジェクトを取得できた場合の処理
‘ …
End If
``On Error Resume Next
* **の乱用を避ける:** 特定の行でエラーが発生しても無視して続行したい場合に限定して使用し、必ず直後にエラーチェックとErr.Clearを行います。On Error GoTo 0
* **エラーハンドリングルーチンをシンプルに保つ:** エラー通知、ログ記録、リソース解放などの基本的な処理に限定し、複雑なビジネスロジックは記述しないようにします。
* **プロシージャの終了前にを設定する習慣をつける:** 特にVBScript環境での安全性を高めます。VBAでもコードの意図を明確にする上で有効です。Err.Number
* **リソースの解放を確実に行う:** ファイル、データベース接続、オブジェクト参照などのリソースは、エラーが発生した場合でも適切にクローズまたは解放されるように、エラーハンドリングルーチン内で解放処理を記述します。
* **エラーメッセージを具体的にする:**やErr.Description`を活用し、何が起こったのか、可能であればどう対処すべきかをユーザーに伝えられるようにします。開発者向けにはログ記録がさらに重要です。
11. まとめ
On Error GoTo 0は、VBA/VBScriptにおけるエラーハンドリングを制御する上で不可欠なステートメントです。その主な役割は、それまで有効になっていたOn Error GoTo lineやOn Error Resume Nextによるエラーハンドリングを無効化し、デフォルトの「エラー発生時にプログラムを停止する」状態に戻すことです。
この記事で解説したように、On Error GoTo 0は単独で使うよりも、他のOn Errorステートメントと組み合わせて使用することが一般的です。
On Error GoTo lineを使用する場合、エラーハンドリングルーチンの冒頭でOn Error GoTo 0を設定することで、ルーチン内で発生する可能性のあるエラーによる予期せぬ停止を防ぐことができます。また、ルーチンを抜ける直前やプロシージャの正常終了直前にOn Error GoTo 0を設定することで、プロシージャ終了時のエラーハンドリング状態をクリーンにリセットし、呼び出し元に影響を与えないようにすることが推奨されます。On Error Resume Nextを使用する場合、その効果を特定のコードブロックに限定するために、ブロックの終了時にOn Error GoTo 0を使用します。これにより、On Error Resume Nextによる影響範囲を制御し、エラーの見落としリスクを軽減できます。ただし、On Error Resume Nextを使用する際は、必ず直後にIf Err.Number <> 0 Then ... Err.Clearの形でエラーチェックとErr.Clearを行うことが必須です。
On Error GoTo 0を実行してもErrオブジェクトに格納されたエラー情報はクリアされない点、そしてVBAとVBScriptでエラーハンドリングのスコープの挙動に違いがある可能性がある点には特に注意が必要です。Err.Clearによる明示的なエラー情報のリセットと、プロシージャ終了時のOn Error GoTo 0によるハンドラ状態のリセットは、コードの堅牢性を高めるための重要な習慣です。
VBA/VBScriptの非構造化エラーハンドリングには限界がありますが、On Error GoTo 0を他のOn Errorステートメントと適切に組み合わせることで、エラーが発生した場合でもプログラムが完全に破綻するのを防ぎ、ユーザーや開発者にとってより扱いやすいアプリケーションを作成することができます。コードの各部分で必要とされるエラーハンドリングのレベルを検討し、On Error GoTo 0を賢く活用することで、より信頼性の高いVBA/VBScriptコードを記述できるようになるでしょう。
12. 参考文献
- Microsoft Docs: On Error ステートメント (VBA) – https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/on-error-statement
- Microsoft Docs: On Error Statement (VBScript) – https://docs.microsoft.com/ja-jp/previous-versions//sbf5ze0a(v=vs.85)
- Microsoft Docs: Err Object – https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/err-object
(これらの参考文献は、より深く学ぶための出発点となります。実際の開発では、具体的なエラー番号や発生状況に応じた固有の対処法を学ぶことも重要です。)
免責事項: この記事は一般的な情報提供のみを目的としており、特定の環境や状況での動作を保証するものではありません。実際の開発においては、ご自身の環境で十分なテストを行ってください。