Project VBAの深淵:ASAP/ALAP制約と期限(Deadline)の動的制御による遅延リスクの極致
プロジェクト管理において、VBAを用いてMS Projectのスケジュールエンジンを操ることは、一種の「調律」である。ASAP(可能な限り早く)とALAP(可能な限り遅く)、そして無慈悲なDeadline(期限)。これらが衝突した際、Projectの計算エンジンは時として予測不能な挙動を示す。
本稿では、レガシーなVBA環境下でこの「制約の衝突」をプログラム的に制御し、遅延リスクを動的に可視化するためのアーキテクチャを提示する。
—
1. MS Projectの制約エンジンの本質を理解する
MS Projectのスケジュールエンジンは、単純なクリティカルパス法以上の「優先順位アルゴリズム」に基づいている。
- ASAP制約:先行タスク完了後に即時開始。
- Deadline:計算上は制約ではないが、遅延フラグを立てるための「影のバリア」。
シニアエンジニアが陥る罠は、`ConstraintDate`と`Deadline`を混同し、オブジェクトのプロパティを安易にループ内で書き換えることだ。これにより、メモリの解放が追いつかず、Projectのオブジェクトモデルが肥大化し、計算コストが爆発する。
—
2. 動的制御のための最適化戦略
大量のタスクを操作する場合、`Application.ScreenUpdating`の停止だけでは不十分だ。以下の原則を遵守せよ。
1. オブジェクトのキャッシュ: `Task`オブジェクトをループ内で直接参照せず、必要なプロパティを配列に格納してから処理する。
2. イベントのバイパス: 計算エンジンを一時的に「手動」へ切り替え、全処理終了後に再計算を走らせる(`Application.Calculation = pjManual`)。
3. APIによるメモリ解放: `Set obj = Nothing`だけでは不十分なケースがある。特に大規模プロジェクトでは、GC(ガベージコレクション)を意識したコンテキスト管理が必須だ。
—
3. 実装:制約優先順位制御アルゴリズム
以下のコードは、Deadlineを考慮しつつ、ASAP制約を動的に再評価するアーキテクチャの核心部である。
‘ プロジェクト制約制御エンジン
Sub UpdateTaskConstraints(targetProject As Project)
Dim tsk As Task
‘ 計算負荷を最小化するため、計算モードを手動に設定
Application.Calculation = pjManual
On Error GoTo Cleanup
For Each tsk In targetProject.Tasks
If Not tsk Is Nothing Then
‘ 期限が設定されているか確認
If tsk.Deadline <> “NA” Then
‘ 現在の開始日が期限を超過しているか判定
If tsk.Start > tsk.Deadline Then
‘ 動的に制約を「開始日指定(Start No Earlier Than)」に変更し
‘ リスクフラグを立てるロジック
tsk.ConstraintType = pjSNET
tsk.ConstraintDate = tsk.Deadline
tsk.Text1 = “CRITICAL: Deadline Exceeded”
Else
‘ 通常時はASAPにリセット
tsk.ConstraintType = pjASAP
tsk.Text1 = “”
End If
End If
End If
Next tsk
Cleanup:
‘ 再計算を実行し、エンジンを自動モードに戻す
Application.Calculation = pjAutomatic
‘ オブジェクトの明示的解放(VBAにおいてはNothing代入が必須の作法)
Set tsk = Nothing
End Sub
—
4. レガシー環境における安定稼働の知見
このアルゴリズムを実運用に乗せる際、注意すべき点が二つある。
Windows APIを用いた「応答なし」回避
大規模なスケジュール計算中、Windowsは「応答なし」と判断しがちだ。これを防ぐには、`kernel32`の`Sleep`関数を用い、計算の合間にプロセッサの権限をOSに委譲する。
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As LongPtr)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
‘ ループ内での使用例
‘ DoEventsを乱用するより、SleepでCPU負荷を下げる方が安全
Sleep 10
メモリリークとの闘い
MS ProjectのCOMオブジェクトは、ループ内で頻繁に生成・破棄を行うとメモリリークを起こす。
「オブジェクトの再利用」こそが、伝説級のエンジニアの流儀である。`For Each`の前に変数を定義し、ループ内で一度も`Set`を再実行せず、プロパティの読み書きのみを行うこと。これが、長期間稼働するシステムを支える唯一の道だ。
—
結びに代えて
プロジェクトの遅延は、システムの問題ではなく、エンジニアが「制約の優先順位」をコードに正しく落とし込めていないことに起因する。ASAPとDeadlineの狭間で揺れるプロジェクトを、VBAの泥臭い記述で制御する。これこそが、我々アーキテクトが担うべき、最もエレガントな職人芸である。
コードが書ける者は多い。しかし、Projectの内部エンジンを制御し、計算リソースを最適化できる者は極めて少ない。君がその一人であることを期待する。
