VBEの「エクスポート/インポート」を極める:Gitなき環境におけるモジュール駆動アーキテクチャと資産管理
Excel VBAの開発現場において、未だに「`.xlsm`ファイルそのものを共有フォルダで上書き保存し合う」「シートにコードがベタ書きされている」という悪夢のような光景に出くわすことがある。バージョン管理システム(Gitなど)が導入されていない、あるいはセキュリティ上の理由で外部ツールを持ち込めないレガシー環境において、コードの散逸と属人化を防ぐ唯一にして最大の防衛線が、VBE(Visual Basic Editor)の標準機能である「エクスポート」と「インポート」だ。
単なる「ファイルの書き出し・読み込み」と侮ってはならない。モジュール単位での切り出しは、クラスのライフサイクル管理、API連携、そして複数開発者による並行作業を成立させるためのアーキテクチャの根幹である。
本稿では、VBEのエクスポート/インポート機能を単なるバックアップ手段としてではなく、堅牢なシステム構築のためのエンジニアリング手法として昇華させる極限の知見を解説する。
—
1. なぜ「バイナリ保存」では戦えないのか: `.xlsm` の構造的欠陥
Excelファイル(`.xlsm` / `.xls`)は、実態としてZIP圧縮されたXML群(OpenXML形式)のパッケージである。VBAのバイナリコード(`vbaProject.bin`)はその内部にカプセル化されている。
このバイナリをそのままバージョン管理や共有の単位とすることには、致命的な欠陥がある。
- 差分管理の崩壊: バイナリの差分比較は不可能であり、誰がどこを変更したのかをコードレベルで追跡できない。
- マージの不可能性: 2人の開発者が同時に `.xlsm` を修正した場合、後から保存した方の変更が完全に上書きされる(コンフリクトの自動解決ができない)。
- ノイズの混入: バイナリ内にはタイムスタンプや一時的なメタデータが含まれるため、実質的なコード変更がなくともファイルハッシュが変わり、無駄な差分を生む。
これに対し、標準モジュール(`.bas`)、クラスモジュール(`.cls`)、フォーム(`.frm` / `.frx`)を個別のテキスト(ソースコード)としてエクスポートし、ファイルシステム上で管理する手法を「モジュール駆動開発」と呼ぶ。これにより、レガシーな環境であっても擬似的なソースコード管理体制を構築できる。
—
2. 手動作業の排除:VBEコンポーネントの完全自動化スクリプト
手動で「右クリック > エクスポート」を繰り返すのは、エンジニアの労力の無駄遣いである。さらに言えば、人間は必ずミスをする。
ここで、VBE自身のオブジェクトモデル(`VBComponent` オレゴン・モデル)を操作し、ブック内の全モジュールを一括して指定ディレクトリへエクスポート、および一括インポートする「メタ・オートメーション」のコードを提示する。
このコードを実行するには、Excelのセキュリティ設定で「Visual Basic プロジェクトへのプログラムからのアクセスを信頼する」にチェックが入っている必要がある。
一括エクスポート / インポートの実装
以下のコードを、作業用のアドインまたは管理用ブックの標準モジュールに配置する。
Option Explicit
‘ =========================================================================
‘ ódulo名: modVBECodeManager
‘ 概要 : VBEコンポーネントのエクスポートおよびインポートを自動化する
‘ 前提 : 「VBA プロジェクトへのプログラムからのアクセスを信頼する」が有効であること
‘ =========================================================================
Public Sub ExportAllModules()
Dim targetBook As Workbook
Dim vbComp As Object ‘ VBComponent
Dim exportPath As String
Dim ext As String
‘ アクティブなブックを対象とする(必要に応じてパスを変更)
Set targetBook = ActiveWorkbook
If targetBook.Path = “” Then
MsgBox “一度ブックを保存してから実行してください。”, vbCritical
Exit Sub
End If
‘ 同階層に “SourceCode” フォルダを作成
exportPath = targetBook.Path & “\SourceCode\”
Call EnsureDirectoryExists(exportPath)
On Error GoTo ErrorHandler
For Each vbComp In targetBook.VBProject.VBComponents
‘ ドキュメントモジュール(ThisWorkbookやシート)はエクスポート形式が特殊なため除外、または拡張子を調整
‘ ここでは標準(1)、クラス(2)、フォーム(3)を対象とする
If vbComp.Type = 1 Or vbComp.Type = 2 Or vbComp.Type = 3 Then
Select Case vbComp.Type
Case 1: ext = “.bas”
Case 2: ext = “.cls”
Case 3: ext = “.frm”
End Select
‘ エクスポート実行
vbComp.Export exportPath & vbComp.Name & ext
Debug.Print “Exported: ” & vbComp.Name & ext
End If
Next vbComp
MsgBox “すべてのモジュールのエクスポートが完了しました。” & vbCrLf & “出力先: ” & exportPath, vbInformation
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
End Sub
Public Sub ImportAllModules()
Dim targetBook As Workbook
Dim importPath As String
Dim fileName As String
Dim fso As Object
Dim vbComp As Object
Set targetBook = ActiveWorkbook
importPath = targetBook.Path & “\SourceCode\”
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(importPath) Then
MsgBox “インポート元フォルダが存在しません: ” & importPath, vbCritical
Exit Sub
End If
If MsgBox(“既存のモジュールを削除、または競合する可能性があります。続行しますか?”, vbYesNo + vbExclamation) = vbNo Then
Exit Sub
End If
On Error GoTo ErrorHandler
‘ フォルダ内のソースファイルを走査してインポート
fileName = Dir(importPath & “.”)
Do While fileName <> “”
Select Case LCase(fso.GetExtensionName(fileName))
Case “bas”, “cls”, “frm”
‘ 既存の同名コンポーネントが存在する場合は事前に削除(競合回避)
Dim compName As String
compName = fso.GetBaseName(fileName)
On Error Resume Next
Set vbComp = targetBook.VBProject.VBComponents(compName)
If Not vbComp Is Nothing Then
targetBook.VBProject.VBComponents.Remove vbComp
End If
On Error GoTo ErrorHandler
‘ インポート実行
targetBook.VBProject.VBComponents.Import importPath & fileName
Debug.Print “Imported: ” & fileName
End Select
fileName = Dir()
Loop
MsgBox “すべてのモジュールのインポートが完了しました。”, vbInformation
Exit Sub
ErrorHandler:
MsgBox “インポート中にエラーが発生しました: ” & Err.Description, vbCritical
End Sub
‘ 簡易ディレクトリ作成ヘルパー
Private Sub EnsureDirectoryExists(ByVal path As String)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(path) Then
fso.CreateFolder path
End If
End Sub
—
3. レガシー環境とチーム開発における「競合・同期」の鉄則
エクスポートしたテキストファイルを社内共有サーバーやローカルのGitリポジトリ(Git for Windows等と組み合わせる場合)で管理する際、シニアエンジニアとして押さえておくべき実務上の知見がある。
① `Attribute` 行の汚染とマージコンフリクト
VBAのモジュールファイルをテキストエディタ(VS Codeなど)で開くと、ファイルの先頭付近に以下のようなメタデータが自動付与されていることがわかる。
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 D1UseTrue
END
Attribute VB_Name = “clsDatabase”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
この `Attribute` 行はVBEが内部的にオブジェクトの性質を保持するために自動生成・更新するものである。複数人が別々に編集してマージを行う際、この属性値やインスタンスID的な記述でコンフリクトが起きやすい。
対策: チーム開発のルールとして、クラスのプロパティ(`Exposed` や `PredeclaredId` など)を変更する作業は担当者を1人に絞り、安易な属性変更を伴うコミットを避けること。
② 改行コードの罠 (CRLF vs LF)
Windows環境のExcelは標準で CRLF の改行コードを要求する。しかし、Gitなどのリポジトリ側で自動改行コード変換(`core.autocrlf = true`)が有効になっていない場合、Linux環境や混在環境を経由した際に改行コードが LF に変わり、VBEへ再インポートした際にコードが1行に潰れて読み込まれるという致命的な文字化け・構文エラーが発生する。
対策: チーム内の `.gitattributes` 等でVBAファイル(`.bas`, `.cls`, `.frm`)の改行コードは明示的に `crlf` に固定する設定を強制すること。
—
4. チーフアーキテクトからの提言:コード資産の外部化による真のメリット
VBEのエクスポート/インポートを日常のビルドプロセス(あるいは定型作業)に組み込むことで、VBA開発のパラダイムは劇的に変わる。
1. コードレビューの実現:
GitHubや社内GitLabにソースコード(`.bas`, `.cls`)をプッシュすることで、プルリクエストを通じたコードレビューが可能になる。「なぜこのループでDoEventsを入れているのか」「このAPI宣言の型定義に誤りはないか」といった品質担保を、Excelファイルを見せることなく実施できる。
2. レガシーシステムからの脱却とモジュール再利用:
業務システムごとに散在していた汎用ユーティリティ(文字列操作、JSONパーサー、ADOラッパーなど)をテキスト資産として一元管理し、新規案件の立ち上げ時にはインポートスクリプトを叩くだけで、最新の共通ライブラリ環境が一瞬で構築される。
「Excelだからバージョン管理などできない」という言い訳は、技術の無知を露呈しているに過ぎない。VBEのエクスポート/インポートという地味ながらも強力なプリミティブを完全に手懐け、堅牢なモジュール駆動アーキテクチャを構築してほしい。
