マクロ開発者にとってエラーは嫌なものです。できればエラーメッセージは見たくありませんし、対処のしかたに慣れていないと、エラーの原因を解明するために思わぬ時間が必要だったりもします。もちろんエラーは発生させないのが一番ですが、そうも言っていられません。ここでは、エラーを回避するテクニックをご紹介します。
たとえば次のコードは、ワークシート名を取得して表示します。
Sub Sample1() Dim i As Long, buf As String For i = 1 To 4 buf = buf & Worksheets(i).Name & vbCrLf Next i MsgBox buf End Sub
しかし、For Nextの繰り返しを4回実行しているため、ワークシートが3枚以下だとエラーになります。
For Nextのループ中でエラーが発生してマクロはストップしますので、For Nextの次に書いたMsgBoxは実行されません。エラーが発生してもマクロを停止せず、マクロが発生した次行から処理を継続させるには、マクロが発生するより前で、On Errorステートメントを実行します。この場合「エラーを無視」したいのですから、On Error Resume NextのようにResume Nextをつけます。これはもう、英単語の意味とか理屈ではなく、暗記してください。
Sub Sample1() Dim i As Long, buf As String On Error Resume Next For i = 1 To 4 buf = buf & Worksheets(i).Name & vbCrLf Next i MsgBox buf End Sub
エラーが発生する前にOn Error Resume Nextが実行されていると、エラーが発生したとき、そのエラーが発生した次の命令から処理が継続されます。たとえば次のコードは、Sheet1~Sheet3までのシート名を取得して表示しますが、もしSheet2が存在しない場合は Worksheets("Sheet" & 2).Name がエラーになります。しかし、For Nextの繰り返しは継続されるので、Sheet3は取得されます。
Sub Sample2() Dim i As Long, buf As String On Error Resume Next For i = 1 To 3 buf = buf & Worksheets("Sheet" & i).Name & vbCrLf Next i MsgBox buf End Sub
エラーが発生したとき、通常の流れとは別の処理をさせたいことがあります。たとえば、あるファイルを開いて、そこに書かれているデータをセルに入力するようなケースでは、何らかの理由でファイルを開けなかったとき、強制的に処理を中断する必要があります。そんなときは、On Error Goto 《ラベル名》 を使います。《ラベル名》にはジャンプ先のラベルを指定します。次のコードは、C:\Sample.dat を開き先頭のデータをセルA1に読み込みます。何らかの理由で C:\Sample.dat を開けなかったときはエラーが発生しますので、ラベル myError にジャンプして処理を分岐しています。
Sub Sample3() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 ''ここでエラーが発生すると Line Input #1, buf Range("A1") = buf Close #1 Exit Sub myError: ''この行にジャンプします MsgBox "ファイルを開けません", vbExclamation End Sub
ラベルは、行頭から任意の文字列(《ラベル名》)を書き、行末にコロン「:」をつけます。
注意しなければならないのは、エラーが発生しなかった場合を想定することです。もしエラーが発生せず、コードの上から順に処理が行われたなら、最後の MsgBox が毎回実行されてしまいます。そこで、エラーが発生しない(ラベルにジャンプしない)ときは、ラベルの手前に Exit Sub と「Subプロシージャを終了させる」コマンドを忘れずに記述します。
※エラーが発生した場合 Sub Sample3() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 ''ここでエラーが発生してmyErrorにジャンプする Line Input #1, buf Range("A1") = buf Close #1 Exit Sub myError: MsgBox "ファイルを開けません", vbExclamation End Sub ''ここで終了する
※エラーが発生しない場合 Sub Sample3() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 Line Input #1, buf Range("A1") = buf Close #1 Exit Sub ''ここで終了する myError: MsgBox "ファイルを開けません", vbExclamation End Sub
もうひとつ重要なポイントがあります。たとえば次のようなケースを考えてみましょう。Sample3で、Sample.datファイルから読み込んだデータをセルA1に入力した後、続けてSheet2の名前を"合計"に変更します。
Sub Sample4() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 Line Input #1, buf Range("A1") = buf Close #1 Worksheets("Sheet2").Name = "合計" ''追加した処理(ここでエラーになる) Exit Sub myError: MsgBox "ファイルを開けません", vbExclamation End Sub
ここでは、C:\Sample.dat は無事に開けた(エラーにならなかった)とします。しかし、このブックにはSheet2が存在しなかったとしたら。Worksheets("Sheet2").Name がエラーになります。で、実行してみると・・・
そう、On Error GoTo myError の後プロシージャが終了するまでは、どんなエラーが発生しても全部 myError にジャンプしてしまうんです。これでは使い勝手がよくありません。そこで「エラーが発生したらジャンプする」に対して「そのジャンプ命令の取り消し」をすることができます。それには On Error GoTo 0 と書きます。最後のは半角のゼロです。On Error Goto 《ラベル名》でエラー発生時に別処理へジャンプさせるときは、予期せぬ場所(エラー)からジャンプしないように注意してください。
もちろん、On Error Goto 0でエラーに対する処理を終了したのですから、Sheet2が存在しないと「Sheet2が存在しない」という別のエラーが発生します。それはそれで、何らかのエラー対策をしなければなりませんね。
エラーが発生すると、どんなエラーが発生したかの情報が Errオブジェクトに格納されます。これは、On Error Resume Nextで「エラーが起きても停止しない」にしていても同様です。On Error Resume Nextは「エラー(で停止すること)を無視する」だけで「エラーを発生させない」命令ではありません。On Error Resume Nextを実行していても、エラーが発生したときには、どんなエラーが発生したかの情報がErrオブジェクトに格納されます。
エラーが発生したときは、Errオブジェクトのプロパティを調べることでどんなエラーが発生したかを調べることができます。Errオブジェクトでよく使われるプロパティとメソッドは次の通りです。これくらいを覚えておけばいいでしょう。
プロパティまたはメソッド | 働き |
---|---|
Number プロパティ | エラーの種類を識別する番号が格納されます |
Description プロパティ | エラーに関する簡単な説明が格納されます |
Clear メソッド | エラーの情報を初期化します |
Numberプロパティには、エラーごとに異なる番号が格納されます。値を設定することもできますが、一般的な使い方では設定しないでしょう。上記Sample3プロシージャを例にすれば、ファイルを開けなかった(Openメソッドが失敗した)ときのNumberプロパティは53で、Worksheets("Sheet2")が存在しなかったときのエラー番号は9です。実際に確認してみましょう。
Sub Sample3() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 ''ここでエラーが発生すると Line Input #1, buf Range("A1") = buf Close #1 Exit Sub myError: ''この行にジャンプします MsgBox "エラー番号:" & Err.Number & vbCrLf & _ "エラーの種類:" & Err.Description, vbExclamation End Sub
エラーが発生したときは、ErrオブジェクトのNumberプロパティにエラーの番号が格納されます。エラーが発生するまで、Numbersプロパティは「0」が入っています。つまり、ErrオブジェクトのNumberプロパティが0でないということは、「エラーが発生した」と判断できるわけです
Sub Sample4() Dim buf As String On Error Resume Next Open "C:\Sample.dat" For Input As #1 Line Input #1, buf Range("A1") = buf Close #1 If Err.Number <> 0 Then MsgBox "エラーが発生しました" & vbCrLf & _ Err.Description, vbExclamation End If Worksheets("Sheet2").Name = "合計" End Sub
注意しなければならないのは、発生したエラーに関する情報は、プロシージャが終了するまでErrオブジェクトに格納され続けるということです。次のケースを考えてみましょう。
という2つの処理に対して、それぞれ「エラーが発生したかどうか」をErrオブジェクトのNumberプロパティで判定します。このとき、
だった場合、どうなるでしょう。
Sub Sample4() Dim buf As String On Error Resume Next Open "C:\Sample.dat" For Input As #1 ''ここでエラー Line Input #1, buf Range("A1") = buf Close #1 If Err.Number <> 0 Then MsgBox "エラーが発生しました" & vbCrLf & _ Err.Description, vbExclamation End If Worksheets("Sheet2").Name = "合計" ''ここは正常 If Err.Number <> 0 Then MsgBox "シート名を変更できませんでした" & vbCrLf & _ Err.Description, vbExclamation End If End Sub
Sheet2の名前変更は正常に完了したにもかかわらず、二度目の「Err.Number <> 0」で"エラーが発生した"と判断されてしまいました。Sample.datが開けたかどうかの「最初のエラー判定」が終わった後は、もうそのエラー情報が不要なのですから、エラー情報をクリアしなければなりません。
Sub Sample4() Dim buf As String On Error Resume Next Open "C:\Sample.dat" For Input As #1 ''ここでエラー Line Input #1, buf Range("A1") = buf Close #1 If Err.Number <> 0 Then MsgBox "エラーが発生しました" & vbCrLf & _ Err.Description, vbExclamation End If Err.Clear Worksheets("Sheet2").Name = "合計" ''ここは正常 If Err.Number <> 0 Then MsgBox "シート名を変更できませんでした" & vbCrLf & _ Err.Description, vbExclamation End If End Sub
Errオブジェクトと、先に解説したOn Error Gotoを併用すると、エラーの種類に応じて適切な対応を準備できます。上記Sample4を例に解説しましょう。ここでは
の、どちらでエラーが発生するか予測できません。そこで、何らかのエラーが発生したら特定のラベルにジャンプして、そこで"どんなエラーが発生したか"を判定します。
Sub Sample4() Dim buf As String On Error GoTo myError Open "C:\Sample.dat" For Input As #1 Line Input #1, buf Range("A1") = buf Close #1 Worksheets("Sheet2").Name = "合計" Exit Sub myError: Select Case Err.Number Case 9 MsgBox "シート名を変更できませんでした" & vbCrLf & Err.Description, vbExclamation Case 53 MsgBox "ファイルを開けませんでした" & vbCrLf & Err.Description, vbExclamation Case Else MsgBox "予期せぬエラーが発生しました", vbExclamation End Select End Sub
これで、エラーが発生すると、
または
または
が表示されます。ポイントは最後の「Case Else節」です。これを加えることで"想定外のエラー"が発生した場合でも、とりあえずはマクロが停止することはありません。