Excel VBA | 関数内部でエラーを細かく扱う方法

VBA
スポンサーリンク

関数の中でエラーを丁寧に扱うと、「原因の特定」「呼び出し側への伝え方」「後処理(片付け)」がうまく回ります。初心者向けに、よく使うパターンとサンプルをまとめます。


基本パターン(On Error GoTo + Err オブジェクト)

構造の定型

Function SafeDivide(ByVal a As Double, ByVal b As Double) As Variant
    On Error GoTo HandleError
    
    ' 入力の事前チェック(論理エラーの予防)
    If b = 0 Then
        Err.Raise Number:=vbObjectError + 1000, _
                  Source:="SafeDivide", _
                  Description:="ゼロ除算はできません"
    End If
    
    SafeDivide = a / b
    Exit Function

HandleError:
    ' ここに来たら例外が起きている
    SafeDivide = CVErr(xlErrDiv0)  ' 返り値でエラーを示す(セル関数互換)
    ' ログに残したい場合は Debug.Print など
    Debug.Print "Error(" & Err.Number & "): " & Err.Description
    ' Err.Clear は次の処理に備えてクリアする時のみ
End Function
VB
  • On Error GoTo: 例外が起きたら指定ラベルへジャンプさせる基本形。
  • Err.Raise: 意図的に「使い方が不正」などの論理エラーを発生させ、ハンドラへ流す。
  • Exit Function: 正常系の最後で必ず抜ける。
  • HandleError ラベル: 返り値の決め方、ログ、後始末を一箇所に集約。

返し方の設計(どの形で伝える?)

  • Variantでセルエラーを返す:
    • 例: CVErr(xlErrValue), CVErr(xlErrDiv0)
    • セルから呼ぶ関数や、呼び出し側が「エラー値」を扱いたい時に向く。
  • Boolean+ByRefメッセージを返す:
Function TryParseInt(ByVal s As String, ByRef value As Long, ByRef message As String) As Boolean
    On Error GoTo HandleError
    
    value = CLng(s)
    TryParseInt = True
    Exit Function

HandleError:
    TryParseInt = False
    message = "整数に変換できません: " & s
End Function
VB
  • 成否をBooleanで、詳細はメッセージで渡す。手続き的な呼び出しに向く。
  • 成功時は値、失敗時はNull/Nothing/Empty:
    • Variantなら Empty/Null を使える。配列やオブジェクトなら Nothing

On Error Resume Next の安全な使い方

部分的に失敗を許容して進めたい場合だけ、範囲を限定して使います。

Function SheetExists(ByVal name As String) As Boolean
    Dim ws As Worksheet
    
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(name)
    If Err.Number <> 0 Then
        Err.Clear
        SheetExists = False
    Else
        SheetExists = True
    End If
    On Error GoTo 0  ' 既定のエラーハンドリングに戻す
End Function
VB
  • ポイント:
    • Resume Next の直後の操作だけを対象にする。
    • チェック後は Err.ClearOn Error GoTo 0 で元に戻す。
    • だらだら長い範囲での使用はバグの温床。

クリーンアップ(擬似 finally)

ファイル/オブジェクト/配列などの後始末を、正常・異常どちらでも確実に実行します。

Function ReadFirstLine(ByVal path As String) As Variant
    Dim ff As Integer
    Dim line As String
    
    On Error GoTo HandleError
    
    ff = FreeFile
    Open path For Input As #ff
    Line Input #ff, line
    ReadFirstLine = line
    GoTo CleanExit

HandleError:
    ReadFirstLine = CVErr(xlErrNA)
    Debug.Print "Read error: "; Err.Number; Err.Description

CleanExit:
    If ff <> 0 Then
        Close #ff
    End If
End Function
VB
  • ラベルを分ける: HandleError と CleanExit を分離すると読みやすい。
  • 状態フラグ: 開いているかどうかを判定して後始末。

入力バリデーションと早期リターン

「エラーになりうる入力」を先回りで弾くと、ランタイム例外を減らせます。

Function GetPrimesInRange(ByVal startNum As Long, ByVal endNum As Long) As Variant
    On Error GoTo HandleError
    
    If startNum < 2 Then startNum = 2
    If endNum < startNum Then
        Err.Raise vbObjectError + 1001, "GetPrimesInRange", "範囲が不正です"
    End If
    
    ' ... 素数計算(省略) ...
    Exit Function

HandleError:
    GetPrimesInRange = CVErr(xlErrValue)
End Function
VB
  • 先に範囲チェック: 条件が不正なら計算を始める前に落とす。
  • メッセージを具体的に: 呼び出し側が直しやすくなる。

カスタムエラー番号とログ

  • 番号設計: vbObjectError + 1000 のように、自分の領域を決めて重複を避ける。
  • Source に関数名: どこで起きたか追跡しやすい。
  • ログの出力: Debug.Print, Trace, あるいは専用のロギング関数に集約。
Sub LogErr(ByVal proc As String, ByVal e As ErrObject)
    Debug.Print "[" & proc & "] Err " & e.Number & ": " & e.Description
End Sub
VB

よくある落とし穴と回避

  • Resume Next の使い過ぎ: 例外を見えなくする。短いスコープで限定使用。
  • Err.Clear の乱用: 原因追跡が困難に。必要な場面だけ。
  • 返り値が曖昧: 何が「成功」かを返り値で明確化。仕様を決める。
  • 後始末忘れ: ファイル/オブジェクトは必ずクリーンアップラベルへ。

まとめの指針

  • 事前に弾く(入力チェック)失敗を検知(Err/ハンドラ)呼び出し側へ分かる形で返す(値・コード・メッセージ)必ず片付ける(CleanExit)
    この流れをひとつのテンプレにしておくと、どの関数でも安定して扱えます。
VBA
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました