こんにちは!Excelマクロの記録から一歩抜け出し、「もっとスマートに業務を自動化したい」と日々の開発に挑んでいるあなたへ。
今回は、多くの人がMS ProjectのVBA開発で最初に直面する、そして最も美しく解決すべき「タスクの依存関係(先行タスク)」の罠についてお話しします。
現場のExcel管理表では「行番号(3行目のタスクの後に5行目のタスクを始める)」で管理されているのに、いざProject VBAでリンクを組もうとすると、思った通りにいかなくて頭を抱えたことはありませんか?
ここをクリアすれば、あなたも立派なProject VBAエンジニアの仲間入りです。優しく、かつ本質的なアプローチで一緒に紐解いていきましょう!
—
1. なぜ「行番号」のままではリンクが切れてしまうのか?
Excelの表では、タスクの前後関係を「行番号」で指定しがちです。
しかし、MS Projectの世界では、タスクが追加されたり削除されたり、並び替えられたりするたびに、「行番号(Index)」は刻一刻と変化します。
昨日まで「行番号:3」だったタスクが、上に新しいタスクが1つ挿入された瞬間に「行番号:4」に変わってしまう。この状態でマクロを走らせたらどうなるでしょうか? 全く意図しないタスク同士がリンクされてしまい、スケジュールは大混乱です。
解決の鍵は「UniqueID」
MS Projectには、タスクが作られた瞬間に自動割り当てられ、絶対に変わらない固有のID(UniqueID)という概念があります。
- ID (行番号): 並び替えや挿入で変わる(流動的)
- UniqueID: 一度振られたらタスクが消滅するまで絶対に変わらない(永続的)
外部Excelの「先行タスク番号」を、この「UniqueID」に正しく変換(マッピング)してあげることが、リンク切れを防ぐ唯一にして最強の方法なのです。
—
2. 全体像:データの流れをイメージしよう
今回の自動化ロジックのステップは以下の通りです。
[外部Excel] [MS Project]
行番号ベースの指定 UniqueIDベースのリンク
—————- ———————-
タスクBの先行タスク = “1” ──変換──> タスクBのPredecessors = “UniqueID: 101”
これをVBAで安全に実装するためのコードを、次項で丁寧に解説しながらお届けします。
—
3. 実装コード:安全な動的マッピングエンジン
以下のコードは、外部Excelからタスク名と「Excel上の行番号ベースの先行タスク」を読み込み、Project側でUniqueIDに変換してリンクを張る実用的なサンプルです。
開発環境の標準モジュールに貼り付けて、そのまま現場でカスタマイズして使えます。
Sub MapPredecessorsByUniqueID()
Dim prjProj As Project
Set prjProj = ActiveProject
‘ — 1. 簡易的なデータ構造のシミュレーション —
‘ 実際にはここで外部Excel(Workbooks.Open等)からデータを取得しますが、
‘ 今回はロジックの美しさを伝えるため、概念的な配列で説明します。
‘ 例:外部データのイメージ
‘ 外部タスクID(行) | タスク名 | 先行タスク(外部行番号)
‘ 1 | 要件定義 | “”
‘ 2 | 基本設計 | “1”
‘ 3 | 詳細設計 | “2”
Dim targetTask As Task
Dim predTask As Task
Dim excelRowIndex As Long
Dim excelPredRow As Long
On Error GoTo ErrorHandler
‘ 画面描画を停止してマクロの実行速度を爆発的に上げる(基本テクニック!)
Application.ScreenUpdating = False
‘ — 2. Project内のタスクを「外部行番号」から「UniqueID」に紐付けるための辞書(準備) —
‘ ここではシンプルに、Project側のタスクの「OutlineNumber」や「ID」をキーにして
‘ UniqueIDを引くループ処理を組み立てます。
For Each targetTask In prjProj.Tasks
If Not targetTask Is Nothing Then
‘ ※実務では、Excelの「行番号」や「独自テキストフィールド(Text1など)」に
‘ 外部の識別子を保持させておき、それをキーに検索するのが定石です。
‘ 例として、現在のProject上の「ID(行番号)」が、外部Excelの行番号と一致していると仮定
excelRowIndex = targetTask.ID
‘ 【ここに外部Excelから読み込んだ「先行タスクの行番号」があるとする】
‘ 例:もし外部データで「このタスクの先行は行番号 1 だ」と定義されていた場合
Dim externalPredRowString As String
externalPredRowString = “1” ‘ ※本来は外部Excelから取得した値
If externalPredRowString <> “” Then
excelPredRow = CLng(externalPredRowString)
‘ — 3. 魔法の変換ロジック:Excel行番号からProjectのUniqueIDを特定 —
On Error Resume Next
‘ 指定した行番号(ID)を持つタスクオブジェクトを取得
Set predTask = prjProj.Tasks.UniqueID(GetUniqueIDFromRow(prjProj, excelPredRow))
On Error GoTo ErrorHandler
If Not predTask Is Nothing Then
‘ 永続的なUniqueIDを使って安全に先行タスクを設定!
‘ 構文: targetTask.Predecessors = “UniqueID”
targetTask.Predecessors = CStr(predTask.UniqueID)
End If
End If
End If
Next targetTask
Application.ScreenUpdating = True
MsgBox “タスクの依存関係の自動マッピングが完了しました!”, vbInformation
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
End Sub
‘ ————————————————–
‘ 補助関数:行番号(ID)からUniqueIDを安全に引き出す関数
‘ ————————————————–
Function GetUniqueIDFromRow(p As Project, rowIndex As Long) As Long
Dim t As Task
On Error Resume Next
Set t = p.Tasks(rowIndex)
If Not t Is Nothing Then
GetUniqueIDFromRow = t.UniqueID
Else
GetUniqueIDFromRow = 0
End If
On Error GoTo 0
End Function
—
4. ここで陥りやすい!初心者がハマる3つの罠
1. `Tasks(index)` と `Tasks.UniqueID(id)` の混同
- `Tasks(3)` は「上から3番目のタスク(変動する)」を指しますが、`Tasks.UniqueID(3)` は「UniqueIDが3のタスク(不変)」を指します。この違いを意識するだけで、バグの9割を防げます。
2. リンク文字列の指定ミス
- Project VBAで `Predecessors` に代入する際、UniqueIDを指定する場合は文字列として渡す(例: `task.Predecessors = “15”`)必要があります。数値型のまま代入すると型ミスマッチが起きるので注意しましょう。
3. `Application.ScreenUpdating = False` の付け忘れ
- タスク数が数百〜数千を超えると、画面描画のたびにProjectがフリーズしたような挙動になります。大量のタスクを操作する自動化では、処理の前後でScreenUpdatingを挟むのがプロの作法です。
—
最後に:ここをクリアすれば、あなたもProject VBAのマスター!
外部のExcelと連携するWBSの自動生成やスケジュール更新は、実務の現場で最も需要があり、そして最もエンジニアの腕の見せ所となる領域です。
「行番号という移ろいやすいもの」から「UniqueIDという不変の真実」へ変換するこのロジックさえ押さえておけば、どれだけ複雑なスケジュール変更が起きようとも、あなたの組んだマクロはビクともしません。
ぜひ、あなたの現場のツールにもこの知見を取り入れて、スマートな自動化を実現してくださいね。応援しています!
