VBEを完全掌握せよ:`Application.VBE`オブジェクトによるプロシージャ自動列挙と極限のドキュメント化
レガシーシステムの暗部。それは、前任者が遺した「動くが誰も中身を知らないAccessデータベース」である。
何万行ものスパゲッティコード、場当たり的なエラーハンドリング、そして一切更新されない仕様書。この負債を前にしたとき、手作業でコードを読み解くのはエンジニアの寿命を削るだけの愚行だ。
我々はコードを書くだけのプログラマーではない。システムを支配するアーキテクトである。
今回は、Accessの内部心臓部である `Application.VBE` オブジェクトを直接叩き、モジュール内のプロシージャ(Sub / Function)を網羅的に抽出し、ドキュメント化を完全自動化する手法を解説する。
VBAのメタプログラミング領域に踏み込む。覚悟のある者だけについてきてほしい。
—
1. 踏み越えてはならない領域:VBE操作の前提条件
`Application.VBE`(Visual Basic for Applications Environment)を操作するということは、IDE(統合開発環境)のメモリ空間に直接干渉することを意味する。ここには通常のVBAコーディングとは異なる、厳格なルールが存在する。
必須のセキュリティ設定
VBEオブジェクトモデルをプログラムから制御するには、Accessのセキュリティ設定で以下の壁を突破しておかなければならない。
1. 「Visual Basic プロジェクトへのプログラムからのアクセスを信頼する」 の有効化(TrustAccessVBOM)。
これを怠ると、実行瞬間に `エラー 1004` またはそれに類するオートメーションエラーが叩きつけられる。
2. 参照設定の追加:
VBEオブジェクトを安全かつ強固に扱うためには、VBAコードから `VBIDE` ライブラリを明示的にバインドする必要がある。
—
2. アーキテクチャ設計:メモリリークを根絶する参照の作法
VBEオブジェクトモデルは、COM(Component Object Model)の塊である。
`VBComponent` や `CodeModule` といったオブジェクトを巡回する際、適切な解放を行わなければ、Accessのプロセス内にメモリリークが蓄積し、最悪の場合はAccessそのものがクラッシュする。
チーフアーキテクトとして、以下の鉄則を遵守するコードを書く。
- オブジェクト変数の完全なスコープ管理と `Nothing` 代入
- 早期バインディング(Early Binding)による型安全性の確保
まずは、VBEの参照設定(Microsoft Visual Basic for Applications Extensibility 5.3)をコード内で動的に検証しつつ、安全にプロシージャ名を取得するコアエンジンを実装する。
—
3. 実装:プロシージャ自動列挙・ドキュメント化エンジン
以下のコードは、現在のデータベース内に存在するすべての標準モジュール、クラスモジュール、フォーム/レポートのコードモジュールを走査し、そこに定義されているすべてのプロシージャ名(Sub / Function / Property)、スコープ(Public / Private)、およびコード行数を抽出するスクリプトである。
‘ ==============================================================================
‘ Module: modVBEInspector
‘ Description: VBEオブジェクトを用いたコード解析・ドキュメント自動生成エンジン
‘ Architecture: Early Binding (Requires Microsoft Visual Basic for Extensibility 5.3)
‘ ==============================================================================
Option Explicit
Public Sub ExportProcedureInventory()
‘ 事前準備: [ツール] -> [参照設定] から “Microsoft Visual Basic for Applications Extensibility 5.3” を有効化すること
Dim vbProj As VBIDE.VBProject
Dim vbComp As VBIDE.VBComponent
Dim codeMod As VBIDE.CodeModule
Dim wsOut As Worksheet ‘ ※Excel連携する場合を想定、またはテーブル出力に改変可能
Dim targetDb As DAO.Database
Dim rsOut As DAO.Recordset
Dim i As Long
Dim lineNum As Long
Dim procName As String
Dim procKind As VBIDE.vbext_ProcKind
On Error GoTo ErrorHandler
‘ 1. VBEプロジェクトの取得
Set vbProj = Application.VBE.ActiveVBProject
‘ 2. 出力先テーブルの初期化(Access内テーブル “T_ProcedureInventory” に出力する設計)
Set targetDb = CurrentDb
targetDb.Execute “DELETE FROM T_ProcedureInventory;”, dbFailOnError
Set rsOut = targetDb.OpenRecordset(“T_ProcedureInventory”, dbOpenTable)
‘ 3. コンポーネント(モジュール、フォーム等)の走査
For Each vbComp vbProj.VBComponents
Set codeMod = vbComp.CodeModule
lineNum = 1
Do While lineNum < codeMod.CountOfLines
' 4. 次のプロシージャ名を取得
procName = codeMod.GetProcOfLine(lineNum, procKind)
If procName <> “” Then
‘ レコードの書き込み
rsOut.AddNew
rsOut!ModuleName = vbComp.Name
rsOut!ModuleType = GetModuleTypeName(vbComp.Type)
rsOut!ProcedureName = procName
rsOut!ProcedureType = GetProcKindName(procKind)
rsOut!StartLine = lineNum
rsOut!LineCount = codeMod.GetProcLines(procName, procKind)
rsOut.Update
‘ 次のプロシージャの先頭行へジャンプ
lineNum = lineNum + codeMod.GetProcLines(procName, procKind)
Else
‘ プロシージャに属さない宣言部などの場合、1行進める
lineNum = lineNum + 1
End If
Loop
‘ COMオブジェクトの解放(メモリ管理の徹底)
Set codeMod = Nothing
Set vbComp = Nothing
Next vbComp
MsgBox “プロシージャインベントリの作成が完了しました。”, vbInformation, “VBEインスペクター”
GoTo Cleanup
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “Critical Error”
Resume Cleanup
Cleanup:
‘ 明示的なオブジェクト解放によるメモリリークの根絶
If Not rsOut Is Nothing Then rsOut.Close: Set rsOut = Nothing
If Not targetDb Is Nothing Then Set targetDb = Nothing
Set vbProj = Nothing
End Sub
‘ ==============================================================================
‘ ヘルパー関数: モジュールタイプの文字列変換
‘ ==============================================================================
Private Function GetModuleTypeName(ByVal mType As VBIDE.vbext_ComponentType) As String
Select Case mType
Case vbext_ct_StandardModule: GetModuleTypeName = “標準モジュール”
Case vbext_ct_ClassModule: GetModuleTypeName = “クラスモジュール”
Case vbext_ct_MSForm: GetModuleTypeName = “ユーザーフォーム”
Case vbext_ct_Document: GetModuleTypeName = “ドキュメント(Form/Report)”
Case Else: GetModuleTypeName = “不明”
End Select
End Function
Private Function GetProcKindName(ByVal pKind As VBIDE.vbext_ProcKind) As String
Select Case pKind
Case vbext_pk_Proc: GetProcKindName = “Sub/Function”
Case vbext_pk_Let: GetProcKindName = “Property Let”
Case vbext_pk_Get: GetProcKindName = “Property Get”
Case vbext_pk_Set: GetProcKindName = “Property Set”
Case Else: GetProcKindName = “Unknown”
End Select
End Function
—
4. チーフアーキテクトの洞察:なぜこの手法が実務で神格化されるのか
単にプロシージャ名を取るだけなら、テキストファイルにエクスポートして正規表現でパースする手もある。しかし、`Application.VBE` を使うことには決定的なアドバンテージがある。
1. リアルタイム性: ファイルをエクスポートするI/Oオーバーヘッドがなく、インメモリで瞬時に解析が完了する。
2. 構造的把握: 単なる文字列ではなく、`CodeModule.GetProcOfLine` といったVBEネイティブのAPIを使うことで、コメントや空白行に惑わされず、正確な「プロシージャの境界」と「行数(肥大化の指標)」を割り出せる。
3. リファクタリングのロードマップ化: 抽出したデータを基に、「行数が300行を超えているスパゲッティ関数(Code Smells)」をSQLで一網打尽に抽出し、技術的負債のヒートマップを描くことができる。
レガシーシステムを恐れるな。システムがどれほど巨大で泥臭くても、その構造はコードで書かれている以上、コードの力で完全に掌握できる。
VBAの限界を嘆く前に、提供されたオブジェクトモデルの深淵を使い切れ。それがプロフェッショナルの仕事である。
