【実務・中級編】Application.CurrentProject.AllModulesを活用した、VBAコードの動的解析と保守ツール開発 – Access VBA解析バイブル

スポンサーリンク

【Access VBAを掌握する極限の知見】第1回:Application.CurrentProject.AllModulesが切り拓く、動的コード解析の世界

こんにちは、チーフアーキテクトの私だ。
開発現場でこんな絶望を味わったことはないだろうか。

「何年も前に退職した前任者が作った巨大なAccessデータベース。どこを直しても別の場所でバグが出る……」
「古いAPIのエンドポイントや、廃止予定の共通関数 `Old_Func()` が、このシステムのどこで使われているのか分からない。手動で全モジュールを開いて目視確認するしかないのか?」

愚問だ。プロのエンジニアが手作業でコードの海を泳ぐなど、リソースの無駄遣いに他ならない。
Access VBAには、オブジェクトの内部構造をプログラム自身から監視・解析する強力なメカニズムが備わっている。

今回は、`Application.CurrentProject.AllModules` コレクションを完全に手なずけ、プロジェクト内の全VBAモジュールをプログラムから走査・解析し、特定のキーワードやプロシージャの使用状況を自動で洗い出す「保守・リファクタリング支援ツール」の全貌を授けよう。

1. なぜ「目視」や「VBEの検索機能」では不十分なのか?

VBE(Visual Basic Editor)の標準機能にある「検索(Ctrl + F)」を使えばいいではないか、と思うかもしれない。
だが、それは玩具の話だ。実務の現場では以下の壁に阻まれる。

1. 複数キーワードの一括調査ができない(廃止候補の関数リストが50個あったらどうする?)
2. 「どこで、どのプロシージャから呼ばれているか」という文脈(Context)が残らない
3. 調査結果をエビデンスとして残せない(上長や他部署への報告用にレポート化できない)

私たちが目指すべきは、「データベース自身に自らのコードを監査させ、結果をテーブルやExcelに出力する」というメタプログラミングの領域だ。

2. 堅牢な設計:Accessオブジェクトモデルの罠と回避策

動的コード解析を行う上で、Accessのオブジェクトモデルにはいくつかの「地雷」がある。これを踏まずしてプロダクションコードは書けない。

① `CurrentDb` と `Access.Application` の使い分け

データベースの構造やテーブルを操作する時は `CurrentDb` だが、VBAプロジェクトのモジュール群(`AllModules`)にアクセスするには、アプリケーションルートである `Application` オブジェクトが必須となる。
`CurrentProject` は、現在開いているデータベースのプロジェクトコンテキストを正確に指し示す最高峰のプロパティだ。

② モジュールの「読み込み状態」の罠

`AllModules` コレクションは、プロジェクト内に存在するすべての標準モジュールおよびクラスモジュールを列挙する。しかし、ここで一つ重大な事実がある。
「モジュール内のコードテキスト(`Lines`)を読み取るには、対象のモジュールがVBE上でロードされていなければならない」場合があるのだ。

これをクリアするため、コード解析を行う前に、対象モジュールをプログラム的(あるいはVBEのコンポーネント操作)に安全に触れる状態にするか、あるいは `AccessObject` の特性を理解した走査を行う必要がある。
今回は、実務で最も確実かつエレガントにコードの中身へアクセスする手法を採用する。

3. 実装:コード解析・リファクタリング支援ツール

以下のコードを、解析対象のAccessファイル内の「標準モジュール」に貼り付けてほしい。
このプログラムは、プロジェクト内の全モジュールを走査し、指定した検索キーワード(関数名や変数名など)がどのモジュールの何行目に存在するかを特定し、一時テーブルに出力するプロフェッショナル仕様のツールだ。

Option Explicit
Option Compare Database

‘ =========================================================================
‘ 模範的プロダクションコード:VBAコード動的解析ツール
‘ Architecture: Chief Architect Office
‘ =========================================================================
Public Sub RunCodeAnalyzer(ByVal targetKeyword As String)
On Error GoTo ErrorHandler

Dim appAccess As Access.Application
Dim prj As Access.CurrentProject
Dim mdl As AccessObject
Dim docMod As Object

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

Dim moduleName As String
Dim lineCount As Long
Dim i As Long
Dim lineText As String
Dim matchCount As Long

‘ 引数のバリデーション
If Trim(targetKeyword) = “” Then
MsgBox “検索キーワードが指定されていません。”, vbExclamation, “パラメータエラー”
Exit Sub
End If

Set db = CurrentDb

‘ 解析結果を格納する一時テーブルの準備(存在しなければ作成)
Call InitializeResultTable(db)

‘ 結果格納用レコードセットのオープン
Set rs = db.OpenRecordset(“T_CodeAnalysisResult”, dbOpenDynaset)

‘ ApplicationおよびCurrentProjectの取得
Set appAccess = Application
Set prj = appAccess.CurrentProject

matchCount = 0

‘ 全モジュール(標準モジュール、クラスモジュール)の走査
‘ ※フォームやレポートの背後にあるコードモジュールは除外し、純粋なコードコンポーネントを対象とする
Dim objMod As AccessObject
For Each objMod In prj.AllModules
moduleName = objMod.Name

‘ デバッグ出力:スキャン中のモジュール名
Debug.Print “Scanning Module: ” & moduleName

‘ モジュールをコード操作可能な状態でVBEにロードさせる
DoCmd.OpenModule moduleName

‘ VBEのVBComponentsからコード行を取得
Set docMod = appAccess.VBE.ActiveVBProject.VBComponents(moduleName).CodeModule
lineCount = docMod.CountOfLines

‘ 行ごとの走査(1行目から最終行まで)
For i = 1 To lineCount
lineText = docMod.Lines(i, 1)

‘ キーワードが含まれているか判定(大文字小文字を区別しない)
If InStr(1, lineText, targetKeyword, vbTextCompare) > 0 Then
‘ レコードの追加
rs.AddNew
rs(“ModuleName”).Value = moduleName
rs(“LineNumber”).Value = i
rs(“CodeLine”).Value = Left(Trim(lineText), 255) ‘ 溢れ防止
rs(“SearchKeyword”).Value = targetKeyword
rs(“AnalyzedDate”).Value = Now
rs.Update

matchCount = matchCount + 1
End If
Next i

‘ モジュールを閉じる(変更は保存しない)
DoCmd.Close acModule, moduleName, acSaveNo
Next objMod

rs.Close
Set rs = Nothing
Set db = Nothing

‘ 完了通知
MsgBox “コード解析が完了しました。” & vbCrLf & _
“検索キーワード: ” & targetKeyword & vbCrLf & _
“一致した行数: ” & matchCount & ” 件” & vbCrLf & _
“結果はテーブル [T_CodeAnalysisResult] に出力されました。”, _
vbInformation, “解析終了”

Exit Sub

ErrorHandler:
‘ エラー時のクリーンアップとモジュールの強制解放
On Error Resume Next
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Set db = Nothing
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error ” & Err.Number & “: ” & Err.Description, vbCritical, “システムエラー”
End Sub

‘ =========================================================================
‘ 補助ルーチン:結果格納用テーブルの動的生成
‘ =========================================================================
Private Sub InitializeResultTable(ByRef db As DAO.Database)
Dim tdf As DAO.TableDef
Dim tblExists As Boolean

tblExists = False
For Each tdf In db.TableDefs
If tdf.Name = “T_CodeAnalysisResult” Then
tblExists = True
Exit For
End If
Next tdf

If tblExists Then
‘ 既存レコードのクリア
db.Execute “DELETE FROM T_CodeAnalysisResult;”, dbFailOnError
Else
‘ テーブルの新設
Set tdf = db.CreateTableDef(“T_CodeAnalysisResult”)

With tdf
.Fields.Append .CreateField(“ID”, dbLong)
.Fields.Append .CreateField(“ModuleName”, dbText, 100)
.Fields.Append .CreateField(“LineNumber”, dbLong)
.Fields.Append .CreateField(“CodeLine”, dbText, 255)
.Fields.Append .CreateField(“SearchKeyword”, dbText, 100)
.Fields.Append .CreateField(“AnalyzedDate”, dbDate)
End With

db.TableDefs.Append tdf

‘ 主キーおよびオートナンバーの設定(DAO経由)
db.Execute “ALTER TABLE T_CodeAnalysisResult ALTER COLUMN ID COUNTER CONSTRAINT PK_AnalysisID PRIMARY KEY;”, dbFailOnError
End If
End Sub

4. このコードのアーキテクチャ的優位性

1. VBEオブジェクトモデルとの融合
単にAccessのオブジェクトを見るだけでなく、`appAccess.VBE.ActiveVBProject.VBComponents` を叩くことで、VBAの心臓部にダイレクトにアクセスしている。これにより、行単位での精緻なテキスト抽出が可能になる。
2. 安全なライフサイクル管理
`DoCmd.OpenModule` で開き、処理後は必ず `DoCmd.Close acModule, …, acSaveNo` で閉じる。さらにエラーハンドラー内で確実にオブジェクトを解放することで、VBEのメモリリークや不正なロック状態を防ぐ設計にしている。
3. エビデンスの永続化
検索結果をイミディエイトウィンドウに垂れ流すのではなく、自動的に `T_CodeAnalysisResult` テーブルへ蓄積する。これにより、クエリやフォーム、さらにはExcelと連携して「どのモジュールにレガシーコードが何行残っているか」をグラフ化・可視化することが容易になる。

5. チーフアーキテクトからの実践アドバイス

このツールは、単なる「検索の自動化」ではない。チーム開発におけるコード品質のガバナンス(統制)を維持するための武器だ。

例えば、毎週末にタスクスケジューラーやAccessの起動時マクロからこの解析ルーチンを走らせ、特定の禁止ワード(非推奨となった古い関数名や、直書きされたハードコードのパスワード文字列など)が含まれていないかを自動チェックするCI/CDパイプライン的な仕組みを構築することも夢ではない。

Access VBAだからといって、力技の泥臭いコーディングで満足していませんか?
オブジェクトのライフサイクルを理解し、メタプログラミングを駆使すれば、Accessは最速で堅牢なエンタープライズ・プロトタイピング環境へと変貌する。

コードの主導権を、常にあなた自身の手に握らせておきたまえ。

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