VBAマクロのエラー原因をコールスタックから調べる

VBAマクロのエラー原因をコールスタックから調べる

停止した行から呼び出し元へ遡り、値が壊れた地点を見つける実務デバッグ

Copyright © 2026 LWP 山中 一弘

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

記事要約

VBAでエラーが表示された行は、必ずしも原因が作られた行ではありません。オブジェクトが Nothing になった、空のファイルパスが渡された、配列の件数が想定と違った、といった異常は、何段階も前の呼び出し元で発生していることがあります。

このような問題では、実行をブレークモードで止め、VBEのコールスタックを Ctrl+L で表示します。現在のプロシージャから呼び出し元へ遡り、呼び出し側の実引数と受け取り側の仮引数を対応させると、値がどこから来たかを追跡できます。

静的な呼び出し構造を滝Libの mod_amalgam、mod_list、mod_tree で整理し、実行時のコールスタック、ローカル変数、配列、Dictionaryを重ねると、コードを上から読むだけでは見えない原因を絞り込めます。

本記事の対象とゴール

この記事は、引き継いだExcel VBAマクロ、仕様書のない業務マクロ、エラー地点と原因地点が離れている処理を調査する人を対象にしています。

ゴールは、次の四点です。

  • エラー行と原因行を分けて考える

  • Ctrl+Lで実行時の呼び出し経路を確認する

  • 実引数と仮引数を対応させ、値の由来を追う

  • 静的調査と動的デバッグを一つの手順として使う

1. エラー行は原因ではなく、異常が表面化した場所

次の行で「オブジェクト変数またはWithブロック変数が設定されていません」と表示されたとします。

targetSheet.Range("A1").Value = resultValue

ここで分かるのは、実行時点の targetSheet が有効なWorksheetではないことだけです。なぜ無効になったかまでは分かりません。

原因の候補には、次があります。

  • 呼び出し元が Nothing を渡した

  • シート名の検索に失敗した

  • 取得失敗後も処理を続行した

  • 別のWorkbookを対象にした

  • ByRefで渡した変数が途中で書き換えられた

したがって、停止行をその場で直す前に、値が作られた経路を遡る必要があります。

2. 静的な地図と実行時の経路は別のもの

調査では、二種類の呼び出し関係を区別します。

静的な呼び出し関係は、ソースコード上で呼び出される可能性がある経路です。滝Libでは次の三つを調査の入口にできます。

  • mod_amalgam: 複数モジュールのコードを一つにまとめる

  • mod_list: モジュールとプロシージャの一覧を出す

  • mod_tree: プロシージャの呼び出しツリーを出す

一方、VBEのコールスタックは、いま実際に開始され、まだ終了していないプロシージャの並びです。分岐やデータによって通らなかった経路は含まれません。

静的な地図で調査候補を絞り、実行時のスタックで今回通った経路を確定する。この二段構えが重要です。

3. ブレークポイントは値が変わる境界へ置く

ブレークポイントは、エラー行だけに置くものではありません。データの状態が変わる境界へ置きます。

代表的な停止位置は次のとおりです。

  • 外部ファイルを開く直前と直後

  • Rangeを配列へ読み込んだ直後

  • Dictionaryへキーを登録した直後

  • 別のSubやFunctionを呼び出す直前

  • 条件分岐へ入る直前

  • シートへ結果を書き戻す直前

  • 戻り値を確定する直前

停止したら、行を眺めるのではなく、ローカルウィンドウ、ウォッチ式、イミディエイトウィンドウなどで値を確認します。

4. Ctrl+Lで現在の呼び出し経路を見る

VBEでは、コードがブレークモードになっているとき、Ctrl+Lで「呼び出し履歴」ダイアログを表示できます。実行中で停止していない状態では利用できません。

たとえば、次の順で処理が呼ばれているとします。

RunReport
  → ImportSales
      → ReadCsv
          → WriteResult

WriteResultで停止した場合、コールスタックを使うと ReadCsv、ImportSales、RunReportへ遡れます。呼び出し元を選択すると、そのプロシージャのコードとローカルな文脈を確認できます。

ここで調べたいのは「どの関数が悪いか」だけではありません。各段階で、次の情報を記録します。

  • 呼び出し側で使われていた変数名

  • 渡された値、型、オブジェクトの状態

  • 呼び出し先で受け取った引数名

  • ByValかByRefか

  • 値が正常だった最後の段階

5. 実引数と仮引数を対応させる

呼び出し側が次のコードだったとします。

ImportData sourcePath, outputSheet

呼び出し先は次の定義です。

Private Sub ImportData( _
    ByVal filePath As String, _
    ByVal targetSheet As Worksheet)

対応関係は次のとおりです。

sourcePath  → filePath
outputSheet → targetSheet

呼び出し元で outputSheet Is Nothing なら、呼び出し先の targetSheetを修正しても根本解決になりません。さらに上の呼び出し元へ遡り、outputSheetを取得した処理を調べます。

ByRefの場合は、呼び出し先が呼び出し元の変数を書き換えられます。値が途中で変化した問題では、引数定義と代入箇所を合わせて確認します。

6. シートに見えない中間データを確認する

業務マクロの処理本体は、シート上ではなく、配列やDictionaryの中にあることがあります。

CSV
  → 二次元配列
      → 商品コードDictionary
          → 集計結果配列
              → 出力シート

最終シートだけが正しく見えても、中間配列で列がずれている場合があります。逆に、エラーが出た出力処理は正しく、中間Dictionaryに必要なキーが入っていないこともあります。

最低限、次を確認します。

  • 配列の次元、LBound、UBound、件数

  • Dictionaryのキー数と代表的なキー

  • Collectionの件数

  • WorkbookとWorksheetの名前

  • RangeのAddressと親Worksheet

  • Variantの実際の型

  • 空文字、Null、Empty、Nothingの違い

大量データをすべて表示するのではなく、件数、先頭、末尾、問題となるキーを絞って観察します。

7. 調査結果を一枚の記録へ戻す

デバッグで見つけた内容を、その場限りの発見にしてはいけません。次の三つを一つの記録へまとめます。

  • 静的な呼び出しツリー

  • 今回実行されたコールスタック

  • 各境界で確認したデータ状態

記録例は次のようになります。

RunReport
  → ImportSales(sourcePath="sales.csv")
      → ReadCsv(rowCount=1,250)
          → BuildProductMap(keyCount=318)
              → WriteResult(targetSheet="集計")

正常値と異常値の境界が分かれば、修正対象を狭くできます。

8. 実務で使う標準手順

手順1 本番ブックを直接触らず、調査用コピーを作る。

手順2 自動実行マクロ、外部接続、出力先を確認する。

手順3 mod_amalgamでコード全体を検索できる形にする。

手順4 mod_listで入口候補と関連プロシージャを探す。

手順5 mod_treeで静的な呼び出し経路を確認する。

手順6 値が変わる境界へブレークポイントを置く。

手順7 停止したら Ctrl+Lで呼び出し元へ遡る。

手順8 実引数、仮引数、ByVal、ByRefを確認する。

手順9 配列、Dictionary、Range、オブジェクトを観察する。

手順10 正常だった最後の地点と、異常になった最初の地点を記録する。

手順11 原因を再現できてから修正する。

手順12 調査用コードや出力を本番へ残さない。

9. 調査時の安全上の注意

引き継いだマクロには、Workbook_Openなどの自動実行処理、ファイル削除、上書き保存、メール送信、外部データ更新が含まれる可能性があります。

信頼できる出所か確認し、コピー、隔離した出力先、必要に応じたネットワーク切断など、対象に合った安全策を取ります。調査用に滝Libや出力コードを追加した場合は、元ブックへ混入させないよう差分を確認します。

10. まとめ

VBAデバッグで重要なのは、止まった一行を直すことではありません。値がどこから来て、どの呼び出し境界で異常になったかを突き止めることです。

滝Libの mod_amalgam、mod_list、mod_treeは静的な地図を作ります。ブレークポイント、Ctrl+L、ローカル変数の確認は、今回実行された経路と値を見せます。

エラー地点から呼び出し元へ遡り、実引数と仮引数を対応させ、シートに見えない配列やDictionaryまで確認する。この順序を守ると、推測ではなく観察結果に基づいて修正箇所を決められます。

出典メモ

  • 記事素材: ほえほえ作成の takilib_macro_debug_research_article_source.md。2026-08-01確認。

  • 滝Libの機能名は、C:\Dropbox\takilib\release\latest_release\taki3libUTF8.basの mod_amalgam、mod_list、mod_treeを2026-08-01に確認した。

  • Microsoft Learn「View menu」: https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/view-menu

  • 既存の「滝Libで既存ExcelVBAマクロを調査する」と重ならないよう、本記事は実行時デバッグとコールスタックへ焦点を限定した。