【Project VBA】タスクの「ID」と「UniqueID」を混同するな。堅牢なリンク設定のアーキテクチャ
Project VBA(MS Project)の自動化において、多くの開発者が最初の数週間で突き当たる「呪い」がある。それは、「タスクの並び替えを行うと、リンクが別タスクに繋がってしまう」というバグだ。
これは、MS Projectのオブジェクトモデルを理解していない者が陥る典型的な罠である。今回は、現場で「動く」レベルではなく、「壊れない」レベルのタスク管理ロジックを伝授する。
—
1. なぜ「ID」でリンクを張ってはいけないのか
MS Projectには二つのIDが存在する。
- ID (Task.ID): 行番号。並び替えや挿入で常に変動する。
- UniqueID (Task.UniqueID): タスク生成時に割り振られる不変の識別子。
初心者は `Task.ID` を使って `Task.TaskDependencies.Add` を行おうとする。だが、考えてみてほしい。昨日まで3行目だったタスクが、今日追加したタスクによって4行目にズレたらどうなる? リンクはそのまま「4行目」を参照し続け、依存関係は崩壊する。
結論:VBAで外部データと同期を取る場合、一切の例外なく `UniqueID` をキーとして設計せよ。
—
2. 堅牢なリンク設定の設計パターン
データ構造として、「タスク名」や「ID」を主軸にするのは愚策だ。外部データベース(ExcelやSQL Server)と連携する際は、以下のステップを厳守せよ。
1. UniqueKeyの保持: 外部システム側のタスクIDを、MS Projectの `Text1`~`Text30` などのカスタムフィールドに格納しておく。
2. マッピングテーブルの構築: 実行時に `UniqueID` と「外部ID」の辞書(Scripting.Dictionary)をメモリ上に作成する。
3. リンクの構築: 外部IDをキーにして、対応する `UniqueID` を引き出し、`Task.TaskDependencies` を操作する。
—
3. 実装:バグを排除するリンク構築プロシージャ
以下は、外部の依存関係リスト(先行タスクID, 後続タスクID)を基に、安全にリンクを生成するコードだ。
‘ 依存関係を構築する堅牢なプロシージャ
‘ 事前条件: 外部IDがカスタムフィールド(Text1)に格納されていること
Sub EstablishDependencies(ByRef proj As Project, ByVal predecessorExtID As String, ByVal successorExtID As String)
Dim tPred As Task, tSucc As Task
Dim foundPred As Boolean, foundSucc As Boolean
‘ 1. カスタムフィールドから目的のタスクを特定
‘ ※毎回ループするのは非効率。実運用ではDictionaryでキャッシュすること
For Each tPred In proj.Tasks
If tPred.Text1 = predecessorExtID Then foundPred = True: Exit For
Next tPred
For Each tSucc In proj.Tasks
If tSucc.Text1 = successorExtID Then foundSucc = True: Exit For
Next tSucc
‘ 2. 整合性チェック(ここが甘いとツールはゴミになる)
If foundPred And foundSucc Then
‘ リンク生成: UniqueIDを使用すること!
‘ Addメソッドは引数にUniqueIDを期待する
On Error Resume Next ‘ 既にリンクがある場合の重複エラー回避
tSucc.TaskDependencies.Add Task:=tPred, Type:=pjFinishToStart
On Error GoTo 0
Else
Debug.Print “Warning: リンク設定失敗 – ” & predecessorExtID & ” -> ” & successorExtID
End If
End Sub
—
4. アーキテクトからの忠告:パフォーマンスと保守性
このコードをそのまま大規模プロジェクトに適用してはいけない。以下の「極限の知見」を付け加えてこそ、プロの仕事だ。
- Dictionaryオブジェクトの活用:
タスク数千件に対して、毎回 `For Each` を回すのは自殺行為だ。最初に全タスクを走査し、`Scripting.Dictionary` に `ExternalID -> UniqueID` のマップをロードせよ。計算量は O(N) から O(1) に激減する。
- 計算モードの制御:
リンクを大量に追加する場合、`Application.Calculation = pjManual` に設定し、すべての処理が終わった後に `CalculateAll` を呼べ。GUIの描画と再計算を抑制するだけで、処理速度は10倍変わる。
- 論理削除への対応:
タスクを「削除」する場合、単に隠すのではなく、ステータスを「完了」にするか、フラグで制御せよ。MS Project上でタスクを物理削除すると `UniqueID` の整合性が保てなくなるリスクがある。
最後に
ツールを作る目的は「楽をすること」ではない。「誰もが同じ手順で、エラーなくプロジェクトを管理できるようにすること」だ。
IDの変動に怯えながら手動修正を繰り返す時代は終わらせよう。`UniqueID` を中心としたデータ構造を設計すれば、君のツールは堅牢なインフラへと進化するはずだ。
次は、プロジェクトの「ベースライン」と「実績」を分離して管理するアーキテクチャについて語ろうか。健闘を祈る。
