プロジェクトファイルの「静かなる腐敗」を断つ:MS Project VBAにおけるバイナリ整合性検証の極意
MS Projectの`.mpp`ファイルは、一見すると単なるデータベースのように見えるが、その実態は複雑なOLE複合ドキュメント構造を持つブラックボックスだ。大規模なスケジュール管理を行う現場において、バックグラウンドでの保存失敗や、ネットワークI/Oの瞬断による「バイナリの静かなる腐敗」は、我々アーキテクトにとって悪夢以外の何物でもない。
今回は、VBAからWindows APIを叩き、ファイル保存という不可逆なプロセスを厳密に監視・検証する、極限の整合性ツールについて解説する。
—
なぜ「保存しました」を信じてはいけないのか
VBAの`FileSave`メソッドがエラーを返さなかったからといって、物理層での書き込みが完了しているとは限らない。特に、共有ドライブ上での運用や、巨大なベースラインを保持するプロジェクトファイルでは、セクタ書き込みの不整合が稀に発生する。
シニアエンジニアとして取るべき防衛策はただ一つ。「保存直前のハッシュ値」と「保存直後のハッシュ値」を比較することだ。
実装の核心:Windows APIによるハッシュ計算
VBA標準機能にはハッシュ計算ライブラリが存在しない。そこで、Windows標準の`bcrypt.dll`を直接呼び出し、メモリ上で高速にSHA-256計算を行う。COMオブジェクトの生成はメモリを浪費するため、APIを直接叩くのが「正解」である。
整合性検証モジュール(抜粋)
‘ 必要なAPI定義(モジュールレベルで宣言)
Private Declare PtrSafe Function BCryptOpenAlgorithmProvider Lib “bcrypt.dll” ( _
ByRef phAlgorithm As LongPtr, ByVal pszAlgId As LongPtr, ByVal pszImplementation As LongPtr, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function BCryptCreateHash Lib “bcrypt.dll” ( _
ByVal hAlgorithm As LongPtr, ByRef phHash As LongPtr, ByVal pbHashObject As LongPtr, _
ByVal cbHashObject As Long, ByVal pbSecret As LongPtr, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
‘ ※注:実運用ではさらにBCryptHashData、BCryptFinishHash等のAPI定義を揃える必要がある
‘ オブジェクトのライフサイクルを管理するため、APIハンドルは確実に閉じること
メモリ最適化とオブジェクト解放の鉄則
大規模プロジェクトファイルを取り扱う際、`Application.Projects`や`Project`オブジェクトを不用意に参照し続けると、VBAはメモリリークを起こす。
1. Set = Nothing の強制: ループ処理の最後には、必ず明示的にオブジェクトを破棄する。
2. イベントの抑止: `Application.EnableEvents = False` を徹底せよ。検証中のファイル再オープンが無限ループを招くリスクを排除する。
3. ストリームの直接操作を避ける: ファイルシステムを直接触る際、VBAの`Open`構文ではなく、`Scripting.FileSystemObject`を経由しつつ、最終的にはAPIでストリームをクローズすることが、OSレベルでのフラッシュを確実にする。
実践:整合性検証の自動化ロジック
以下は、保存処理をラップし、整合性を検証する高信頼性保存プロシージャの骨子である。
Public Sub SecureProjectSave(ByVal targetProject As Project)
Dim hashBefore As String, hashAfter As String
Dim filePath As String: filePath = targetProject.FullName
‘ 1. 保存前の状態をキャプチャ(ハッシュ計算)
hashBefore = CalculateFileHash(filePath)
‘ 2. 保存実行
On Error GoTo SaveError
targetProject.Save
‘ 3. 保存後の状態を比較
hashAfter = CalculateFileHash(filePath)
If hashBefore <> hashAfter Then
Err.Raise vbObjectError + 1001, “IntegrityManager”, “ファイル整合性チェックに失敗しました。破損の可能性があります。”
End If
Exit Sub
SaveError:
MsgBox “致命的エラー: ” & Err.Description, vbCritical
End Sub
レガシー環境への配慮とシステム間連携
この手法は、Project ServerやPWA(Project Web App)と連携するハイブリッド環境においても有効だ。ローカルキャッシュが汚染されている場合、サーバーへのチェックイン処理は「ゴミを正解として同期する」最悪の結果を生む。
- チェックサムのログ出力: 計算したハッシュ値をCSV等の別ログに吐き出し、SIEM(セキュリティ情報イベント管理)等で監視させることで、企業レベルでのインフラ保護が可能になる。
- バイナリバックアップ: 整合性エラーを検知した瞬間、Windows API `CopyFile` を使い、その瞬間の「破損バイナリ」を別名保存して解析用に隔離する。これが後にデバッグの唯一の手掛かりとなる。
—
アーキテクトからの提言
コードが「動くこと」と、システムが「永続的に信頼できること」は次元が異なる。
VBAはレガシーと言われるが、その背後にあるWindows APIは、現代のC#アプリケーションが依存しているものと本質的には同じだ。APIのレイヤーまで降り、メモリとバイナリを直接制御するこのアプローチこそが、複雑なプロジェクト管理を司るエンジニアの矜持である。
プロジェクトファイルは単なるデータではない。企業の戦略そのものだ。その整合性を守り抜くことは、エンジニアとして最も高潔な仕事の一つであると信じている。
