【入門編】【実務中級】Task.OutlineChildrenを用いたWBSの再帰的走査:階層構造をCSVへ書き出すツール – Project VBA解析バイブル

スポンサーリンク

こんにちは!Project VBAの世界へようこそ。
マクロの記録ボタンを押すだけの日常から一歩踏み出し、「自分の手でプロジェクトの構造を自在に操りたい」と願うあなたへ。

今回は、実務で必ず直面する大きな壁――「WBS(Work Breakdown Structure)の階層構造をプログラムで綺麗に読み解き、外部に出力する」というテーマに挑みます。

ここをクリアすれば、Project VBAの基本はバッチリです。オブジェクトモデルの本質を掴み、ワンランク上のエンジニアへとステップアップしましょう!

1. なぜ「再帰処理」と「OutlineChildren」が必要なのか?

MS Projectを開くと、タスクが綺麗にツリー状(階層構造)に並んでいますよね。
「親タスク」の下に「子タスク」があり、その中にはさらに「孫タスク」がいる……。この構造は、人間が見る分には直感的で分かりやすいのですが、ExcelやCSVなどのフラット(平坦)な表に落とし込むときは少し工夫が必要です。

ここで登場するのが、伝説のプロパティ `OutlineChildren` です。

タスクの家族関係を想像してみよう

Project VBAにおいて、すべてのタスクは `Task` オブジェクトという部品でできています。
親タスクの視点に立ってみてください。自分には「子供(直属の子タスク)」が何人かいます。その子供たちにも、さらに子供がいるかもしれません。

これを上から順番に手動で追いかけていくのは、タスク数が100個、1000個と増えたら発狂してしまいますよね。
そこで、「自分に子供がいるなら、子供のところへ行って同じ作業を繰り返す(これを自分自身を呼び出す=再帰呼び出しと言います)」というアルゴリズムを使います。

この「再帰(Recursion)」と `OutlineChildren` を組み合わせることで、どんなに複雑な階層構造のWBSであっても、綺麗に上から順に(あるいは下から順に)舐め尽くすことができるのです。

2. 全体像のイメージ:ツリーからリストへの変換

概念を図解してみましょう。

[プロジェクト全体]
┣ 1. 要件定義 (親)
┃ ┣ 1.1 ヒアリング (子)
┃ ┗ 1.2 要件定義書作成 (子)
┗ 2. 設計 (親)
┣ 2.1 外部設計 (子)
┗ 2.2 内部設計 (子)

これを、私たちのプログラムは次のような「フラットなCSVリスト」に変換します。

ID,階層レベル,タスク名,開始日,終了日
1,1,要件定義,2023/10/01,2023/10/10
2,2,ヒアリング,2023/10/01,2023/10/05
3,2,要件定義書作成,2023/10/06,2023/10/10
4,1,設計,2023/10/11,2023/10/20

この魔法を実現するコードを、次章で一気に見ていきましょう。

3. 実装コード:WBS再帰的CSV出力ツール

MS ProjectのVBE(Visual Basic Editor)を開き、標準モジュールに以下のコードを貼り付けてください。実務ですぐに使えるよう、ファイル出力のエラーハンドリングや文字コードへの配慮も組み込んでいます。

‘ =====================================================================
‘ módulo名: modWBSExporter
‘ 概要: ProjectのWBS階層構造を再帰的に走査し、CSVファイルへ出力する
‘ =====================================================================
Sub ExportWBSToCSV()
Dim filePath As String
Dim fileNum As Integer
Dim tsk As Task

‘ 出力先ファイルのパス(デスクトップの WBS_Export.csv)
filePath = CreateObject(“WScript.Shell”).SpecialFolders(“Desktop”) & “\WBS_Export.csv”

‘ 空きファイル番号を取得
fileNum = FreeFile

‘ CSVファイルを書き込みモードでオープン
Open filePath For Output As #fileNum

‘ ヘッダー行の書き込み
Print #fileNum, “ID,OutlineLevel,Name,Start,Finish,Duration”

‘ アクティブプロジェクトのルートタスク群に対して再帰処理を開始
‘ ※ Project.Tasks はプロジェクト全体のタスクコレクション
For Each tsk In ActiveProject.Tasks
‘ 削除されたタスクやNothing(空スロット)を安全に弾くためのガード
If Not tsk Is Nothing Then
‘ サマリータスク(親)のトップレベル、または独立したタスクからスタート
‘ OutlineLevelが1のものを起点とするのが安全です
If tsk.OutlineLevel = 1 Then
Call TraverseTasks(tsk, fileNum)
End If
End If
Next tsk

‘ ファイルを閉じる
Close #fileNum

MsgBox “WBSのCSV出力が完了しました!” & vbCrLf & “保存先: ” & filePath, vbInformation, “完了”
End Sub

‘ =====================================================================
‘ サブルーチン: 再帰的にタスクを走査しCSVに書き出す心臓部
‘ =====================================================================
Private Sub TraverseTasks(ByVal currentTask As Task, ByVal fileNum As Integer)
Dim childTask As Task

‘ 1. まず「自分自身」の情報をCSVの1行として書き出す
‘ ダブルクォーテーションで囲むことで、タスク名にカンマが含まれてもCSVが壊れないように配慮
Print #fileNum, currentTask.ID & “,” & _
currentTask.OutlineLevel & “,” & _
“””” & currentTask.Name & “””,” & _
currentTask.Start & “,” & _
currentTask.Finish & “,” & _
currentTask.Duration / 480 & “日” ‘ Projectの内部単位(分)日単位(8時間=480分)に変換

‘ 2. 自分に「子タスク」がいるかどうかをチェックする
‘ OutlineChildrenプロパティは、直属の子タスクたちのコレクションを返す
If currentTask.OutlineChildren.Count > 0 Then
‘ 子タスクが複数いるので、1つずつループを回す
For Each childTask In currentTask.OutlineChildren
‘ 【重要】子タスクを引数にして、自分自身(TraverseTasks)をもう一度呼び出す!
Call TraverseTasks(childTask, fileNum)
Next childTask
End If
End Sub

4. コードの深掘り解説:ここがプロの勘所

初心者の方が最初に戸惑うポイントと、このコードの美しさをいくつか解説します。

① `If Not tsk Is Nothing Then` というお作法

MS Projectの `Tasks` コレクションは、タスクが途中で削除されたりすると、メモリ上に「空の箱(Nothing)」を残すことがあります。これをそのまま `tsk.Name` などと呼び出すと、無慈悲な 「実行時エラー ’91’: オブジェクト変数が設定されていません」 が発生します。
実務コードでは、`Not tsk Is Nothing` によるガードが必須の作法です。

② `OutlineChildren` と `OutlineLevel` のコンビネーション

  • `OutlineLevel`: そのタスクがツリーの何階層目にあるかを教えてくれる数値です(一番上が1)。
  • `OutlineChildren`: 直下の子供たちだけを内包するコレクションです。孫以下のタスクは含まれません。

だからこそ、子タスクそれぞれに対して再び `TraverseTasks` を呼び出す(再帰)ことで、孫、ひ孫…と無限の深さまで自動で潜っていくことができるのです。

③ 時間単位の罠 (`Duration` プロパティ)

Project VBAの `Duration` は、人間が目にする「日」や「時間」ではなく、「分(Minutes)」を基準に持っています。そのため、そのまま出力すると「2400」のような大きな数字になってしまいます。
コード内では `currentTask.Duration / 480` として、1日(8時間 = 480分)で割って「日単位」に直しているのが、実務で役立つ細かな配慮です。

5. よくあるエラーと回避のコツ

  • Q. 実行してもファイルに何も書き込まれない、あるいは一部のタスクが抜ける
  • A. ルートの絞り込み条件(`If tsk.OutlineLevel = 1 Then`)を確認してください。MS Projectの構成によっては、トップレベルにプロジェクト名サマリーが存在し、その下がすべてレベル1になっている場合があります。もし「プロジェクト自体」がタスクとして含まれるのが嫌な場合は、`ActiveProject.SummaryTask.OutlineChildren` を起点にするのも一つの手です。
  • Q. CSVをExcelで開いたときに文字化けする
  • A. VBAの `Open … For Output` はデフォルトでShift-JIS(ANSI)で出力します。もし最新のExcelや他システム連携でUTF-8が必要な場合は、ADODB.Streamオブジェクトを使ってストリーム書き込みを行う高度な実装に拡張してみてください。まずは今回の基本形を完璧にマスターするのが近道です。

まとめ

いかがでしたでしょうか?
「再帰処理」と聞くと難解な数学のパズルのように聞こえますが、実態は「親子関係のバトンタッチをプログラムに自動でやらせているだけ」です。

ここをクリアできれば、Projectのデータを自由自在に加工・集計し、社内のBIツールやExcelレポートへシームレスに連携できるようになります。あなたの業務自動化スキルは、間違いなく「中級者」の領域に到達しています。

ぜひ、日々のプロジェクト管理の現場でこのコードを役立ててみてください。
それでは、次回の極限知見でお会いしましょう!

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