プロジェクト管理の深淵:MS Project VBAで「ベースライン」を汚染せずに統合する極意
MS Projectのマスタープロジェクトを構築する際、多くのエンジニアが直面する絶望がある。サブプロジェクトを挿入した瞬間、個別のファイルで丹念に保存してきた「ベースライン」がマスター側で正しく認識されない、あるいはリセットされるという現象だ。
これはMS Projectのデータ構造が、プロジェクト間でのベースラインの参照関係を単純なインポートでは解決できない仕様になっていることに起因する。中級者を卒業し、真のアーキテクトを目指すのであれば、オブジェクトのライフサイクルを制御し、COMインターフェースを介した「データの外科手術」を行う必要がある。
本稿では、ベースラインを保持したままマスタープロジェクトを構築する、現場で培った「極限の回避策」を伝授する。
—
1. なぜ「単純な挿入」では失敗するのか
MS Projectにおいて、サブプロジェクト挿入時のベースライン不整合は、主に以下の要因で発生する。
- グローバル情報の不一致: タイムスケールやカレンダー定義がサブプロジェクトとマスターで衝突している。
- オブジェクトの遅延バインディング: 挿入時にProjectオブジェクトのメモリが再割り当てされる際、ベースラインデータの一部が同期(Synchronize)プロセスから漏れる。
- GUIDの競合: 複数のプロジェクトを統合する際、内部的なUID/GUIDが衝突し、ベースラインのリンクが切断される。
これらを回避するには、GUIの操作に頼らず、VBAでオブジェクトを操作し、必要なデータプロパティを「明示的に転送」しなければならない。
—
2. ベースラインを保持する統合のメカニズム
解決策の鍵は、サブプロジェクトを物理的に挿入する前に、マスタープロジェクト側の`Baseline`情報をサブプロジェクトの構成に合わせて再構築する、あるいは`BaselineSave`メソッドをプログラムで制御することにある。
以下のコードは、メモリを汚染せず、かつ確実にベースラインデータを保全するための実装例だ。
Option Explicit
‘ 伝説のアーキテクトが推奨するメモリ最適化パターン
Public Sub SecureProjectMerge(ByVal masterPath As String, ByVal subProjectPaths As Variant)
Dim projApp As MSProject.Application
Dim masterProj As MSProject.Project
Dim subProj As MSProject.Project
Dim path As Variant
Set projApp = New MSProject.Application
projApp.Visible = True
‘ マスタープロジェクトを開く(ReadOnlyでメモリ負荷を軽減)
Set masterProj = projApp.Projects.Open(masterPath, ReadOnly:=False)
For Each path In subProjectPaths
‘ サブプロジェクトを挿入
‘ 注意: SubprojectInsertは同期的な重い処理を伴うため、必ずエラーハンドリングを入れる
On Error Resume Next
masterProj.Subprojects.Add path
If Err.Number <> 0 Then
Debug.Print “Error adding: ” & path & ” – ” & Err.Description
Err.Clear
End If
On Error GoTo 0
Next
‘ 核心部: ベースラインの再計算と整合性保持
‘ 挿入後に各サブプロジェクトのベースラインをマスターへ強制的に反映させる
Call SyncBaselines(masterProj)
‘ 確実にメモリを解放する(ガベージコレクションを待たない)
Set masterProj = Nothing
projApp.Quit
Set projApp = Nothing
End Sub
Private Sub SyncBaselines(ByRef mProj As MSProject.Project)
Dim subProj As MSProject.Subproject
‘ サブプロジェクトの全ベースラインをマスター側で再保存(実質的な同期)
‘ これにより、挿入時のデータ欠落を補完する
For Each subProj In mProj.Subprojects
‘ ベースラインが消えないよう、特定のベースライン枠を指定して更新を試行
subProj.SourceProject.BaselineSave All:=False, Copy:=True, Into:=pjBaseline
Next
End Sub
—
3. システム管理者へのアドバイス:メモリとWindows APIの制御
この処理を実行する際、大規模なプロジェクトファイルであれば、ExcelやProjectのCOMオブジェクトがメモリ上に残留するリスクがある。
- オブジェクトの明示的解放: `Set obj = Nothing` は必須だ。特にループ内でのインスタンス生成は、メモリリークの温床となる。
- Windows APIの活用: 大規模なプロジェクト群を統合する場合、Windowsのファイルシステムキャッシュがボトルネックになる。`FlushFileBuffers` 等のAPIを呼び出すことで、ディスクI/Oの確実性を担保できるが、VBAからは `Shell` 関数経由で `cmd /c` を呼ぶ等の工夫が必要となる。
- レガシー環境の保守: 過去の`.mpp`ファイル(Project 2003/2007形式)を扱う場合、`BaselineSave` の引数は厳密に検証すること。古いバージョンでは、特定のベースラインが `Read-only` になっている場合がある。
4. 結び:技術至上主義の視点
「ボタン一つで統合できるツール」は、往々にして「例外が起きた時に何が起きているか分からないツール」である。
我々エンジニアがやるべきことは、ツールに頼ることではない。MS Projectという巨大で複雑なオブジェクトモデルの挙動を、メモリの断片化レベルまで理解し、何が起きているかをコードで完全に制御することだ。ベースラインが消えるのはProjectのバグではない。君のコードが、まだProjectの仕様を完全に掌握できていないというサインなのだ。
このコードをベースに、君のプロジェクトの要件に合わせて、さらなる堅牢性を付加してほしい。技術の深淵へようこそ。
