【実務・中級編】Application.VBEオブジェクトを用いた、モジュール内のプロシージャ名一括取得とドキュメント化 – Access VBA解析バイブル

スポンサーリンク

こんにちは。開発プロジェクトの現場で、泥臭いレガシーシステムの改修や自動化に立ち向かっているエンジニアの皆さん。

Access VBAにおける最大にして最悪のブラックボックス、それは「誰も中身を把握していないVBAモジュールの山」ではないだろうか。前任者が残した数万行のコード、散らばったプロシージャ、ドキュメントのない仕様。改修のたびに「どこに影響が出るか分からない」と冷や汗をかき、全モジュールを人力で目視確認して仕様書を起こす……そんな不毛な作業に、いつまで貴重な時間を費やすつもりか?

今回は、Accessの内部構造の核心である `Application.VBE` オブジェクト を完全に掌握し、モジュール内のプロシージャ名をプログラムの力で一網打尽に取得、自動ドキュメント化する極限の手法を伝授する。

単なる「お遊びのコード列挙」ではない。実務の現場で耐えうる堅牢性、セキュリティの罠、そしてパフォーマンスを考慮したプロダクションコードを公開しよう。

なぜ「人力の仕様書作成」は破綻するのか?

業務システム開発において、ドキュメントの同期は永遠の課題だ。コードを修正した瞬間に仕様書が古びる。これを解決するには「コード自身に語らせる(セルフドキュメンテーション)」しかない。

しかし、Access VBAから自身のコードを解析するには、通常の DAO や ADO とは異なる、VBE(Visual Basic for Applications)のオブジェクトモデルを直接叩く必要がある。ここには、を知る者だけが踏み越えられる「厳格なルール」が存在する。

実務で絶対に直面する2つの壁

1. セキュリティの罠:「VBEプロジェクトへのプログラムからのアクセスを信頼する」にチェックが入っていない環境では、コードがクラッシュするか実行時エラーになる。
2. メモリリークとCOMオブジェクトの解放:VBE関連のオブジェクト(`VBComponent`, `CodeModule` 等)は、不適切に参照を保持するとAccessが不安定になり、最悪の場合mdb/accdbが破損する。

これらをロジカルにクリアし、安全かつ高速にプロシージャを抽出するアーキテクチャを構築する。

事前準備:プロジェクトの信頼設定

プログラムを走らせる前に、Accessのセキュリティ設定を確実にクリアしておく必要がある。これを行わないと、`Application.VBE` にアクセスした瞬間に「アプリケーションによる Visual Basic プロジェクトへのアクセスは信頼されていません」という致命的なエラーが発生する。

  • Accessの設定手順:

1. [ファイル] > [オプション] > [トラスト センター] > [トラスト センターの設定] を開く。
2. [マクロの設定] で 「Visual Basic プロジェクトへのプログラムからのアクセスを信頼する」 にチェックを入れる。

※エンタープライズ環境ではグループポリシーで制限されている場合がある。その場合はIT部門との調整が必要となる点に留意してほしい。

プロダクションコード:VBEを完全制御するプロシージャ抽出エンジン

以下のコードを、解析対象とは別の「ユーティリティ用モジュール」に貼り付けてほしい。
エラーハンドリング、参照のクリーンアップ、そして大規模プロジェクトに耐える走査ロジックを組み込んである。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 模範的アーキテクチャ: VBE走査によるプロシージャ自動列挙エンジン
‘ =========================================================================
Public Sub ExportProcedureListToTable()
Dim vbProj As Object ‘ VBProject
Dim vbComp As Object ‘ VBComponent
Dim codeMod As Object ‘ CodeModule

Dim db As DAO.Database
Dim rs As DAO.Recordset

Dim i As Long
Dim lineNum As Long
Dim procName As String
Dim procKind As Integer
Dim targetTable As String

targetTable = “m_ProcedureList”

‘ 1. 出力先テーブルの準備(存在しなければ作成、存在すればクリア)
Set db = CurrentDb
Call InitializeLogTable(db, targetTable)

Set rs = db.OpenRecordset(targetTable, dbOpenDynaset)

‘ 2. VBEプロジェクトの取得 (CurrentDb.VBE ではなく Application.VBE を使用)
On Error GoTo ErrorHandler
Set vbProj = Application.VBE.ActiveVBProject

‘ 3. 全モジュールのイテレーション
For Each vbComp In vbProj.VBComponents
‘ 標準モジュール(1)、クラスモジュール(2)、フォーム/レポート(100)を対象とする
If vbComp.Type = 1 Or vbComp.Type = 2 Or vbComp.Type = 100 Then
Set codeMod = vbComp.CodeModule

lineNum = 1
‘ モジュールの先頭から最後までプロシージャをスキャン
Do While lineNum < codeMod.CountOfLines procName = codeMod.ProcOfLine(lineNum, procKind) If procName <> “” Then
‘ 重複登録を防ぐため、プロシージャの最初の行でのみ記録
If codeMod.ProcStartLine(procName, procKind) = lineNum Then
rs.AddNew
rs!ModuleName = vbComp.Name
rs!ModuleType = GetModuleTypeName(vbComp.Type)
rs!ProcedureName = procName
rs!ProcedureType = GetProcedureTypeName(procKind)
rs!StartLine = lineNum
rs!LinesCount = codeMod.ProcCountLines(procName, procKind)
rs.Update
End If

‘ 次のプロシージャへジャンプ(パフォーマンス最適化)
lineNum = codeMod.ProcStartLine(procName, procKind) + codeMod.ProcCountLines(procName, procKind)
else
lineNum = lineNum + 1
End If
Loop
End If
Next vbComp

MsgBox “プロシージャの抽出が完了しました。仕様書用テーブルを確認してください。”, vbInformation, “完了”

CleanUp:
‘ 4. 厳格なオブジェクト解放(メモリリーク防止)
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not db Is Nothing Then Set db = Nothing
Set codeMod = Nothing
Set vbComp = Nothing
Set vbProj = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

‘ =========================================================================
‘ ヘルパー関数群
‘ =========================================================================

Private Sub InitializeLogTable(db As DAO.Database, tableName As String)
‘ 既存テーブルの存在チェックと再作成
Dim tdf As DAO.TableDef
Dim exists As Boolean

exists = False
For Each tdf In db.TableDefs
If tdf.Name = tableName Then
exists = True
Exit For
End If
Next tdf

If exists Then
db.Execute “DELETE FROM ” & tableName & “;”, dbFailOnError
Else
Set tdf = db.CreateTableDef(tableName)
tdf.Fields.Append tdf.CreateField(“ModuleName”, dbText, 255)
tdf.Fields.Append tdf.CreateField(“ModuleType”, dbText, 50)
tdf.Fields.Append tdf.CreateField(“ProcedureName”, dbText, 255)
tdf.Fields.Append tdf.CreateField(“ProcedureType”, dbText, 50)
tdf.Fields.Append tdf.CreateField(“StartLine”, dbLong)
tdf.Fields.Append tdf.CreateField(“LinesCount”, dbLong)
db.TableDefs.Append tdf
End If
End Sub

Private Function GetModuleTypeName(moduleType As Long) As String
Select Case moduleType
Case 1: GetModuleTypeName = “標準モジュール”
Case 2: GetModuleTypeName = “クラスモジュール”
Case 100: GetModuleTypeName = “フォーム/レポート”
Case Else: GetModuleTypeName = “その他”
End Select
End Function

Private Function GetProcedureTypeName(procKind As Integer) As String
‘ vbext_ProcKind の値に対応
Select Case procKind
Case 0: GetProcedureTypeName = “Sub / Function (general)”
Case 1: GetProcedureTypeName = “Property Get”
Case 2: GetProcedureTypeName = “Property Let”
Case 3: Get ProcedureTypeName = “Property Set”
Case Else: GetProcedureTypeName = “不明”
End Select
End Function

アーキテクチャの解説:なぜこのコードが「プロ仕様」なのか?

1. `Application.VBE` の採用とスコープ

初心者はよく `CurrentDb.VBE` のような存在しないプロパティや、曖昧な参照を書きたがる。AccessのVBEはアプリケーション全体で一つしか存在しない。そのため、確実かつ安全にアクセスするには `Application.VBE.ActiveVBProject` を叩くのが唯一にして最大の正解である。

2. ループの最適化(パフォーマンスの担保)

モジュールの全行を1行ずつ `lineNum = lineNum + 1` で走査するのは、コード量が多いシステムでは致命的に遅い。
上記のコードでは、`CodeMod.ProcStartLine` と `ProcCountLines` を組み合わせることで、1つのプロシージャを検知した瞬間にその終了行へジャンプ(Skip)させている。これにより、数万行のコードベースであっても数秒でスキャンが完了する。

3. オブジェクトの徹底的な解放(ガベージコレクションの自前化)

VBAのCOMオブジェクト参照は、明示的に `Set xxx = Nothing` しないとAccessプロセス内にゾンビのように残り続ける。これを放置すると、VBAエディタが強制終了したり、コンパイルエラーが頻発するようになる。
`CleanUp` ラベリングによる確実な参照破棄は、プロフェッショナルなVBA開発者にとっての必須作法だ。

発展:ここから「真の自動仕様書生成」へつなげる

このコードによって、Accessデータベース内の全プロシージャ名、行数、配置場所が `m_ProcedureList` テーブルに綺麗に格納された。

ここから先は、あなたのアイデア次第でいくらでも自動化の魔改造ができる。

  • Excel出力: このテーブルをテンプレート化されたExcelシートにエクスポートし、右側に「機能概要」「担当者」「テスト結果」の列を自動生成する。
  • 依存関係解析: `CodeModule.Lines` を正規表現で走査し、どのプロシージャがどの関数を呼び出しているか(コールグラフ)まで抽出する。

「手動で仕様書を書く」という前世紀の悪習は、今日で終わりだ。
コードはコードに語らせ、人間はより本質的なビジネスロジックの設計に集中する。これこそが、Access VBAを完全に掌握したエンジニアの姿である。

タイトルとURLをコピーしました