【実務・中級編】【中級者向け】タスクの「UniqueID」と「ID」の不一致を解消する:削除・挿入に強いリンク設定術 – Project VBA解析バイブル

スポンサーリンク

【中級者向け】タスクの「UniqueID`と「ID」の不一致を解消する:削除・挿入に強いリンク設定術

プロジェクト管理の現場でVBAを使い、タスクの依存関係(先行タスク・後続タスクのリンク)を自動化しようとして、絶望したことはないだろうか。

「昨日まで完璧に動いていたマクロが、ユーザーがタスクを1行挿入・削除した途端に別のタスクとリンクし、ガントチャートがぐちゃぐチャになった」

この悪夢の原因は、あなたが`Task.ID`をキーにしてリンクを構築してしまったことにある。
今回は、Project VBAの隠された罠を暴き、削除や挿入の嵐が吹いても微動だにしない「真に堅牢なリンク設定ロジック」を授けよう。

—

1. なぜ「ID」を使ってはいけないのか?(オブジェクトライフサイクルの真実)

MS ProjectのVBA初学者が最初に犯す致命的なミスが、`ActiveProject.Tasks(i)` や `Task.ID` をそのまま依存関係のキーとして使うことだ。

ID と UniqueID の決定的な違い

  • ID(揮発性の表示順序)
  • タスクが画面上で上から何番目にいるかを示す「インデックス番号」。
  • ユーザーがタスクを挿入・削除・並び替えすると、IDは動的に再割り当て(リナンバリング)される。
  • つまり、プログラムが実行された瞬間に意味が変わるため、永続的なキーとしては全く使い物にならない。
  • UniqueID(不変の識別子)
  • タスクが作成された瞬間に発行される「一意のシステムID(Read-only)」。
  • タスクを削除しようが、上に100行挿入されようが、そのタスクが生存している限りUniqueIDの値は絶対に変わらない。

業務自動化ツールにおいて、動的に変動する「ID」を依存関係のキーにするなど、地雷原を裸足で歩くようなものだ。リンクを張る際は、例外なく `UniqueID` を使用しなければならない。

—

2. 実務で即破綻する「NGコード」と「正解の設計思想」

まずは、よく見かける「やってはいけないコード」を確認しておこう。

❌ 破綻するアンチパターン

‘ 【絶対真似してはいけないコード】
Sub BadLinkExample()
Dim t1 As Task, t2 As Task
‘ IDでタスクを指定している
Set t1 = ActiveProject.Tasks(1) ‘ タスクID: 1
Set t2 = ActiveProject.Tasks(2) ‘ タスクID: 2

‘ リンク作成(先行:ID1、後続:ID2)
t2.TaskDependencies.Add From:=t1, Type:=pjLinkFinishToStart

‘ -> もしこのマクロの直前にユーザーが行を挿入していたら?
‘ -> 全く意図しないタスク同士がリンクされる。
End Sub

⭕ プロダクションコードの設計思想

堅牢なツールを作るためのアプローチはこうだ。
1. 外部ファイル(ExcelやDB)やマスタ定義からは、「ビジネス上の識別コード(WBSコードやタスク名など)」でタスクを特定する。
2. Project側から該当タスクの `UniqueID` を引く。
3. 取得した `UniqueID` を基に、`ActiveProject.Tasks.UniqueID(id)` を使って安全にオブジェクトを特定し、リンクメソッド(`TaskDependencies.Add`)を叩く。

—

3. 【実装例】削除・挿入に完全耐性を持つリンク設定プロシージャ

ここからが本題だ。外部から渡された「先行タスクのUniqueID」と「後続タスクのUniqueID」を安全に結びつける、実務レベルのモジュールを提供する。

エラーハンドリングを網羅し、存在しないUniqueIDを指定された場合でも沈黙せず、明確なログを残す設計にしている。

‘ ==============================================================================
‘ módulo: ModTaskLinker
‘ 概要: UniqueIDをベースにした堅牢なタスク依存関係設定エンジン
‘ ==============================================================================
Option Explicit

Public Sub ApplyRobustTaskLink(ByVal predecessorUniqueID As Long, _
ByVal successorUniqueID As Long, _
Optional ByVal linkType As PjTaskLinkType = pjLinkFinishToStart)

Dim tPred As Task
Dim tSucc As Task

On Error GoTo ErrorHandler

‘ 1. UniqueIDから先行タスクオブジェクトを取得
‘ ※ 通常の Tasks(id) ではなく Tasks.UniqueID(id) を使うのが鉄則
Set tPred = GetTaskByUniqueID(predecessorUniqueID)
If tPred Is Nothing Then
Err.Raise vbObjectError + 1000, “ApplyRobustTaskLink”, _
“先行タスク (UniqueID: ” & predecessorUniqueID & “) がプロジェクト内に見つかりません。”
End If

‘ 2. UniqueIDから後続タスクオブジェクトを取得
Set tSucc = GetTaskByUniqueID(successorUniqueID)
If tSucc Is Nothing Then
Err.Raise vbObjectError + 1000, “ApplyRobustTaskLink”, _
“後続タスク (UniqueID: ” & successorUniqueID & “) がプロジェクト内に見つかりません。”
End If

‘ 3. 循環参照や重複リンクのエラーを防ぎつつ依存関係を追加
‘ すでに同一のリンクが存在するかチェックするロジックを入れるとなお堅牢
If Not HasDependency(tSucc, tPred) Then
tSucc.TaskDependencies.Add From:=tPred, Type:=linkType
Debug.Print “リンク設定成功: [先行 UID:” & predecessorUniqueID & “] -> [後続 UID:” & successorUniqueID & “]”
Else
Debug.Print “スキップ: 既にリンクが存在します ([先行 UID:” & predecessorUniqueID & “] -> [後続 UID:” & successorUniqueID & “])”
End If

Exit Sub

ErrorHandler:
MsgBox “タスクリンクの設定中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “VBAリンク自動化エンジン”
End Sub

‘ ——————————————————————————
‘ ヘルパー関数: UniqueIDからタスクを安全に取得する
‘ ——————————————————————————
Private Function GetTaskByUniqueID(ByVal uid As Long) As Task
Dim t As Task
On Error Resume Next
‘ Tasks.UniqueID メソッドは存在しないUIDを指定すると実行時エラーになるため
‘ On Error と組み合わせるか、ループで走査する
Set GetTaskByUniqueID = ActiveProject.Tasks.UniqueID(uid)
On Error GoTo 0
End Function

‘ ——————————————————————————
‘ ヘルパー関数: 既に指定した先行タスクとの依存関係があるかチェック
‘ ——————————————————————————
Private Function HasDependency(ByVal targetTask As Task, ByVal predTask As Task) As Boolean
Dim dep As TaskDependency
For Each dep In targetTask.TaskDependencies
If dep.From.UniqueID = predTask.UniqueID Then
HasDependency = True
Exit Function
End If
Next dep
HasDependency = False
End Function

—

4. データベース・Excel連携時の注意点

この設計をさらに外部連携(ExcelやSQL Serverなど)へとスケールさせる場合、以下のアーキテクチャ上の注意が必要となる。

1. 初回インポート時のマッピング保持

ExcelからWBSを新規インポートする際、Excel側にはまだMS Projectの `UniqueID` は存在しない(タスクが作成されていないため)。
この場合の正しいフローは以下の通りだ。
1. Excelの行を上から順にタスクとして追加(Add)。
2. 追加した直後に `NewTask.UniqueID` を取得し、Excel側のデータテーブル(またはScripting.Dictionaryなど)に「WBSコード/タスク名 = UniqueID」の対応表として書き戻す。
3. 全タスクの生成が完了した後に、依存関係シートを読み込み、保存した `UniqueID` を使って先ほどの `ApplyRobustTaskLink` を呼び出す。

2. 再インポート(差分更新)時のID枯渇問題

タスクを一度全削除して再作成するような安易なバッチを作ってはならない。タスクを削除・再作成すると `UniqueID` は新しい採番に変わり、アサインされているリソース(ResourceAssignment)や実績値の紐付けがすべて吹き飛ぶ。
必ず 「既存タスクは UniqueID で検索してプロパティをUPDATEし、新規のみADDする」 という差分更新(Upsert)の思想をコードに組み込むこと。

—

5. チーフアーキテクトからの総括

Project VBAの開発において、目先の「動いた動いた」でコードを書くプログラマは三流だ。
「ユーザーがどう行を挿入しようとも、絶対に意図したスケジュール計算が壊れない」というデータ構造の不変性(Invariance)を担保して初めて、プロのエンジニアと名乗ることができる。

今回解説した `UniqueID` を軸にしたアプローチは、あらゆるProject自動化の基盤となる。ぜひ君のプロジェクトにもこの堅牢性を導入し、保守地獄から解放された美しい自動化ライフを手に入れてほしい。

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