VBA業務マクロのエラー処理を三層と三分類で設計する

VBA業務マクロのエラー処理を三層と三分類で設計する

On Error を場当たり的な後始末ではなく、業務継続とUI判断の設計部品として扱う

Copyright © 2026 LWP 山中 一弘

本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。

記事要約

VBAの業務マクロでは、エラー処理が後付けになりやすい。On Error GoTo で最後にまとめて捕まえるだけでは、処理を止めるべきエラー、ユーザーに選ばせれば続行できるエラー、データ検証として通常の戻り値で返すべきエラーが混ざってしまう。

難しいのは、VBAに構造化例外の仕組みがなく、Err オブジェクトと On Error の制御範囲を手続き的に扱う必要がある点である。さらに、業務マクロでは下位関数の途中でUI判断が必要になる場面があり、そこで安易にメッセージボックスを出すと、業務処理、画面操作、再実行制御が一体化してしまう。

この記事では、エラーをA/B/Cの三分類で整理し、UI層、業務層、データ層の三層へ責務を分ける。Err.Raise で止めるべきものと、戻り値構造で返すべきものを分離し、業務継続とユーザー判断を扱えるエラー処理設計へ落とし込む。さらに、8通りの責務配置、状態機械による再開制御、ログ、テスト、命名規約、チーム規約まで含めて、実務で維持できる設計体系としてまとめる。

本記事の対象とゴール

想定読者

  • Excel VBAで業務マクロを作っていて、エラー処理が毎回ばらつく人

  • On Error GoTo ErrHandler は書けるが、どこで捕まえるべきかに迷う人

  • 下位関数からメッセージボックスを出す設計に違和感がある人

  • 業務継続、処理中断、ユーザー判断を設計として分けたい人

本記事で得られること

  1. VBAのエラー処理を、例外、業務エラー、継続判断に分けて考えられる。

  2. UI層、業務層、データ層のどこが何を担当するかを決められる。

  3. Err.Raise と戻り値構造を使い分けるための実装方針を持てる。

  4. エラー処理をコード末尾の後始末ではなく、業務マクロの設計項目として扱える。

  5. ログ、テスト、命名、レビュー観点まで含めたエラー処理規約を作れる。

本記事で扱わないこと

  • VBAの全構文リファレンス

  • すべてのExcel実行時エラー番号の一覧

  • 特定ブックや特定プロジェクトの個別実装

  • クラスモジュールを使った大規模フレームワークの完全実装

先に結論

VBAのエラー処理は、すべてを On Error で捕まえる設計では破綻しやすい。

まず、処理不能なものは例外として止める。業務上の不成立は、戻り値構造で返す。ユーザーが選べば続行できるものは、UI層へ判断を返す。

そのうえで、下位関数は画面を出さない。データ層は検出する。業務層は分類する。UI層は通知し、必要なときだけユーザー判断を受け取る。

この分離ができると、VBAでも、例外、エラー、業務継続を混ぜずに扱える。

1. `On Error` だけでは設計にならない

1.1 `On Error` は捕捉手段であって分類ではない

VBAでは、実行時エラーを扱う代表的な構文として On Error GoTo を使う。

ただし、On Error は「エラーが起きたらどこへ飛ぶか」を指定する構文であり、「そのエラーが何を意味するか」は決めてくれない。

ファイルが開けないのか、必須列がないのか、入力値が空なのか、ユーザーがキャンセルしたのか。これらを全部同じ ErrHandler に送ると、最後はメッセージを出して終了するだけの設計になる。

業務マクロでは、それでは足りない。止めるべきエラーと、戻すべきエラーと、ユーザー判断を挟むべきエラーを分ける必要がある。

1.2 `Err` は情報置き場であって責務分離ではない

Err.Number や Err.Description は便利だが、Err に入っている情報だけで設計が整理されるわけではない。

Err は直近のエラー情報を持つ仕組みであり、業務上の意味を持った結果オブジェクトではない。したがって、業務処理の結果として「処理成功」「業務上の不成立」「ユーザー判断待ち」を返したい場合は、Err とは別に戻り値構造を用意したほうが読みやすい。

Err は、予期しない実行時エラーや、処理を中断すべき例外を上位へ伝えるために使う。業務上の判定結果をすべて Err に詰め込むと、正常な分岐と異常系の分岐が混ざる。

1.3 下位関数でUIを出すと再利用できなくなる

業務マクロでは、処理中に「このまま続けますか」「この行はスキップしますか」「別ファイルを選び直しますか」といった判断が必要になる。

ここで下位関数が直接 MsgBox を出すと、その関数はUIと結びつく。単体テストもしにくくなり、別の画面や別の呼び出し元から再利用することも難しくなる。

下位関数は、ユーザーに聞くのではなく、「判断が必要である」という結果を返す。UI層がその結果を受け取り、ユーザーへ通知し、選択結果を業務層へ戻す。このほうが、処理の責務が明確になる。

2. エラーをA/B/Cの三分類で見る

2.1 A: 致命的なシステムエラー

Aは、処理を継続できない構造的な失敗である。

たとえば、ファイルを開けない、必要なシートが存在しない、アクセス権がない、想定外の実行時エラーが発生した、といった状態である。

この種のエラーは、下位で握りつぶしてはいけない。業務層で補足情報を付け、最終的にはUI層または最上位の実行入口で捕捉して、処理を止める。

2.2 B: 致命的な業務エラー

Bは、プログラム自体は動いているが、業務処理として成立しない状態である。

たとえば、必須項目がない、金額と明細が一致しない、登録対象として扱えないデータである、といった状態である。

Bはシステム障害ではない。しかし、そのまま登録や転記を続けると、誤った成果物を作る。したがって、業務層で「処理不能」と分類し、上位へ返すか、例外として止める。

2.3 C: 継続可能な業務エラー

Cは、業務上は注意が必要だが、判断によって処理を続けられる状態である。

たとえば、重複候補がある、任意項目が空である、警告を出したうえでスキップできる、ユーザーが確認すれば続行できる、といった状態である。

Cをすべて例外として止めると、業務マクロは使いにくくなる。一方で、何も聞かずに続行すると危険である。Cは戻り値構造として返し、UI層で判断を受け取り、業務層が次の処理を決めるのがよい。

3. 三層で責務を分ける

3.1 データ層は検出する

データ層は、ファイル、シート、セル、配列、辞書、テーブルなど、処理対象に近い場所で異常を検出する。

ここで重要なのは、データ層が「表示」まで担当しないことである。データ層は、存在しない、型が違う、値が不正である、といった事実を検出する。どう通知するか、続行するか、ユーザーに聞くかは、上位の責務である。

3.2 業務層は分類する

業務層は、データ層から上がってきた状態をA/B/Cへ分類する。

同じ「値が空」でも、必須項目ならB、任意項目ならC、そもそも列が存在しないならAになりうる。この判断は、データ層だけでは決められない。業務ルールを知っている層が分類する必要がある。

業務層は、分類した結果を戻り値構造にして返すか、処理不能として Err.Raise で上位へ投げる。

3.3 UI層は通知し、判断を受け取る

UI層は、ユーザーへ通知し、必要な判断を受け取る。

ただし、UI層が業務ルールを全部持つわけではない。UI層は「何を表示するか」「どの選択肢を出すか」「選択結果をどう返すか」を担当する。選択結果を受けて、スキップする、再試行する、中断する、といった処理方針は業務層が決める。

この分離により、業務ロジックは画面に依存しにくくなる。あとからメッセージボックスをフォームに変える、ログ出力を追加する、テスト用の疑似UIに置き換える、といった変更もしやすい。

4. `Err.Raise` と戻り値構造を使い分ける

4.1 止めるものは `Err.Raise` で上げる

Aのように処理継続できないものは、例外として上げる。

VBAでは、構造化例外のような仕組みはないが、Err.Raise により上位の On Error GoTo へ伝播させることはできる。下位で捕まえた場合も、必要な文脈を付けて再送出する。

次のコードは概念例である。実際のエラー番号体系はプロジェクトごとに決める。

Private Sub RaiseFatal(ByVal sourceName As String, ByVal message As String)
    Err.Raise vbObjectError + 1000, sourceName, message
End Sub

大事なのは、下位でエラーを消さないことである。処理不能な状態を握りつぶすと、上位は成功したものとして次へ進んでしまう。

4.2 業務上の分岐は戻り値で返す

BやCの一部は、例外ではなく処理結果として返したほうがよい。

たとえば、入力行の検証で「必須項目がない」と分かった場合、その行をエラー一覧に積み、全件検査を続けたいことがある。この場合、毎回 Err.Raise で止めると、検査処理として扱いにくい。

戻り値構造を使えば、成功、業務NG、ユーザー判断待ち、中断を同じ型で返せる。

Public Enum ProcStatus
    ProcStatusOk = 0
    ProcStatusNg = 1
    ProcStatusNeedDecision = 2
    ProcStatusAbort = 3
End Enum
Public Type ProcResult
    Status As ProcStatus
    Value As Variant
    ErrorNumber As Long
    Message As String
    DecisionKey As String
End Type

このような戻り値を用意すると、呼び出し元は Status を見て、成功処理、警告表示、ユーザー判断、中断を分けられる。

4.3 `On Error` は入口と境界で使う

On Error は、あらゆる行に散らすより、入口と境界で使うほうがよい。

具体的には、公開Sub、ファイルI/O、外部アプリ操作、配列や辞書の境界処理、変換処理など、失敗しうる境界に置く。

小さな純粋関数や単純な判定関数まで全部 On Error で包むと、どこで何を守っているのかが分からなくなる。エラー処理は多ければよいのではない。失敗の意味が変わる境界で入れる。

5. 8パターンで責務配置を検討する

5.1 8パターンは全採用候補ではなく、設計検討の地図である

A/B/Cの三分類を、UI層と業務層のどちらで主に処理するかを考えると、理屈上は8通りの責務配置が生じる。

  1. A/B/CをすべてUI層で処理する。

  2. A/BをUI層で処理し、Cを業務層で処理する。

  3. A/CをUI層で処理し、Bを業務層で処理する。

  4. B/CをUI層で処理し、Aを業務層で処理する。

  5. AだけをUI層で処理し、B/Cを業務層で処理する。

  6. BだけをUI層で処理し、A/Cを業務層で処理する。

  7. CだけをUI層で処理し、A/Bを業務層で処理する。

  8. A/B/Cをすべて業務層で処理する。

この8パターンは、「どれも同じように選べる」という意味ではない。実務では、責務の置き場所を考えるための地図として使う。

危険なのは、A/B/Cの意味を決めないまま、近くにある層で処理してしまうことである。下位関数でメッセージを出す、上位Subで業務判定を全部持つ、業務層でユーザーの選択肢文言まで決める、といった設計は、この地図で見ると責務が混ざっている。

5.2 実務の基本形は「A/Bは止める、Cは選ばせる」である

実務では、A/B/Cを次のように扱うと安定しやすい。

  • Aは処理不能として上位へ上げ、最上位入口で止める。

  • Bは業務処理として不成立であり、業務層で分類したうえで上位へ返すか、必要なら止める。

  • Cはユーザー判断を挟めば続行できるため、戻り値構造でUI層へ判断要求を返す。

ここでいう「UI層で処理する」とは、UI層が業務ルールを持つという意味ではない。UI層は通知し、選択肢を提示し、選択結果を返す。業務ルール上どの選択肢を許すか、選択後に何をするかは、業務層で決める。

したがって、推奨形は「A/Bは上位で止めるまたは通知する、Cは業務層が判断要求として返し、UI層が選択を受け取る」である。

5.3 すべて業務層で処理する方式は強く見えるが閉じすぎる

A/B/Cをすべて業務層で処理すると、一見すると業務層が強くなり、UI層が薄くなる。

しかし、業務層の中で通知、判断、再試行、スキップ、中断まで抱えると、結局は業務層がUIに依存する。メッセージボックス、フォーム、ログ、実行入口の都合が業務層へ漏れ込む。

業務層は分類と制御方針を持つ。UI層は通知と入力を持つ。この境界を崩すと、後からUIを差し替えるときに、業務ロジック全体を直すことになる。

5.4 すべてUI層で処理する方式は薄く見えるが業務ルールが漏れる

A/B/CをすべてUI層で処理すると、下位関数は単純になる。

ただし、UI層が「このエラーは止める」「このエラーは警告だけ」「このエラーはスキップ可能」と決め始めると、業務ルールがUI層へ流れる。

画面側が業務ルールの中心になると、バッチ実行、テスト、別画面からの再利用が難しくなる。UI層に置いてよいのは、表示と選択受付である。業務上の意味づけは、業務層へ戻す。

6. UI判断を処理途中へ混ぜない

6.1 ユーザー判断は結果として返す

下位関数でユーザー判断が必要になったときは、直接UIを出すのではなく、判断要求を戻り値として返す。

たとえば、重複候補が見つかった場合、下位関数は「重複候補がある」「選択が必要である」「候補IDはこれである」と返す。UI層はそれを表示し、選択結果を業務層へ渡す。

この設計にすると、業務処理は「判断が必要な状態」を扱えるようになる。UIの種類が変わっても、業務ロジックの中心を作り直さずに済む。

6.2 VBAには本当のyieldがない

処理途中でUIに戻し、あとから同じ場所へ再開するような設計は、VBAでは簡単ではない。

そのため、複雑な再開を無理に作るより、処理状態を外へ出し、「次に何をするか」を明示した状態遷移として扱うほうが安定する。

単純なマクロであれば、判断が必要になった時点でいったん結果を返し、UI層で選択を受け取り、次の処理を呼び直す。複雑なマクロであれば、処理状態を表す値を持ち、状態ごとに次の関数へ進める。

6.3 コールバック注入は最終手段にする

UI判断関数を引数として渡す、関数名を渡して動的に呼ぶ、クラスに判断メソッドを持たせる、といった設計も可能である。

ただし、VBAで動的呼び出しを多用すると、追跡性が落ちる。呼び出し先が文字列になり、検索しにくくなり、実行時まで間違いに気づきにくい。

まずは、戻り値で判断要求を返す設計を標準にする。コールバック注入は、処理の途中でどうしてもUI判断を差し込みたい場合だけ使う。

6.4 複数判断は選択肢を標準化して返す

UI判断は、単純なYes/Noだけでは済まない。

実務では、続行、スキップ、再試行、ファイル選び直し、中断、詳細確認など、複数の選択肢が出る。これを毎回文字列で処理すると、分岐が散らばる。

判断要求を返すときは、選択肢をEnumや定数で標準化する。

Public Enum UserDecision
    UserDecisionNone = 0
    UserDecisionContinue = 1
    UserDecisionSkip = 2
    UserDecisionRetry = 3
    UserDecisionAbort = 4
End Enum

UI層は、ユーザーの操作をこの値へ変換する。業務層は、この値を受け取って次の状態を決める。

6.5 中断とスキップを混同しない

業務継続設計では、中断とスキップを分ける。

中断は、処理全体を止めることである。スキップは、対象の一部を処理せず、次へ進むことである。

たとえば、1行のデータが不正な場合、行単位の処理ならスキップできることがある。しかし、ヘッダー構造が壊れている場合は、全体を中断すべきである。

この区別を戻り値構造に入れておくと、UI層で「キャンセル」と表示された結果が、業務上は中断なのか、対象スキップなのかを明確にできる。

7. 実装テンプレートの考え方

7.1 最上位Subは捕捉と通知を担当する

最上位の実行入口では、予期しない例外を捕捉し、ユーザーへ通知し、ログが必要なら記録する。

Public Sub RunImport()
    On Error GoTo FatalError
    Dim result As ProcResult
    result = ImportCore()
    Select Case result.Status
        Case ProcStatusOk
            MsgBox "処理が完了しました。", vbInformation
        Case ProcStatusNg
            MsgBox result.Message, vbExclamation
        Case ProcStatusNeedDecision
            HandleDecision result
        Case ProcStatusAbort
            MsgBox "処理を中断しました。", vbInformation
    End Select
    Exit Sub
FatalError:
    MsgBox Err.Description, vbCritical
End Sub

ここで重要なのは、最上位Subが業務の細部を判定しないことである。最上位Subは、業務層から返ってきた結果を見て、通知と判断受付を行う。

7.2 業務関数は分類して返す

業務関数は、データ層から返った状態を見て、処理を続けるか、業務NGとして返すか、ユーザー判断が必要かを決める。

Private Function ImportCore() As ProcResult
    On Error GoTo FatalError
    Dim result As ProcResult
    If Not SourceExists() Then
        result.Status = ProcStatusNg
        result.Message = "入力元が見つかりません。"
        ImportCore = result
        Exit Function
    End If
    If HasDuplicateCandidate() Then
        result.Status = ProcStatusNeedDecision
        result.Message = "重複候補があります。"
        result.DecisionKey = "DUPLICATE_ROW"
        ImportCore = result
        Exit Function
    End If
    result.Status = ProcStatusOk
    ImportCore = result
    Exit Function
FatalError:
    Err.Raise Err.Number, "ImportCore", Err.Description
End Function

この形にすると、業務関数は画面を出さない。画面を出すのはUI層である。業務関数は分類と結果返却に集中する。

7.3 データ関数は事実を返す

データ関数は、業務判断を持ちすぎない。

入力元が存在するか、列が存在するか、値を読めるか、変換できるか。こうした事実を返す。データ関数が「この業務では中断すべき」と決め始めると、業務ルールが下位へ漏れる。

下位で起きた実行時エラーは、必要に応じて Err.Raise で上げる。通常の検査結果は、Boolean、Enum、または結果構造で返す。

7.4 状態機械型のテンプレートを使う

UI判断を挟んで処理を再開する場合、処理途中のローカル変数に依存したまま再開しようとすると破綻しやすい。

再開が必要な処理では、状態を明示する。たとえば、入力確認、重複確認、ユーザー判断待ち、登録、完了という段階を状態として持つ。

Public Enum ImportState
    ImportStateStart = 0
    ImportStateCheckInput = 1
    ImportStateNeedDecision = 2
    ImportStateRegister = 3
    ImportStateDone = 4
End Enum

状態を外へ出すと、UI層は「いま何待ちか」を理解できる。業務層は、前回の状態とユーザー判断を受け取り、次の状態へ進められる。

これは本物の yield ではない。しかし、業務マクロでは、処理を段階に分けて再呼び出しできるだけで、十分に扱いやすくなる。

7.5 例外のラップは文脈を足すために使う

下位で発生した例外を上位へ送るとき、単に Err.Number と Err.Description をそのまま投げ直すだけでは、どの業務処理で失敗したかが分かりにくい。

再スローやラップを使う目的は、エラーを隠すことではない。文脈を足すことである。

業務層で再スローするなら、処理名、対象、分類、補足メッセージを付ける。逆に、元のエラー番号や説明を消してしまうと、原因調査が難しくなる。

8. レビューで見るべきポイント

8.1 エラー分類がコードに出ているか

レビューでは、エラー処理の有無だけを見ても足りない。

次を確認する。

  • A/B/Cのどれなのかが分かるか

  • 例外として止めるものと、戻り値で返すものが分かれているか

  • ユーザー判断が必要な状態を、UI層へ返しているか

  • 下位関数が直接メッセージを出していないか

  • Err.Raise で上げ直すとき、どの関数で起きたかが分かるか

8.2 `On Error Resume Next` が狭く使われているか

On Error Resume Next は、使ってはいけない構文ではない。

ただし、使う範囲を狭くする必要がある。存在確認、オブジェクト取得、外部アプリ接続の一部など、失敗を直後に検査する場合に限る。

On Error Resume Next を広い範囲に置いたままにすると、本来止めるべきエラーまで通過する。使った後は、すぐに Err.Number を確認し、必要なら On Error GoTo 0 または通常のエラー処理へ戻す。

8.3 ログは分類と一緒に残す

ログを残すなら、単に Err.Description を保存するだけでは弱い。

最低限、次を残すとよい。

  • エラー分類

  • 発生した処理名

  • 対象ファイル、シート、行番号などの文脈

  • ユーザー判断があった場合の選択結果

  • 再試行、スキップ、中断の結果

ログは後から原因を追うためのものなので、画面に出したメッセージと同じ文章だけでは不足する。

8.4 命名で責務が見えるか

エラー処理設計は、関数名にも出る。

たとえば、ImportData、ValidateInput、ReadSource、AskContinue は、それぞれ責務が違う。名前が曖昧だと、どの関数が検出し、どの関数が分類し、どの関数がユーザー判断を扱うのかが見えない。

検証だけをする関数には、Validate や Check を使う。処理を実行する関数には、Do、Run、Import、Register などを使う。ユーザーへ聞く関数には、Ask や Prompt を使う。

命名規約は飾りではない。関数ツリーを見たとき、エラー発生責任と通知責任を追えるようにするための道具である。

8.5 `On Error` の多重定義で構造が崩れていないか

VBAでは、プロシージャ内の On Error の状態が手続き的に切り替わる。

そのため、途中で On Error Resume Next を使い、そのまま通常処理へ戻ると、後続のエラーが見えなくなる。逆に、On Error GoTo ErrHandler を広く置きすぎると、どの行の失敗なのかを追いにくくなる。

レビューでは、On Error の開始位置、解除位置、再設定位置を見る。失敗を許容する短い範囲と、処理全体を守る範囲を混同しない。

9. ログとテストを設計に含める

9.1 ログはUI通知とは別に設計する

ユーザーへ出すメッセージと、開発者や運用者が見るログは目的が違う。

ユーザー向けメッセージは、何が起きたか、次にどうすればよいかを短く伝える。ログは、あとから原因を追えるように、分類、処理名、対象、入力値、行番号、再試行結果を残す。

UI層で例外を捕捉したとき、どこでログを出すかを決めておく。最上位Subでまとめて記録するのか、業務層で文脈を付けて記録するのかを混ぜない。

9.2 戻り値分岐はテストしやすい

戻り値構造でエラーを返す設計は、テストしやすい。

Status が Ok のとき、Ng のとき、NeedDecision のとき、Abort のときで、期待する次の処理を確認できるからである。

一方、下位関数が直接メッセージボックスを出す設計では、テストが難しい。UIを出さずに業務関数を呼び、戻り値だけを検査できる構造にしておくと、エラーケースを網羅しやすくなる。

9.3 UI判断はモックできる形にする

UI判断を含む処理をテストするには、実際の画面を出さずに選択結果を返せる必要がある。

単純な設計なら、UI層へ判断要求を返し、テストではその結果を直接与える。より複雑な設計なら、判断関数や判断オブジェクトを差し替えられる形にする。

重要なのは、業務層が MsgBox の戻り値に直接依存しないことである。業務層が依存するのは、標準化された判断結果である。

9.4 パターン別のテストリストを作る

エラー処理のテストは、正常系だけでは足りない。

最低限、次を確認する。

  • Aの例外が最上位で捕捉される。

  • Bの業務エラーが処理不能として返る。

  • Cの判断要求がUI層へ返る。

  • ユーザーが続行を選んだとき、次の状態へ進む。

  • ユーザーがスキップを選んだとき、対象だけを飛ばす。

  • ユーザーが中断を選んだとき、全体を止める。

  • ログに分類、処理名、対象が残る。

このテストリストがあると、エラー処理が「書いてある」だけでなく、「期待した分岐として動く」ことを確認できる。

10. 設計規約としてチームへ広げる

10.1 共通規約がないと関数ごとに流儀が変わる

業務マクロのエラー処理は、個人ごとの癖が出やすい。

ある関数は Err.Raise、別の関数は戻り値、別の関数はメッセージボックス、別の関数は何も返さない、という状態になると、呼び出し側が毎回読み直さなければならない。

チームや長期保守を考えるなら、次を共通規約にする。

  • A/B/Cの分類名

  • 戻り値構造

  • ユーザー判断のEnum

  • 例外番号の範囲

  • ログ項目

  • 関数命名

  • レビュー観点

10.2 既存マクロには段階的に入れる

既存マクロを一度に全部直す必要はない。

まず、最上位入口の捕捉と通知をそろえる。次に、戻り値構造を作る。次に、下位関数の直接UI表示を減らす。最後に、ログとテストを足す。

この順序であれば、既存マクロを壊しにくい。設計方針だけを先に決め、実装は変更しやすい場所から進める。

10.3 レビューではコード量より分岐の意味を見る

エラー処理を入れると、コード量は増える。

ただし、見るべきなのは行数ではない。どの分岐がAで、どの分岐がBで、どの分岐がCなのか。どこで止まり、どこで返し、どこで選ばせるのか。ログとテストで追えるのか。

この観点でレビューすると、エラー処理は単なる後始末ではなく、業務マクロの設計品質そのものになる。

11. 実務での採用順序

11.1 まず最上位入口をそろえる

既存マクロへ導入する場合、最初から全関数を書き換える必要はない。

まず、実行入口のSubをそろえる。最上位で予期しない例外を捕捉し、業務層からの戻り値を Select Case で処理する形にする。

ここがそろうと、処理の終了、警告、ユーザー判断、中断の表示が統一される。

11.2 次に戻り値構造を決める

次に、処理結果を表すEnumやTypeを決める。

名称はプロジェクトごとに異なってよい。ただし、意味は統一する。成功、業務NG、判断待ち、中断の区分が揺れないようにする。

戻り値構造が決まると、各関数は「失敗したら何を返すか」を統一できる。

11.3 最後に下位関数からUIを取り除く

最後に、下位関数から直接のUI表示を減らしていく。

すぐに全部なくす必要はない。まず、データ層や業務層の中にある MsgBox を見つけ、戻り値構造へ置き換えやすいものから移す。

この作業は、単なるリファクタリングではない。業務処理とUI判断の境界を明確にする設計作業である。

まとめ

VBAのエラー処理は、構文の知識だけでは安定しない。

On Error、Err、Err.Raise は道具である。どのエラーを止め、どのエラーを戻し、どのエラーをユーザー判断へ渡すかは、設計として決める必要がある。

実務では、A/B/Cの三分類と、UI層、業務層、データ層の三層分離を先に決めるとよい。データ層は検出する。業務層は分類する。UI層は通知し、判断を受け取る。

この形にすると、VBAでも、場当たり的なエラー処理から、業務継続を支えるエラー処理設計へ進める。

出典メモ

本記事は、VBAのエラー処理、UI判断、業務継続設計に関する内部DOCX素材4件を統合し、公開記事として再構成したものです。

記事化にあたり、個別プロジェクト名、個別モジュール名、個別定数名は一般化しました。追加資料に含まれていた、例外通知の8パターン、状態機械、複数判断、ログ、テスト、命名、チーム規約の論点を本文へ統合しました。