Project VBAの深淵:Excelの「先行タスク」をUniqueIDで確実にリンクさせる極限の設計術
現場でWBSを管理する際、Excelの行番号をそのまま先行タスクとして参照するようなコードを書いていないか?もしそうなら、今すぐその実装を捨てるべきだ。
Projectにおける「行番号(ID)」は、タスクの並び替えや挿入によって容易に変動する不安定な値だ。これに依存した依存関係の構築は、プロジェクトが進むにつれて必ず破綻する。真のプロフェッショナルは、「不変の識別子(UniqueID)」を軸にマッピングを行う。
今回は、Excel上の管理データからProjectの`UniqueID`を動的に特定し、堅牢にリンクを張るためのアーキテクチャを伝授する。
—
1. なぜ「ID」ではなく「UniqueID」なのか
Projectオブジェクトモデルにおいて、`ID`は表示順序に過ぎない。対して`UniqueID`は、そのタスクが作成された瞬間に割り当てられる(削除されるまで)一意の番号だ。
実務で「先行タスク:10番」と書かれたExcelデータがあっても、Project上の10番が本当に意図したタスクである保証はどこにもない。この不一致こそが、スケジュールの破損を招く。我々は「検索キーとしてのUniqueID」を介在させることで、この不確実性を排除する。
—
2. 堅牢なマッピングロジックの設計思想
以下の手順で実装することで、保守性が高く、かつ誤作動のない自動化が可能となる。
1. Mapオブジェクトの生成: Project内の全タスクを走査し、`[任意のキー(例:タスク名や管理コード)] : [UniqueID]` の対応表をメモリ上(`Scripting.Dictionary`)に保持する。
2. 先行タスクの解決: Excel側の先行タスク列を読み取り、上記Dictionaryから対応する`UniqueID`を逆引きする。
3. リンクの構築: `Task.TaskDependencies.Add`メソッドを利用し、`UniqueID`を直接指定してリンクを生成する。
—
3. 実践コード:プロダクションレベルの実装
このコードは、エラーハンドリングを重視しつつ、Dictionaryを活用して高速にリンクを構築する設計だ。
Option Explicit
‘ 参照設定: Microsoft Project 16.0 Object Library
‘ 参照設定: Microsoft Scripting Runtime
Public Sub LinkTasksByUniqueID()
Dim proj As MSProject.Project
Dim tsk As MSProject.Task
Dim dict As Object
Set dict = CreateObject(“Scripting.Dictionary”)
Set proj = ActiveProject
‘ 1. プロジェクト内のタスクをUniqueIDベースで辞書化
‘ ここではタスク名をキーにするが、必要に応じて「管理コード」カスタムフィールドに変更せよ
For Each tsk In proj.Tasks
If Not tsk Is Nothing Then
If Not dict.Exists(tsk.Name) Then
dict.Add tsk.Name, tsk.UniqueID
End If
End If
Next tsk
‘ 2. Excelから読み込んだ先行タスク情報をもとにリンク設定
‘ 例: ws.Range(“A2”)にタスク名、ws.Range(“B2”)に先行タスク名があると仮定
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(“WBS”)
Dim i As Long
For i = 2 To ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim targetName As String: targetName = ws.Cells(i, 1).Value
Dim predName As String: predName = ws.Cells(i, 2).Value
If dict.Exists(targetName) And dict.Exists(predName) Then
Dim tskTarget As MSProject.Task
Set tskTarget = proj.Tasks.UniqueID(dict(targetName))
‘ 先行タスクのUniqueIDを指定して依存関係を追加
‘ Addメソッド: PredecessorUniqueIDを指定することが重要
On Error Resume Next ‘ 既にリンクがある場合の重複エラー回避
tskTarget.TaskDependencies.Add dict(predName), pjFinishToStart
On Error GoTo 0
End If
Next i
MsgBox “リンク構築が完了しました。”, vbInformation
End Sub
—
4. 運用上の極意と注意点
A. 検索キーの完全一致を保証せよ
上記コードは`tsk.Name`をキーにしているが、Project上のタスク名にスペースが混入しているだけでマッチングは失敗する。実務では、Projectの「テキスト1」などのカスタムフィールドに「一意な管理コード」を入力し、それを検索キーにするのが最も堅牢だ。
B. パフォーマンスの罠
タスク数が数千を超える場合、`proj.Tasks`へのループアクセスは重くなる。しかし、一度`Dictionary`に格納してしまえば、メモリ上での検索になるため、Excel側のループ処理は瞬時に終わる。計算量を最小化するこの設計が、数万行のプロジェクトを扱う上での必須作法だ。
C. データベース連携の視点
もしExcelではなくSQL Server等からデータを吸い上げる場合、この`Dictionary`を構築するステップがそのまま「ETL(Extract/Transform/Load)」の役割を果たす。この設計にしておけば、データの提供元がExcelからDBに変わっても、中核のロジックを一切修正する必要がない。
—
最後に:ツールを作る者の心得
自動化ツールは「動けばいい」ものではない。「壊れた時に、なぜ壊れたかが即座に分かる」ことこそが重要だ。今回紹介した`UniqueID`ベースの設計は、プロジェクトのライフサイクルにおける「変化」を許容する。
君たちが作るツールが、単なる作業の代行ではなく、プロジェクト管理の信頼性を担保する基盤となることを期待している。さあ、実装に移れ。
