Access VBAを掌握する極限の知見:`Application.CurrentProject.AllModules`による動的コード解析と保守ツールの極意
レガシーシステムの寿命は、往々にして「コードのブラックボックス化」によって縮まる。
数年、あるいは数十年におよぶ改修の歴史の中で、誰も全体像を把握できなくなったAccessデータベース。その中身を紐解くとき、私たちはVBE(Visual Basic Editor)の画面をマウスでポチポチと開いて回るという、前近代的な苦行から脱却しなければならない。
シニアエンジニアや社内システム管理者が直面するこの「巨大化・複雑化したMDB/ACCDBの保守」という壁を突破するため、今回は `Application.CurrentProject.AllModules` を核としたVBAコードの動的解析エンジン、およびメタデータ抽出ツールの設計思想と実装を解説する。
—
1. なぜ「動的解析ツール」が必要なのか:オブジェクトモデルの深層
Access VBAのオブジェクトモデルにおいて、`CurrentProject.AllModules` コレクションは、データベース内に存在するすべての標準モジュール(Module)およびクラスモジュール(Class Module)のコンテナである。
フォームやレポートに埋め込まれたコード(いわゆる「オブジェクト内コード」)は別管理となるが、システムのコアロジックや共通ライブラリの大部分は、これらのモジュール群に集約されている。
レガシー環境におけるVBEの限界
- 手動検索の破綻: 数十個のモジュールに散らばる特定のAPI呼び出しや、非推奨となった関数名を一斉置換・調査する場合、VBEの「検索」機能では取りこぼしが発生する。
- 依存関係の不可視性: どのプロシージャがどこから呼ばれているか(コールグラフの欠如)が、リファクタリングを極限まで困難にする。
これをコードの力で自動走査し、インベントリ(目録)化することこそが、堅牢なシステム保守の第一歩である。
—
2. 実装:モジュール一括走査・解析エンジンの全貌
以下のコードは、データベース内のすべてのモジュールを走査し、指定したキーワード(プロシージャ名や変数定義など)が含まれる行を抽出し、ログテーブルへ出力する実用的なメンテナンスツールのコアロジックである。
Option Compare Database
Option Explicit
‘ =========================================================================
‘ 処理名: データベース内モジュール動的解析ツール
‘ 概要 : AllModulesを走査し、指定キーワードの検索と出現行の特定を行う
‘ =========================================================================
Public Sub ExecuteModuleAnalyzer(ByVal TargetKeyword As String)
Dim appAccess As Access.Application
Dim modItem As Access.Module
Dim doc As AccessObject
Dim i As Long
Dim totalLines As Long
Dim currentLineText As String
Dim hitCount As Long
‘ メモリ最適化とパフォーマンス向上のための画面描画停止
Application.Echo False
DoCmd.Hourglass True
hitCount = 0
On Error GoTo ErrorHandler
Debug.Print “=== モジュール解析開始: 検索キーワード [” & TargetKeyword & “] ===”
‘ 1. カレントプロジェクトの全モジュールを走査
For Each doc In Application.CurrentProject.AllModules
‘ モジュールがデザインビューで閉じている場合は開く必要がある
‘ ※実際にはドキュメントを開かずにVBEコンポーネントを直接叩く方法もあるが、
% 安定性と確実性を考慮し、ここでは標準的なオープン・スキャン方式を採用する
DoCmd.OpenModule doc.Name
Set modItem = Modules(doc.Name)
totalLines = modItem.CountOfLines
If totalLines > 0 Then
‘ モジュール内の全行をループ
For i = 1 To totalLines
currentLineText = modItem.Lines(i, 1)
‘ キーワード一致判定(大文字小文字を区別しない)
If InStr(1, currentLineText, TargetKeyword, vbTextCompare) > 0 Then
hitCount = hitCount + 1
‘ 検出結果をイミディエイトウィンドウおよびログ出力(必要に応じてテーブルへ保存)
Debug.Print “発見 [モジュール: ” & doc.Name & ” | 行: ” & i & “]: ” & Trim$(currentLineText)
End If
Next i
End If
‘ 2. オブジェクトの明示的クローズ(メモリリーク防止の鉄則)
DoCmd.Close acModule, doc.Name, acSaveNo
Next doc
Debug.Print “=== 解析完了: 総ヒット数 ” & hitCount & ” 件 ===”
CleanUp:
‘ 画面描画とカーソルの復元
Application.Echo True
DoCmd.Hourglass False
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “解析エンジンエラー”
Resume CleanUp
End Sub
—
3. シニアエンジニアが押さえるべき「極限の知見」とアーキテクチャの罠
上記のコードは一見シンプルに見えるが、Access VBAの深層を知るエンジニアであれば、いくつかの重大な落とし穴と、それを回避するための知見に気づくはずだ。
① VBEの参照とセキュリティ設定(COMコンポーネントの罠)
Accessでコード解析を行う際、より深くVBAのプロパティ(VBEオブジェクトモデル)にアクセスするためには、`Microsoft Visual Basic for Applications Extensibility 5.3` への参照設定が必要となる。
さらに、セキュリティセンターの「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックが入っていなければ、外部からのコード走査はセキュリティ例外(エラー)によって阻まれる。
実務上の対策: 社内配布ツールとする場合、レジストリを直接操作してこの信頼設定をサイレントに変更するか、上記コードのように `Modules` コレクションの安全なラッパー経由でアクセスする設計に留めるべきである。
② メモリリークとCOMオブジェクトの解放
VBAはガベージコレクションを持たない。`For Each` でループする際、`AccessObject` や `Module` オブジェクトを暗黙的に生成・破棄するため、長時間の実行や巨大なデータベースではメモリリークを引き起こすリスクがある。
- ループ内で使用したオブジェクト変数(`Set modItem = Nothing` など)は、スコープを意識して適切に解放すること。
- 大規模システムでは、一度にすべてのモジュールを開くのではなく、エラーハンドリングを強固にしつつ、ガベージが蓄積しないようこまめに `DoEvents` を挟む配慮が必要となる。
③ フォーム・レポートモジュールの盲点
冒頭で触れた通り、`CurrentProject.AllModules` は独立したモジュールしか捉えない。フォーム(`Form_`)やレポート(`Report_`)のクラスモジュール内にあるコードを解析対象に含めるには、`CurrentProject.AllForms` および `CurrentProject.AllReports` を同様に走査し、それぞれの `HasModule` プロパティを判定した上で処理を分岐させる必要がある。
完全なインベントリを作るためには、この拡張レイヤーの実装が不可欠となる。
—
4. 保守ツールの高度化:テーブルへのメタデータ永続化
イミディエイトウィンドウに出力するだけでは、シニア管理者のレポートとしては不十分だ。抽出した結果を専用の管理用テーブル(例: `T_CodeAnalysisLog`)に書き込むことで、データベースの「コード健康診断書」を自動生成するシステムへと昇華させることができる。
‘ ログテーブルへの書き込みロジックの断片
Private Sub LogResult(ModuleName As String, LineNo As Long, CodeSnippet As String, Keyword As String)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset(“T_CodeAnalysisLog”, dbOpenTable)
rs.AddNew
rs!AnalysisDate = Now
rs!ModuleName = ModuleName
rs!LineNumber = LineNo
rs!TargetKeyword = Keyword
rs!CodeLine = Left$(CodeSnippet, 255) ‘ フィールド長制限への配慮
rs.Update
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
このようにDAO(Data Access Objects)を直接叩くことで、ADOのオーバーヘッドを排除し、極限まで高速なロギングを実現する。
—
5. 総括
Accessは「手軽なプロトtypingツール」と揶揄されることがあるが、ひとたび巨大化すれば、エンタープライズの基幹システムをも凌ぐ複雑怪奇なスパゲッティコードの温床となる。
`Application.CurrentProject.AllModules` をはじめとするAccessオブジェクトモデルの深部を理解し、手動の限界をコードの自動化で超越すること。これこそが、レガシーシステムの寿命を延ばし、エンジニアとしての尊厳を守る唯一の手段である。
妥協なきコードで、ブラックボックスに光を突き刺せ。
