【テクニカル・上級編】ガントチャートの「制約インジケーター」をVBAで操作し、進捗報告を効率化 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:制約インジケーターのDOM操作と進捗自動生成エンジン

レガシーシステムとモダンなプロジェクト管理が交差する現場において、Microsoft ProjectのVBA(Visual Basic for Applications)は、依然として強力な自動化の基盤である。しかし、Project Object Modelの内部構造、特にスケジュールの根幹をなす「制約条件(Constraints)」と、それに連動する「制約インジケーター(Constraint Indicators)」のライフサイクルを正確に理解している開発者は少ない。

本稿では、Project VBAにおけるスケジューリングエンジンの挙動をハックし、制約インジケーターの動的判定から、ステークホルダー向けの進捗報告用カスタムビューを極限まで最適化して自動生成するアーキテクチャを解説する。

—

1. Projectスケジューリングエンジンと制約インジケーターの深層

Microsoft Projectのタスクには、ASAP(できる限り早く)、ALAP(できる限り遅く)、SNET(指定日以降に開始)など、様々な制約条件が設定される。ユーザーインターフェース上では、これらは「インジケーター」列のアイコンとして視覚化される。

しかし、VBAからこのインジケーターを直接操作・判定しようとした場合、表面的なプロパティ(例: `Task.ConstraintType`)だけを見ていると、プロジェクト全体の動的なスケジュール変更や、リソース平準化による遅延を正確に捉え損ねる。

パフォーマンスとメモリの罠

数千行に及ぶの大規模WBS(Work Breakdown Structure)に対し、安易に `For Each` ループを回して各タスクのプロパティにアクセスすると、COMの境界を越えるオーバーヘッドにより、実行時間が数分単位に膨れ上がる。
さらに、Projectのオブジェクトモデルは参照の解放漏れがメモリリーク(ひいてはApplicationの不安定化)に直結する。オブジェクト変数は必ず `Nothing` を代入して明示的に破棄しなければならない。

—

2. 実装:制約インジケーター判定とカスタムビュー自動生成エンジン

以下のコードは、全タスクの制約条件を高速にスキャンし、例外的な制約(ASAP/ALAP以外の、スケジュールを硬直化させる制約)を持つタスクをハイライトするカスタムビュー(フィルターおよびグループ)を動的に生成する、実務投入レベルのチーフアーキテクト特製モジュールである。

Option Explicit

‘ ==============================================================================
‘ módulo名: modConstraintEngine
‘ 概要: 制約インジケーターを解析し、進捗報告用カスタムビューを自動生成する
‘ アーキテクト: 伝説のチーフアーキテクト
‘ ==============================================================================

Public Sub GenerateConstraintReportView()
Dim prjApp As MSProject.Application
Set prjApp = ActiveProject.Application

‘ 画面描画と自動再計算を停止し、COM通信のオーバーヘッドを極限まで削減
prjApp.ScreenUpdating False
prjApp.Calculation = pjManual

On Error GoTo ErrorHandler

Dim tsk As MSProject.Task
Dim targetViewName As String
targetViewName = “【自動生成】要確認制約レポート”

‘ 1. 既存の同名カスタムビューが存在する場合は安全に削除(リソースのクリーンアップ)
Call RemoveExistingView(prjApp, targetViewName)

‘ 2. タスク群の制約条件をスキャンし、カスタムテキストフィールドにフラグを立てる
‘ ※インジケーターのアイコン描画を待たずに、論理的な制約タイプを高速判定
Dim constrainedCount As Long
constrainedCount = 0

For Each tsk In prjApp.ActiveProject.Tasks
If Not tsk Is Nothing Then
If Not tsk.Summary Then
‘ ASAP(0) および ALAP(1) 以外の「ハード制約」を検知
If tsk.ConstraintType <> pjConstraintAsSoonAsPossible And _
tsk.ConstraintType <> pjConstraintAsLateAsPossible Then

‘ Text1カスタムフィールドにメタデータを注入(進捗報告用フィルタのトリガー)
tsk.Text1 = “要確認制約: ” & GetConstraintTypeName(tsk.ConstraintType)
constrainedCount = constrainedCount + 1
Else
tsk.Text1 = “”
End If
End If
End If
‘ オブジェクトの局所的解放(ループ内でのメモリ肥大化を防ぐ)
Set tsk = Nothing
Next tsk

‘ 3. レポート用カスタムフィルターの作成
Call CreateCustomFilter(prjApp, targetViewName)

‘ 4. レポート用ビューの適用とレイアウト最適化
MsgBox “制約インジケーターの解析完了。” & vbCrLf & _
“該当タスク数: ” & constrainedCount & “件”, vbInformation, “制約エンジン”

ErrorHandler:
If Err.Number <> 0 Then
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “Critical Error”
End If

‘ 5. 環境の復元(例外発生時も確実に実行)
prjApp.Calculation = pjAutomatic
prjApp.ScreenUpdating True

‘ オブジェクトの明示的解放
Set tsk = Nothing
Set prjApp = Nothing
End Sub

‘ — ヘルパー関数: 制約タイプの文字列化 —
Private Function GetConstraintTypeName(ByVal cType As Long) As String
Select Case cType
Case pjConstraintAsSoonAsPossible: GetConstraintTypeName = “できる限り早く”
Case pjConstraintAsLateAsPossible: GetConstraintTypeName = “できる限り遅く”
Case pjConstraintMustStartOn: GetConstraintTypeName = “指定日開始”
Case pjConstraintMustFinishOn: GetConstraintTypeName = “指定日終了”
Case pjConstraintStartNoEarlierThan: GetConstraintTypeName = “指定日以降開始”
Case pjConstraintStartNoLaterThan: GetConstraintTypeName = “指定日以前開始”
Case pjConstraintFinishNoEarlierThan: GetConstraintTypeName = “指定日以降終了”
Case pjConstraintFinishNoLaterThan: GetConstraintTypeName = “指定日以前終了”
Case Else: GetConstraintTypeName = “不明な制約”
End Select
End Function

‘ — 安全なビュー削除処理 —
Private Sub RemoveExistingView(ByRef app As MSProject.Application, ByVal viewName As String)
Dim v As MSProject.View
On Error Resume Next
Set v = app.Views(viewName)
If Not v Is Nothing Then
v.Delete
End If
On Error GoTo 0
Set v = Nothing
End Sub

‘ — カスタムフィルターの動的生成 —
Private Sub CreateCustomFilter(ByRef app As MSProject.Application, ByVal filterName As String)
On Error Resume Next
app.CustomFilters.Add pjFilterTask, filterName, “Text1”, pjFieldText1, pjContains, “要確認制約”, , , True
On Error GoTo 0
End Sub

—

3. チーフアーキテクトの知見:システム間連携とレガシー保守の極意

上記のVBAスクリプトは単なるローカルマクロではない。これを基盤として、企業内のPMO(プロジェクトマネジメントオフィス)やERPシステム(SAPやDynamicsなど)との連携を構築する際の、実戦的なアーキテクチャ指針を記す。

① 外部システム連携時のトランザクション整合性

Projectファイル(`.mpp`)は、単一ファイルとしての排他制御が非常にシビアである。外部のバッチプロセスからこのVBAをヘッドレス(非表示)で起動する場合、`Application.FileOpenEx` の引数における読み取り専用フラグや、マルチユーザー環境での競合(Master/Subproject構造の崩壊)に細心の注意を払わなければならない。

② Windows APIとの連携によるパフォーマンス計測

極限のパフォーマンスが求められる現場では、`QueryPerformanceCounter` などのWin32 APIをVBAから呼び出し、ミリ秒単位でスケジュールの再計算コストをプロファイリングする手法が有効である。無駄な `Calculate` メソッドの呼び出しを排除し、プロジェクトの「ダーティフラグ(変更フラグ)」を制御することが、数万タスクを扱う巨大プロジェクトをフリーズさせない唯一の道である。

③ 保守性の担保

VBAのコードは往々にして「属人化」しやすい。マジックナンバーを排除し、上記のように定数(`pjConstraintMustStartOn`等)を適切に使用すること、そしてエラーハンドリング内で必ず `ScreenUpdating` と `Calculation` を元の状態に復元する防御的プログラミングを徹底せよ。例外時に画面が描画されないままフリーズしたように見える現象は、エンドユーザーに無用な恐怖を与える。

—

結び

Project VBAは過去の遺物ではない。APIのライフサイクルとメモリモデルの本質を理解したシニアエンジニアの手にかかれば、プロジェクト管理の自動化を極限まで加速させる最強の武器となる。
「動けばいい」という甘えを捨て、オブジェクトの解放から画面描画の制御まで、すべてのリソースを完璧に支配下に置け。それこそが、プロフェッショナルな業務自動化エンジニアの流儀である。

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