2014/12/07

【VBA】例外処理(Try-Catch-Finally)を使う方法

結論から言うと、VBAにはTry-Catch-Finallyが存在しない。
そこでOn Errorステートメントとラベルを使う。

一番簡単な例外処理(エラーハンドリング)は以下のとおり。
Sub ErrHandlingTest()
    On Error GoTo ErrorHandler
    
    Dim value As Long
    value = 2 / 0     '-- ゼロ除算のためエラー発生

    Exit Sub
ErrorHandler:
    '-- 例外処理
    MsgBox Err.Number & ":" & Err.Description, vbCritical & vbOKOnly, "エラー"
End Sub
このようにすると、エラーが発生した時点で「On Error GoTo ラベル名」に指定したラベル名(上記の例ではErrorHandler)に飛ぶ。




エラーごとにキャッチしたい


ひとつの例外しか発生しないなら、前述のとおりで良いんだけど、Catchでエラー別に取得したいときがある。

C#やJavaなどのプログラミング言語では、必ず「Catchだけですべての例外を取得しない。エラーごとにCatchする」と習うだろう。

VBAでは若干面倒だが、以下の2つの方法で対応することができる。
 

自分で例外をスローする


Sub ErrHandlingTest()
    On Error GoTo ErrorHandler
    
    Dim value As Long
    Dim div As Long
    div = 0

    If div = 0 Then
        GoTo DivisionByZero
    Else
        value = 2 / div
    End If

    Exit Sub

DivisionByZero:
    '-- ゼロ除算の場合にのみここにくる
    MsgBox "ゼロ除算ですよ"
    Exit Sub

ErrorHandler:
    '-- 例外処理
    MsgBox Err.Number & ":" & Err.Description, vbCritical & vbOKOnly, "エラー"
End Sub

こんな感じに、あらかじめわかっている処理なら自分で例外を流ればよい。
(業務エラーとかの場合はコレを使う)

ポイントは、通常処理の最後とDivisionByZeroラベルの最後に「Exit Sub」があること。
これがないと、続けてDivisionByZeroやErrorHandlerまで実行されてしまうので注意。



キャッチしたあとで振り分ける


Sub ErrHandlingTest()
    On Error GoTo ErrorHandler
    
    Dim value As Long
    Dim div As Long
    div = 0

    value = 2 / div

    Exit Sub

DivisionByZero:
    '-- ゼロ除算の場合にのみここにくる
    MsgBox "ゼロ除算ですよ"
    Exit Sub

ErrorHandler:
    '-- 例外処理(必ずここに来る)

    Select Case Err.Number
    Case 11
        GoTo DivisionByZero
    Case Else
        MsgBox Err.Number & ":" & Err.Description, vbCritical & vbOKOnly, "エラー"
    End Select
End Sub

エラーが発生するとErrオブジェクトにエラー情報が格納される。
その中の「Err.Numberプロパティ」に従い、処理を振り分ける方法。

ただ「Err.Number = 11」と書いてもよくわからないので「Const ERROR_DIVISION_BY_ZERO As Long = 11」のように定数化しておくことをオススメする。

Err.Numberについては、Office TANAKA - Excel VBAの実行時エラーを参照。



Finallyで終了処理がしたい



Finallyを使って終了処理をしたいことがある。
たとえば、CreateObjectでExcelを操作するとき、CloseやQuitをしないとプロセスに「EXCEL.EXE」が残ってしまう。

一番簡単な方法としては、以下のように冗長的に書くこと。
Sub ErrHandlingTest()
    On Error GoTo ErrHandler
    Dim obj As Object
    Set obj = CreateObject("Excel.Application")
    obj.Application.Workbooks.Open Filename:="C\:test.xlsx"

    '-- いろんな処理

    '-- 正常終了の場合は
    obj.Close
    obj.Quit

    Exit Sub

ErrHandler:
    '-- エラーが発生した場合
    obj.Close
    obj.Quit

End Sub
個人的に大嫌いな書き方。
冗長的で、いかにも頭が悪そうに見える。

そんなときは、惜しげも無くGoToを使ってしまおう。

といっても、GoToを使用するのは極力さけたいので、エラー処理と終了処理以外では、使用しないほうが良いだろう。

Sub ErrHandlingTest()
    On Error GoTo ErrHandler
    Dim obj As Object
    Set obj = CreateObject("Excel.Application")
    obj.Application.Workbooks.Open Filename:="C\:test.xlsx"

    '-- いろんな処理

    GoTo Finally

ErrHandler:
    '-- エラーが発生した場合
    Resume Finally

Finally:
    If Not obj Is Nothing Then
        obj.Close
        obj.Quit
    End If
End Sub
ErrHandlerのなかで「Resume Finallly」と書いているのは、例外をスローするため。
ただし、Finallyで処理しなければエラーがもみ消されてしまうので注意。
(できれば、Finallyに投げる前に処理を済ませて置いたほうが良い)

さきほど「Exit Sub」だった場所を、「GoTo Finally」にかえることで必ずFinally(終了処理)を実行することができる。

ちなみに「ErrHandler」の「Resume Finally」がなくてもFinallyは実行される。
違いはエラー情報をFinallyに渡すかどうか。



おまけ


エラーをキャッチせずに無理やり処理を続行したい場合は、「On Error GoTo ラベル」を「On Error Resume Next」を使うとエラーを無視して処理を続けることができる。



以上

written by @bc_rikko

0 件のコメント :

コメントを投稿