イントロダクション
前回(第17回)は、On Error Resume Next でエラーを無視する方法と、Resume でエラーの行をやり直す方法を学んだ。
今回は、エラー処理編の応用として、次の2つを取り上げる。
Err.Raise:Function の中で見つけた問題を、呼び出したSub側に伝える方法- 後始末:エラーが起きても、
ScreenUpdatingなどの設定を元に戻す書き方
Functionの中でエラーを発生させる
ここで、第16回で作った 割り算 Function を思い出そう。
0で割ると、VBAが用意している「実行時エラー11」が自動的に発生した。
では、「マイナスの数を渡したら不正な値としてエラーにしたい」といった、VBA標準にはないルールを作りたい場合はどうすればよいか。
そこで使うのが Err.Raise である。これは、任意のタイミングで自分からエラーを発生させる命令である。
Function 平方根を求める(値 As Double) As Double
If 値 < 0 Then
Err.Raise Number:=vbObjectError + 1, _
Description:="マイナスの値には対応していません。"
End If
平方根を求める = Sqr(値)
End FunctionNumber:エラー番号。自作のエラーにはvbObjectError + 任意の数値を使うのが決まりである。これにより、VBAが元々使っている番号と重ならなくなるDescription:エラーの内容を表す文字列。Err.Descriptionとして、呼び出し元に伝わる
呼び出し元でエラーを受け取る
Err.Raise で発生させたエラーも、第16回で学んだ On Error GoTo で受け取れる。VBAが自動的に起こすエラーと、扱い方は同じである。
Sub 平方根の計算()
Dim 入力値 As Double
Dim 結果 As Double
On Error GoTo エラー処理
入力値 = CDbl(InputBox("平方根を求める数を入力してください"))
結果 = 平方根を求める(入力値)
MsgBox "平方根は " & 結果 & " です。"
Exit Sub
エラー処理:
MsgBox "計算できませんでした。" & vbCrLf & Err.Description, vbExclamation
End Sub-4 を入力すると、Function 内の Err.Raise で発生したメッセージがそのまま表示される。13(型が一致しません)のような、VBAが用意した番号と自作のエラーを、同じ仕組みで扱えることが分かるだろう。
(注)-4は、平方根の計算Subを実行したときに表示されるInputBox(「平方根を求める数を入力してください」というボックス)に入力しよう。
なぜFunctionの中でMsgBoxを出さないのか
「エラーが起きたら Err.Raise を使わず、Function の中で直接 MsgBox を表示すればよいのでは」と思うかもしれない。しかし、これには問題がある。
Function は、いろいろな場所から呼び出されて使い回されるものである。Function の中に MsgBox を書いてしまうと、呼び出し元の事情を無視して、常に同じダイアログが表示されてしまう。たとえば、ループの中で100回呼び出されたら、100回ダイアログが出て止まってしまう。
Err.Raise でエラーを「伝える」だけにしておけば、それをどう扱うか(メッセージを出す、ログに記録する、既定値を使うなど)を呼び出し元が決められる。
これは第12回で学んだ「Functionは値を返すことに専念する」という考え方の延長線上にある。
後始末を忘れないマクロ
エラー処理でもう一つ大事なのが「後始末」である。処理の途中で Application.ScreenUpdating = False(画面の更新を止める)のような設定をしていた場合、エラーで処理が中断すると、その設定を元に戻す行が実行されないままになってしまう。
わかりやすい例で見てみよう。まず、後始末をしていない、問題のあるコードである。
Sub 後始末なしの例()
Application.ScreenUpdating = False
Dim i As Long
For i = 1 To 10
Cells(i, 1).Value = 100 / (i - 5) ' i=5のとき0で割ってエラー
Next i
Application.ScreenUpdating = True
End Subi = 5 のときにエラーが起き、マクロは途中で止まる。すると、最後の Application.ScreenUpdating = True が実行されないまま終わってしまう。
画面上は更新が止まったままになり、Excelの操作がおかしく見えてしまう。
後始末なしの例を実行すると、i = 5のときに100 / (i - 5)で0除算になるので、「実行時エラー ’11’: 0 で除算しました。」というエラーダイアログが出る。
(注)このエラーで処理が止まると、Application.ScreenUpdating = Falseが実行されたままになり、元に戻されない。そのままだと、この後の操作でExcelの画面更新が止まったように見えることがある。
確認のため、VBEでCtrl + Gを押してイミディエイトウィンドウを開き、
Application.ScreenUpdating = Trueと入力してEnterを押し、画面更新を手動で戻しておこう(この流れが、次の後始末ありの例で解決する問題そのものである)。
さて、これを、エラー処理を使って直してみよう。
Sub 後始末ありの例()
On Error GoTo エラー処理
Application.ScreenUpdating = False
Dim i As Long
For i = 1 To 10
Cells(i, 1).Value = 100 / (i - 5)
Next i
後始末:
Application.ScreenUpdating = True
Exit Sub
エラー処理:
MsgBox "処理中にエラーが発生しました。" & vbCrLf & Err.Description, vbExclamation
Resume 後始末
End Subここでのポイントは、後始末: というラベルを、正常終了時とエラー発生時の両方から通る場所に置いていることである。
- 正常に終われば、そのまま
後始末:に流れ込み、ScreenUpdatingを元に戻す - エラーが起きれば
エラー処理:に飛び、メッセージを表示した後、Resume 後始末で後始末:へジャンプする
前回学んだ Resume ラベル名 が、ここで活きてくる。「後始末は1か所にまとめ、正常時もエラー時もそこを通す」という形は、覚えておくと役に立つ。
まとめ
今回は、エラーの応用的な扱い方を学んだ。
Err.Raiseで、自分の決めたルールに沿ったエラーを発生させられる- 番号には
vbObjectError + 任意の数値を使う - Function の中で
MsgBoxを出さず、Err.Raiseで呼び出し元に判断を委ねる - 後始末(設定を元に戻す処理)は1か所にまとめ、正常時もエラー時も
Resume ラベル名で通す
次回(第19回)は、エラー処理編の最終回として、「そもそもエラーを起こさない」ための事前チェックの考え方を取り上げる。IsNumeric や、第17回で扱ったシート存在確認を事前チェックの形に書き直しながら、シリーズ全体を振り返っていこう。


コメント