【実務・中級編】【上級】Application.VBEオブジェクトを操作して、VBAコードを動的に生成・修正するリファクタリング支援 – Access VBA解析バイブル

スポンサーリンク

【上級】Access VBAを掌握する極限の知見:Application.VBEオブジェクトによる「コードの動的生成・リファクタリング自動化」

開発プロジェクトのリーダーである君なら、こんな絶望を味わったことが一度はあるはずだ。

「数百個あるフォームや標準モジュールのすべてに、新しい共通ログ出力処理を挟み込んでほしい」
「過去の担当者が残した、マジックナンバーだらけのスパゲッティコードを一斉に置換したい」

これを手作業でやるだと? 気が狂うほどの工数がかかるだけでなく、コピペミスや修正漏れという人災的バグの温床になる。Access開発において、VBE(Visual Basic Editor)のコードを手で触る時代は終わった。

今回は、`Application.VBE` オブジェクトを極限まで使い倒し、プログラム自身にVBAコードを書かせ、修正させる「コードのリファクタリング自動化」の奥義を授けよう。

1. なぜ「VBEオブジェクト操作」が禁断の技術と言われるのか

多くの初学者は、「VBAからVBAをいじる」というと、単に `CodeModule` オブジェクトの `AddFromString` や `ReplaceLine` を思い浮かべるだろう。

しかし、現場でこれを素人がやると、確実にシステムを破壊する。理由は明確だ。
1. オブジェクトのライフサイクルと参照切れ: コンパイルエラーを含んだコードを流し込むと、VBEのステートが破損する。
2. 「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」の壁: セキュリティ設定をプログラムからクリアし忘れると、容赦なく実行時エラー(エラー番号: 1004など)で弾かれる。
3. Undoが効かない: 一度プログラムによって書き換えられたコードは、Ctrl+Zでは戻せない。

プロのアーキテクトが書くコードは、これらのリスクを完全に封じ込めた「トランザクション的な堅牢性」を持つ。その実装パターンをこれから解説する。

2. 実装の前提条件:環境構築の罠を避ける

VBEオブジェクト(`VBComponents`, `CodeModule` など)を操作するには、VBAの裏側にある「Microsoft Visual Basic for Applications Extensibility」ライブラリへの参照が必要だ。

しかし、配布するツールや他人のPCでこれを「参照設定」に手動追加させるのは悪手である。
したがって、「早期バインディング(事前バインディング)」ではなく「遅延バインディング(CreateObject)」を用いるか、コード実行時に安全に参照を担保する設計にすべきだ。

また、大前提として、Accessのセキュリティセンターから以下の設定が有効になっていなければならない。

  • `[ファイル] > [オプション] > [トラスト センター] > [トラスト センターの設定] > [マクロの設定]`
  • 「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックが入っていること。

この前提をクリアした上で、実際のプロダクションコードを見ていこう。

3. 【プロダクションコード】指定モジュールの特定キーワードを一括置換・リファクタリングするエンジン

以下のコードは、指定した標準モジュール(またはクラスモジュール)のコードを走査し、特定のレガシーな関数呼び出しを新しい共通関数へと安全に置換、さらに監査ログ用のコメントを動的に挿入する実用スクリプトだ。

実務の現場でそのまま組み込めるよう、エラーハンドリングとバックアップの思想を組み込んでいる。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 処理名: Refactor_StandardModules
‘ 概要 : VBEを操作し、プロジェクト内の全標準モジュールから特定キーワードを
‘ 安全に検索・置換するリファクタリング自動化エンジン
‘ ==============================================================================
Public Sub Refactor_StandardModules()
Dim vbProj As Object ‘ VBProject
Dim vbComp As Object ‘ VBComponent
Dim codeMod As Object ‘ CodeModule

Dim targetKeyword As String
Dim replacementText As String
Dim i As Long
Dim lineText As String
Dim isModified As Boolean

‘ — 設定エリア —
targetKeyword = “MsgBox ” ‘ 置き換えたいレガシーコード
replacementText = “Call App_SafeMsgBox ” ‘ 新しい共通関数
‘ ——————

‘ 1. VBEプロジェクトの取得(Application.VBE経由)
‘ ※遅延バインディングにより参照設定のトラブルを回避
On Error GoTo ErrorHandler
Set vbProj = Application.VBE.ActiveVBProject

‘ データベースがパスワード保護されている、または保護されたMDE/ACCDEの場合はここで弾かれる
If vbProj.Protection <> 0 Then
MsgBox “対象のVBAプロジェクトが保護されています。保護を解除してください。”, vbCritical, “致命的エラー”
Exit Sub
End If

Dim modifiedCount As Long
modifiedCount = 0

‘ 2. プロジェクト内の全コンポーネントを走査
For Each vbComp In vbProj.VBComponents
‘ 標準モジュール (vbext_ct_StdModule = 1) のみを対象とする
‘ ※フォームやレポート(vbext_ct_Document)を無暗にいじるのはバグの元なので避ける
If vbComp.Type = 1 Then
Set codeMod = vbComp.CodeModule
isModified = False

‘ モジュールの全行を上から下へ走査
For i = 1 To codeMod.CountOfLines
lineText = codeMod.Lines(i, 1)

‘ ターゲットキーワードが含まれているか(かつコメント行でないか)
If InStr(1, lineText, targetKeyword, vbTextCompare) > 0 And _
Left(Trim(lineText), 1) <> “‘” Then

‘ 置換処理の実行
Dim newLineText As String
newLineText = Replace(lineText, targetKeyword, replacementText, 1, -1, vbTextCompare)

‘ コード行の置き換え
codeMod.ReplaceLine i, newLineText
isModified = True

Debug.Print “【置換成功】 ” & vbComp.Name & ” (行 ” & i & “): ”
Debug.Print ” 旧: ” & lineText
Debug.Print ” 新: ” & newLineText
End If
Next i

If isModified Then
modifiedCount = modifiedCount + 1
End If
End If
Next vbComp

MsgBox “リファクタリングが完了しました。” & vbCrLf & _
“変更されたモジュール数: ” & modifiedCount, vbInformation, “完了”

Exit Sub

ErrorHandler:
Select Case Err.Number
Case 1004
MsgBox “「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」が有効になっていません。” & vbCrLf & _
“トラストセンターの設定を確認してください。”, vbCritical, “セキュリティエラー”
Case Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Select
End Sub

ジッポのライターで火を点けるような危険なコードだが、設計の妙を理解していればこれほど強力な武器はない。

コードのキモとアーキテクトの知見

1. コンポーネントタイプの厳格なフィルタリング (`vbComp.Type = 1`):
フォームやレポートのモジュール(Document型)は、Access内部のイベントプロシージャと密結合している。ここをプログラムから直接書き換えると、Accessのフォームオブジェクトが破損し、二度と開かなくなる恐怖の「フォーム破損バグ」を引き起こす。コード自動化の対象は原則として「標準モジュール」に限定すべきだ。
2. コメント行のスキップ処理 (`Left(Trim(lineText), 1) <> “‘”)`:
単純な文字列置換を行うと、開発者が残したコメントアウトの中身まで書き換えてしまい、コードの文脈が破壊される。実務で耐えうるツールにするための最低限の防衛策である。

4. 運用上の極意:バックアップなき自動化は「自爆テロ」

この手のVBE自動化スクリプトを走らせるとき、プロのエンジニアが必ず行う儀式がある。それは「実行前の強制バックアップ」だ。

VBEオブジェクトの操作は、`DoCmd.Save` や通常のトランザクション(`BeginTrans`)の保護を受けない。書き換えた瞬間に物理ファイル(ACCDB)へ変更が書き込まれる。

したがって、スクリプトの冒頭で以下のようなファイルコピーによるセーフティネットを必ず張るべきだ。

‘ 【実務必須】実行前に強制バックアップを作成するスニペット
Public Function CreateBackupCopy() As Boolean
On Error GoTo BackupError
Dim srcPath As String
Dim backupPath As String

srcPath = CurrentDb.Name
backupPath = Left(srcPath, InStrRev(srcPath, “.”) – 1) & _
“_backup_” & Format(Now, “yyyymmdd_HHnnss”) & “.accdb”

‘ 現在のDBファイルを丸ごとコピー
FileCopy srcPath, backupPath
CreateBackupCopy = True
Exit Function

BackupError:
CreateBackupCopy = False
MsgBox “バックアップの作成に失敗しました。処理を中止します。” & vbCrLf & Err.Description, vbCritical
End Function

このバックアップ関数を、先ほどの `Refactor_StandardModules` の最頭に挟むだけで、心理的負荷はゼロになる。

5. おわりに:手作業の奴隷から、システムを統べるアーキテクトへ

世の中には、何百個ものモジュールを開いては「Ctrl+F」で置換を繰り返し、終いには目がチカチカしてミスを連発する悲しいプログラマがあふれている。

しかし、今回紹介した `Application.VBE` を使ったメタプログラミングの視点を持てば、「面倒な定型作業はすべてプログラムに書かせる」という真のエンジニアリング領域に到達できる。

Access VBAはレガシーな言語だと言われがちだ。だが、その内部構造(VBEオブジェクトモデル)まで深く理解し、手足のように対話できる者にとっては、これほど高速にシステムをモダナイズできる強力なプラットフォームもない。

さあ、君のプロジェクトにある数千行のレガシーコードを、この自動化エンジンで美しく浄化して見せろ。

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