【Excel VBA入門 第17回】On Error Resume NextとResumeを使いこなそう!エラーを無視する・やり直す

スポンサーリンク
Excel VBA

イントロダクション

前回(第16回)は、On Error GoTo でエラーをラベルへジャンプさせ、Err オブジェクトで原因を調べる基本形を学んだ。

今回は、エラー処理のもう一つの柱である「エラーを無視して先へ進む」On Error Resume Next と、「エラーの後に処理を再開する」Resume を取り上げる。あわせて、Err.Clear と On Error GoTo 0 の使い方も押さえておこう。

On Error Resume Nextとは

On Error Resume Next を書くと、エラーが起きてもマクロを止めず、次の行へ進むようになる。エラーの内容は Err オブジェクトに残るため、後から調べることもできる。

使いどころとして分かりやすいのが、「指定したシートが存在するかどうかの確認」である。
存在しないシートを指定すると、通常は実行時エラー(9)になる。この性質を逆手に取って、エラーになるかどうかで存在を判定する。

Function シートが存在する(シート名 As String) As Boolean
    Dim ws As Worksheet

    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(シート名)
    On Error GoTo 0

    シートが存在する = Not ws Is Nothing
End Function

Worksheets(シート名) が失敗しても、エラーは無視されて次の行へ進む。その場合、ws には何も代入されないので Nothing のままである。これを Not ws Is Nothing で判定している。

Sub シート確認()
    If シートが存在する("Sheet1") Then
        MsgBox "Sheet1は存在します。"
    Else
        MsgBox "Sheet1は存在しません。"
    End If
End Sub

シート名を、存在するものと存在しないものに変えて試してみよう。

On Error GoTo 0でエラー処理を解除する

先ほどのコードには、On Error GoTo 0 という行があった。これはそれまでのエラー処理の設定を解除する命令である。

On Error Resume Next を書いたまま放置すると、その後に起きたエラーもすべて無視されてしまう。マクロは止まらないが、途中で失敗しているのに気づけないため、原因の特定が難しくなる。

そのため、On Error Resume Next は次の使い方を守りたい。

  • 無視してよい1行〜数行だけを囲む
  • 囲み終わったらすぐ On Error GoTo 0 で解除する

「マクロ全体の先頭に On Error Resume Next を書いておく」という使い方は避けよう。

Err.Clearでエラー情報をリセットする

On Error Resume Next の後でエラーが起きると、Err.Number にエラー番号が残る。この状態を放置すると、次の判定で「まだエラーが起きている」と誤って判断してしまう。

複数のシートを順番に確認する例で見てみよう。

Sub 複数シートの確認()
    Dim シート名 As Variant
    Dim ws As Worksheet

    On Error Resume Next
    For Each シート名 In Array("売上", "経費", "集計")
        Set ws = Nothing
        Set ws = ThisWorkbook.Worksheets(シート名)

        If Err.Number <> 0 Then
            Debug.Print シート名 & " シートが見つかりません"
            Err.Clear
        Else
            Debug.Print シート名 & " シートがあります"
        End If
    Next シート名
    On Error GoTo 0
End Sub

ポイントは次の2つである。

  • Set ws = Nothing:前のループで取得したシートが残らないよう、毎回リセットしている
  • Err.Clear:見つからなかったシートの後で、エラー情報を消している。これがないと、次のシートが存在していても Err.Number が0にならず、「見つかりません」と表示されてしまう

なお、On Error GoTo 0 は、それまで設定していたエラー処理を解除する命令である。
エラー情報を明示的に消したい場合は、Err.Clear を使用する。

そのため、複数の処理を On Error Resume Next で順番に確認する場合などは、必要に応じて Err.Clear を実行して、前の処理で発生したエラー情報をリセットする。

エラーの有無で処理を分ける

On Error Resume Next と Err.Number を組み合わせれば、「失敗したら別の処理をする」という書き方ができる。ブックを開く例を見てみよう。

Sub ブックを開く()
    Dim wb As Workbook

    On Error Resume Next
    Set wb = Workbooks.Open("C:\test\データ.xlsx")
    If Err.Number <> 0 Then
        MsgBox "ブックを開けませんでした。" & vbCrLf & _
               Err.Description, vbExclamation
        On Error GoTo 0
        Exit Sub
    End If
    On Error GoTo 0

    MsgBox wb.Name & " を開きました。"
End Sub

ファイルが存在しなければメッセージを表示して終了し、存在すれば後続の処理へ進む。
On Error GoTo 0 は、どちらの流れでも解除されるように2か所に書いている。

Resumeでエラーの後に処理を再開する

ここからは、On Error GoTo で飛んだエラー処理ルーチンの中で使う命令である。エラー処理を終えたあとの再開位置を、Resume で指定できる。

  • Resume:エラーが起きた行をもう一度実行する
  • Resume Next:エラーが起きた行の次の行から再開する
  • Resume ラベル名:指定したラベルへジャンプして再開する

On Error Resume Next(エラー発生前に書く設定)と、Resume Next(エラー処理ルーチンの中で書く再開命令)は別物なので、混同しないようにしよう。

Resume の例として、ブックが開けないときに「再試行」できるマクロを作ってみる。

Sub ブックを開くやり直し()
    Dim wb As Workbook

    On Error GoTo エラー処理

    Set wb = Workbooks.Open("C:\test\データ.xlsx")
    MsgBox wb.Name & " を開きました。"
    Exit Sub

エラー処理:
    If MsgBox("ブックを開けませんでした。" & vbCrLf & _
              "ファイルを配置してから、再試行しますか?", _
              vbRetryCancel + vbExclamation) = vbRetry Then
        Resume
    End If
End Sub

ファイルが見つからないとエラー処理に飛び、「再試行 / キャンセル」のダイアログが表示される。ここで、別のウィンドウからファイルを所定の場所に置いて[再試行]を押すと、Workbooks.Open の行がもう一度実行される。[キャンセル]を押せば、そのままマクロが終了する。

Resume は、エラーの原因が解消されないまま実行すると、同じエラーが繰り返されて無限ループになる。今回のように、ユーザーが[キャンセル]で抜けられる仕組みを用意しておこう。

3つの書き方の使い分け

ここまでの内容を整理しておく。

書き方動作主な使いどころ
On Error GoTo ラベルエラー時にラベルへジャンプする想定外のエラーへの備え、メッセージ表示
On Error Resume Nextエラーを無視して次の行へ進む失敗してもよい処理、存在確認
On Error GoTo 0エラー処理の設定を解除するResume Next の範囲を閉じる
Resumeエラーの行をやり直す再試行(エラー処理ルーチン内)

まとめ

今回は、エラーを無視する・やり直す方法を学んだ。

  • On Error Resume Next は、エラーを無視して次の行へ進む
  • 使うときは範囲を最小限にし、On Error GoTo 0 で必ず解除する
  • 複数回の判定では、Err.Clear でエラー情報を消す
  • Resume はエラーの行をやり直し、Resume Next は次の行から再開する
  • Resume は、無限ループにならないよう抜け道を用意する

次回(第18回)は、Err.Raise を使った「エラーを呼び出し元に伝える」方法を解説する。Function の中で起きた問題を、呼び出したSubの側で処理する設計と、途中で失敗しても画面更新などの設定を元に戻す「後始末」の書き方を学んでいこう。

コメント

タイトルとURLをコピーしました