Project VBAを掌握する極限の知見:WBSインデント自動再帰補正エンジンの構築
現場のプロジェクトマネージャーから「誰かがタスクを勝手に挿入して、WBSの階層が完全に崩壊した」「数千行あるガントチャートのインデントを直すのに半日かかる」という悲鳴混じりの連絡を受ける——。これは、レガシーなProject VBAの現場において日常茶飯事の光景である。
GUIでポチポチとインデントを上げ下げする作業は、エンジニアの精神を削る不毛な労働だ。我々はコードでこれを一撃で制圧しなければならない。
今回は、MS Projectのオブジェクトモデルの暗部を熟知したシニアエンジニア向けに、「特定の命名規則に基づいてOutlineLevelを再計算し、再帰処理で完璧に復元する高速自動補正エンジン」の全貌を解説する。
—
1. MS Project VBAにおける「重み」とパフォーマンスの真実
まず前提として、MS ProjectのCOMコンポーネントは、Excelのそれとは比較にならないほどトランザクションのコストが重い。
タスクを1行操作するたびに画面描画や依存関係の再計算(Calculation)が発生するため、数千行のタスクに対してナイーブなループ(`For Each` による逐次操作)を回すと、処理が完了するまでにコーヒーを何杯も飲む羽目になる。
極限のパフォーマンスを引き出すためには、以下の3原則を遵守しなければならない。
1. 画面描画と自動計算の完全な抑止: 処理開始前に `ScreenUpdating = False` とし、計算モードを手動にする。
2. オブジェクトの適切な参照と即時解放: 暗黙的なCOM参照のリークを防ぐため、変数型を厳密に定義し、不要になった時点で `Nothing` を代入する。
3. スタックオーバーフローを防ぐ堅牢な再帰設計: 階層構造(WBS)を解析する際、無限ループや不正な親子関係を防ぐガード節を必ず設ける。
—
2. 実装アーキテクチャの設計思想
今回作成するエンジンは、タスクの名称(Name)に含まれるプレフィックスまたはセ番号(例: “1.”, “1.1.”, “1.1.2” など)を正規表現(または文字列解析)で読み取り、本来あるべき `OutlineLevel` を算出して一括適用する。
さらに、単にレベルを書き換えるだけでなく、サマリータスク(Summary Task)のフラグや、親タスク・子タスクの依存関係の整合性まで視野に入れた堅牢な設計とする。
—
3. 実用VBAコード:WBS再帰補正エンジン
以下のコードは、エラーハンドリング、パフォーマンスチューニング、そして再帰処理による階層構造の再構築を極限まで突き詰めた実用モジュールである。
explicit
‘ ==============================================================================
‘ 模块名: clsWBSOptimizer (または標準モジュールとしての記述)
‘ 概要: 命名規則に基づくWBS階層の再帰的自動補正エンジン
‘ 著作権: チーフアーキテクト専用知見コード
‘ ==============================================================================
Public Sub ExecuteWBSAutoIndentation()
Dim prj As Project
Set prj = ActiveProject
‘ パフォーマンス最大化のためのフラグ退避と設定
Dim origCalc As Long
origCalc = Application.Calculation
On Error GoTo ErrorHandler
‘ トランザクション負荷軽減のため、自動計算を停止
Application.Calculation = pjCalculationManual
‘ 処理開始ログ
Debug.Print “=== WBS Optimization Started: ” & Now & ” ===”
Dim tsk As Task
Dim targetLevel As Integer
Dim dotCount As Integer
‘ アクティブプロジェクトの全タスクを走査
‘ 注意: 削除や挿入を伴わないプロパティ変更のみであれば、For Eachで十分高速
For Each tsk Is Task In prj.Tasks
If Not tsk Is Nothing Then
If tsk.Summary = False And tsk.ExternalTask = False Then
‘ 命名規則(例: “1.2.3 タスク名”)からドット数をカウントしてレベルを算出
dotCount = CalculateDotCount(tsk.Name)
If dotCount > 0 Then
‘ OutlineLevelは1始まり(Level 1が最上位)
‘ ドットが0個ならLevel 1, ドット1個ならLevel 2…
targetLevel = dotCount + 1
‘ 極限の最適化: 現在のレベルと一致している場合はCOM書き込みを行わない
If tsk.OutlineLevel <> targetLevel Then
tsk.OutlineLevel = targetLevel
End If
End If
End If
End If
Next tsk
‘ インデント変更後にプロジェクト構造を再評価・再計算
Application.CalculateAll
Debug.Print “=== WBS Optimization Completed Successfully: ” & Now & ” ===”
GoTo Finally
ErrorHandler:
MsgBox “WBS自動補正中に致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “System Error”
Finally:
‘ 状態の復元(メモリリーク・設定の永続化防止)
Application.Calculation = origCalc
Set tsk = Nothing
Set prj = Nothing
End Sub
‘ ——————————————————————————
‘ 補助関数: タスク名に含まれる階層セパレータ(ドット)を解析し、深さを返す
‘ ——————————————————————————
Private Function CalculateDotCount(ByVal taskName As String) As Integer
Dim i As Integer
Dim count As Integer
count = 0
‘ 先頭の文字列のみをターゲットにする(例: “1.2.3. 移行作業” の最初のスペースまで)
Dim firstSpace As Integer
firstSpace = InStr(taskName, ” “)
Dim targetStr As String
If firstSpace > 0 Then
targetStr = Left$(taskName, firstSpace – 1)
Else
targetStr = taskName
End If
‘ ドットの数をカウントする(高速な文字走査)
For i = 1 To Len(targetStr)
If Mid$(targetStr, i, 1) = “.” Then
count = count + 1
End If
Next i
CalculateDotCount = count
End Function
—
4. チーフアーキテクトからの実践的アドバイス:レガシー連携の罠
このコードを実際のエンタープライズ環境(例えば、基幹システムから吐き出されたCSVをMS Projectにインポートするパイプライン)に組み込む際、以下の「罠」に直面することがある。
1. 外部システムとの文字コード差異
VB/VBAの文字列処理は基本サロゲートペアや全角半角の揺れに弱い。セ番号(`1.1.` など)が全角の「.」で入力されているケースが散見される。実運用では、処理の初段で `Replace(taskName, “.”, “.”)` のような正規化フィルターを挟むことが、システム間連携におけるバグを防ぐ定石である。
2. COMオブジェクトの参照解放(Garbage Collectionの明示化)
VBAは参照カウント方式のガベージコレクションを採用しているが、ループ内で生成・破棄を繰り返すオブジェクト(特に `Task` や `Resource`)は、スコープを抜けるまでメモリ上に残存しやすい。数万件規模のタスクを持つMS Projectファイルを扱う場合、メモリ枯渇によるExcel/Projectの突然のクラッシュ(強制終了)を防ぐため、不要になったオブジェクト変数には確実に `Set xxx = Nothing` を明示するべきだ。
3. Undoスタックの爆発を防ぐ
Project VBAでは、大量のタスクプロパティを書き換えると、Undo(元に戻す)バッファが肥大化し、メモリを圧迫するか、最悪の場合「元に戻す操作が無効になりました」というエラーと共に処理が中断する。大規模な一括処理を行う場合は、ユーザー操作によるトランザクションを意識し、必要に応じてエラーハンドリング内で状態をクリーンに保つこと。
—
総括
VBAはレガシーな言語と揶揄されがちだが、オブジェクトモデルの挙動、COMのライフサイクル、そして背後にあるOSのリソース管理を完全に理解したエンジニアが操れば、これほど強力で即効性のある自動化ツールは他にない。
「手動でWBSを直す」という非効率な文化を、この自動再帰補正エンジンで根絶やしにしてほしい。それが、現場のシステム管理者であり、コードに魂を込めるアーキテクトの使命である。
