【テクニカル・上級編】【中級者向け】WBSの階層構造を再帰関数で走査し、親タスクの進捗率を子タスクから自動計算する – Project VBA解析バイブル

スポンサーリンク

Project VBAの深淵:再帰的WBS進捗集計のアーキテクチャ

多くのエンジニアがProject VBAの「進捗率(PercentComplete)」を自動計算しようとして泥沼にハマる。理由は単純だ。Projectのタスク階層を「単なるリスト」として扱い、ループで処理しようとするからだ。

真のアーキテクトであれば、WBSを「ノードとエッジからなる再帰的グラフ構造」として捉える。今日は、再帰関数を用いて親タスクの進捗を子タスクの重み付け平均で算出し、それをメモリ効率を最大化した設計で実装する極意を伝授する。

1. なぜ「再帰」でなければならないのか

Projectの`OutlineLevel`を頼りにループを回す手法は、階層が深くなった瞬間に破綻する。再帰アルゴリズムを採用することで、スタックメモリを最小限に消費しながら、無限の階層深さに対して線形的な計算時間を保証できる。

ここでのポイントは、「ボトムアップの集計」だ。末端のタスクから値を吸い上げ、親の進捗を更新する。この際、計算コストを抑えるために、サマリータスクの「手動計算モード」を意識した実装が必要になる。

2. 実装の核心:再帰的進捗計算エンジン

以下のコードは、単なるプロシージャではない。Projectオブジェクトモデルのトラバーサル(走査)において、最もオーバーヘッドが少ない手法の一つだ。

Option Explicit

‘ メモリ最適化のための定数定義
Private Const STACK_OVERFLOW_LIMIT As Long = 100

”’

”’ 再帰的にWBSを走査し、サマリータスクの進捗率を計算する
”’

Public Sub UpdateProgressRecursive(ByVal parentTask As Task)
Dim subTask As Task
Dim totalWork As Double, completedWork As Double
Dim childCount As Long

‘ 子タスクが存在しない場合は終了
If parentTask.OutlineChildren.Count = 0 Then Exit Sub

totalWork = 0
completedWork = 0

For Each subTask In parentTask.OutlineChildren
‘ 子タスクがサマリーの場合は再帰呼び出し
If subTask.OutlineChildren.Count > 0 Then
UpdateProgressRecursive subTask
End If

‘ 重み付け計算(ここでは単純工数ベースの集計)
‘ 実務では Duration ではなく Work を基準に計算することを強く推奨する
totalWork = totalWork + subTask.Work
completedWork = completedWork + (subTask.Work (subTask.PercentComplete / 100))

‘ オブジェクトの明示的解放(VBAのガベージコレクションを待たない)
Set subTask = Nothing
Next subTask

‘ 親タスクの進捗を更新
If totalWork > 0 Then
parentTask.PercentComplete = (completedWork / totalWork) 100
End If
End Sub

3. チーフアーキテクトの視点:パフォーマンスの極限

このコードを実戦投入する際、留意すべき「知見」が3点ある。

  • オブジェクトの解放: VBAは参照カウント方式のメモリ管理を行う。ループ内で`Task`オブジェクトを多用する場合、`Set subTask = Nothing`は必須だ。これを行わないと、巨大なプロジェクトファイルではメモリリークが蓄積し、COM呼び出しのパフォーマンスが劇的に低下する。
  • イベントの抑制: `Application.EnableEvents = False` を忘れてはならない。再帰の中で`PercentComplete`を書き換えるたびにProjectの計算エンジンが走り、再計算がループする「再帰的更新の罠」に陥る。
  • Windows APIの活用: もし大規模なWBSで計算が数秒以上かかるなら、`QueryPerformanceCounter`を用いてプロファイリングを行うべきだ。どの階層でボトルネックが発生しているか、ミリ秒単位で計測し、必要であれば計算対象をフラグ(カスタムフィールド)で制御する工夫を入れよ。

4. レガシー環境での保守戦略

このロジックは、Projectのバージョン間(2016/2019/2021/365)でオブジェクトモデルの挙動が微妙に異なるケースがある。特に、`PercentComplete`と`PhysicalPercentComplete`の混同は致命的だ。

  • 鉄則: 常にどのフィールドを「正」とするか、カスタムフィールドに計算ロジックを分離せよ。
  • 防御的実装: `Err.Number`を監視し、タスクが「マイルストーン」や「制約あり」の場合に計算をスキップする条件分岐を必ず入れよ。

最後に

コードは「動く」ことがゴールではない。「後世のエンジニアが迷わずに保守できること」こそが、最高峰の自動化エンジニアに求められる責務だ。

この再帰ロジックを理解した今、君はもはや単なるVBAユーザーではない。Projectの内部構造を掌握するアーキテクトとしての第一歩を踏み出したと言える。次は、このロジックを非同期的に実行するためのQueue処理や、外部DBとのAPI連携へと視野を広げてほしい。

現場からは以上だ。健闘を祈る。

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