【Project VBAを極める】命名規則からWBS階層構造を自動構築する「インデントパーサー」の実装
プロジェクトマネージャーや業務効率化を担うエンジニア諸君。日々のプロジェクト管理において、Excelやテキストベースで作成されたWBS(Work Breakdown Structure)をMicrosoft Projectに手動で落とし込む作業にいかに多くの時間をドブに捨てているか、気づいているかね?
「1. 〇〇」「1.1 〇〇」「1.1.1 〇〇」といった番号つきの命名規則。これを人間が目視で判断し、Project上でインデント(のアウトラインレベル)を一つずつポチポチ下げていく……。そんな前時代的な作業は、今日で終わりにしよう。
今回は、タスクの「名前(Name)」に含まれるドット区切りの番号体系を正規表現で解析し、一瞬で完璧なWBS階層構造を構築するプロダクション品質のVBAパーサーを授けよう。
単に動くだけのコードではない。Project VBA特有の「オブジェクトライフサイクル」と「パフォーマンスの罠」を完全回避する、チーフアーキテクト直伝の堅牢な設計を解説する。
—
1. なぜ「力技のインデント操作」は破綻するのか?
初心者がやりがちな実装はこうだ。
‘ 【悪例】絶対にやってはいけない実装
Dim t As Task
For Each t In ActiveProject.Tasks
If Left(t.Name, 3) = “1.1” Then
t.OutlineIndent
End If
Next t
笑えないことに、実際の現場でもこの手のコードが見受けられる。何が問題か?
1. 暗黙の再計算と画面描画のコスト: `OutlineIndent`メソッドを呼び出すたびに、Projectはスケジュール全体と依存関係の再計算、さらにGUIの再描画走査走らせる。タスクが1,000件あれば、マクロが完了するまでにコーヒーを飲み干せるほどの時間がかかる。
2. トップダウンかボトムアップかの順序問題: インデントを下げる(レベルを深くする)操作を誤った順序で行うと、親タスクが存在しない状態で子タスクのレベル変更が発生し、Projectのエンジンがエラーを吐くか、意図しないガントチャートの崩壊を招く。
我々が目指すべきは、「一括してタスクをフラットに高速生成し、計算を抑制した状態でレベルを一網打尽に決定する」というアルゴリズムだ。
—
2. 堅牢なインデントパーサーの設計思想
今回構築するパーサーのアーキテクチャは以下の通りだ。
1. データソースの分離: インポート元(ExcelやCSV、あるいは配列)からデータを安全にメモリ上に読み込む。
2. 正規表現によるレベル判定: 文字列の先頭にある `1.2.3` のようなパターンを正規表現(RegEx)でキャプチャし、ドットの数から「アウトラインレベル(OutlineLevel)」を数理的に算出し、即座に数値として保持する。
3. トランザクション的処理(画面更新の凍結): `Application.ScreenUpdating = False` と計算モードの制御により、爆速の処理速度を実現する。
—
3. 【プロダクションコード】WBS自動構築パーサー
以下のコードは、エラーハンドリング、トランザクション制御、正規表現による厳密なパース処理を網羅した、そのまま現場で使える実用コードだ。
Option Explicit
‘ ==============================================================================
‘ 担当: チーフアーキテクト
‘ 概要: タスク名に含まれるプレフィックス(例: “1.2.3 〇〇”)を解析し、
‘ Microsoft ProjectのWBS階層構造を自動構築するパーサー
‘ ==============================================================================
Public Sub BuildWBSHierarchy()
‘ 画面描画と自動計算を停止し、パフォーマンスを限界まで引き上げる
Application.ScreenUpdating = False
Dim originalCalcMode As Long
originalCalcMode = Application.Calculation
Application.Calculation = pjCalculationManual ‘ 手動計算モードへ移行
On Error GoTo ErrorHandler
Dim wsData As Worksheet
‘ ※実務では適切なシートオブジェクトを指定してください
Set wsData = ActiveSheet
Dim lastRow As Long
lastRow = wsData.Cells(wsData.Rows.Count, “A”).End(xlUp).Row
If lastRow < 2 Then
MsgBox "インポートするタスクデータが存在しません。", vbExclamation, "WBSパーサー"
GoTo Finally
End If
' Excelからタスク名を二次元配列が一括取得(I/Oを最小化)
Dim rawData As Variant
rawData = wsData.Range(wsData.Cells(2, 1), wsData.Cells(lastRow, 1)).Value
Dim i As Long
Dim taskName As String
Dim targetLevel As Integer
Dim regEx As Object
Dim matches As Object
' RegExpオブジェクトの初期化(ループ外で生成しメモリ効率を最適化)
Set regEx = CreateObject("VBScript.RegExp")
regEx.Pattern = "^([0-9]+(\.[0-9]+))\s+"
regEx.IgnoreCase = True
regEx.Global = False
' ステップ1: タスクを一旦すべてルートレベル(Level 1)でフラット追加する
Dim t As Task
For i = 1 To UBound(rawData, 1)
taskName = CStr(rawData(i, 1))
If Len(Trim(taskName)) > 0 Then
Set t = ActiveProject.Tasks.Add(Name:=taskName)
t.OutlineLevel = 1 ‘ 全てフラットに並べる
End If
Next i
‘ ステップ2: 各タスクの名前を解析し、正しいアウトラインレベルを設定する
Dim currentTaskIndex As Long
currentTaskIndex = 1
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
If regEx.Test(t.Name) Then
Set matches = regEx.Execute(t.Name)
‘ ドットの数からインデントレベルを算出(例: “1” = 1, “1.1” = 2, “1.1.1” = 3)
targetLevel = Len(matches(0).SubMatches(0)) – Len(Replace(matches(0).SubMatches(0), “.”, “”)) + 1
‘ Projectの制限値(最大10階層)に丸める
If targetLevel > 10 Then targetLevel = 10
If targetLevel < 1 Then targetLevel = 1
t.OutlineLevel = targetLevel
End If
End If
Next t
' 変更を反映するために再計算を実行
Calculate
MsgBox "WBSの階層構造の構築が完了しました。", vbInformation, "WBSパーサー"
Finally:
' 環境を確実に元の状態に戻す(異常系・正常系共通)
Application.ScreenUpdating = True
Application.Calculation = originalCalcMode
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "WBSパーサー異常終了"
Resume Finally
End Sub
---
4. プロジェクト運用上の注意点とデータベース連携の極意
このツールを実際の業務フロー(例えば、PMBOKに準拠した全社プロジェクト管理基盤)に組み込む際、以下のアーキテクチャ上の鉄則を守ってほしい。
1. 外部DBやExcelとのデータ同期における「UID(ユニークID)」の保持
単にタスク名でインデントを再構築するだけのアプローチは、すでに進捗( `% Complete` )や実績工数( `Actual Work` )が入力されている既存プロジェクトに対して実行すると、タスクの順序やIDがズレてデータが破壊される危険がある。
もし既存プロジェクトをアップデートする場合は、タスク名だけでなく、隠しフィールド(テキストカスタムフィールド `Text1` など)に外部DB側のタスクIDや一意識別子を持たせ、IDベースでマッチングした上で `OutlineLevel` を制御する設計に拡張すべきだ。
2. トランザクション制御の重要性
前述のコードで `Application.Calculation = pjCalculationManual` を挟んでいるのは、Projectのパフォーマンスを守るための生命線だ。タスク数が数千件規模になると、オートマチック計算が走るたびにCPUコアが張り付き、マクロがフリーズしたような挙動を示す。必ず処理の前後で描画と計算を制御し、最後に明示的な `Calculate` を呼ぶこと。
—
5. 総括
業務自動化の本質は、「人間が手作業で行う必要のない無駄な認知コストと作業時間を、コードによって極限までゼロに近づけること」にある。
今回提供したインデントパーサーは、その思想を具現化したものだ。このコードをあなたの組織のツールボックスに組み込み、煩雑なWBSの手動整備という無駄な労働からチームを解放してやりたまえ。
健闘を祈る。
