Project VBAの深淵:なぜ「ループ」でタスクを探してはいけないのか?
Project VBAを扱うエンジニアの多くが、最初に行う過ち。それは「全タスクをFor Eachで回して、名前をIf文で判定する」というナイーブな実装です。
小規模なプロジェクトならそれで動きます。しかし、数千行のタスクを抱えるエンタープライズ環境でそれをやれば、UIは固まり、メモリは浪費され、何より「コードとしての品格」を欠くことになります。
今日は、ProjectのAPIが提供する強力な検索エンジン――`Tasks.Find`メソッドを使いこなし、堅牢かつ爆速でタスクを掌握する方法を伝授します。
—
1. なぜ「全探索ループ」は悪手なのか
`For Each t In ActiveProject.Tasks` を書くことは、Projectがメモリ上に展開しているタスクツリーに対して、毎回フルスキャンをかけることを意味します。
- 非効率: タスク数が増えるほど、処理時間は線形(あるいはそれ以上)に増大します。
- 保守性の欠如: プロジェクト構造が変わるたびにループの条件式を書き換えるリスクがあります。
- APIの無駄遣い: MS Projectには、インデックスを活用した高速な検索メソッドが既に備わっているのです。
—
2. 実践:`Tasks.Find` を用いた「正しい」検索手法
`Tasks.Find` は、UI上の「検索」機能をコードから呼び出すものです。これを使えば、内部的な最適化アルゴリズムを享受できます。
ただし、「検索結果が選択状態になる」という副作用があるため、実務コードでは「現在の選択状態を退避させ、検索後に戻す」というアーキテクチャが必須です。
堅牢なタスク検索・取得関数(プロダクションコード)
以下のコードは、単にタスクを取得するだけでなく、エラーハンドリングと状態復元を完璧に行うテンプレートです。
‘ @description 指定した名前のタスクを検索し、オブジェクトを返す
‘ @param taskName 検索対象のタスク名
‘ @return 見つかればTaskオブジェクト、見つからなければNothing
Public Function FindTaskByName(ByVal taskName As String) As Task
Dim app As MSProject.Application
Set app = Application
‘ 現在の選択状態を記憶(検索による副作用を無効化するため)
Dim currentSelection As Selection
Set currentSelection = app.ActiveSelection
On Error Resume Next
‘ 検索を実行:名前フィールドを対象に一致検索
‘ 最後の引数 True は「完全一致」を意味する
If app.Find(Field:=”Name”, Test:=”equals”, Value:=taskName, SearchAllTasks:=True) Then
‘ 見つかった場合、現在選択されているタスクを取得
Set FindTaskByName = app.ActiveSelection.Tasks(1)
Else
Set FindTaskByName = Nothing
End If
On Error GoTo 0
‘ 選択状態を復元(ユーザーへの影響を最小限にする)
currentSelection.Select
End Function
—
3. 現場で生き残るための設計上の注意点
このコードを実務に組み込む際、以下の「罠」を避けることがプロとしての責任です。
① データベース・外部ファイル連携の注意点
外部のCSVやExcelから読み込んだタスク名をキーにする場合、「見えない空白」に注意してください。
`Trim()`関数を必ず通すこと。また、プロジェクト内に同じ名前のタスクが複数存在する場合、上記のコードは「最初に見つかったもの」を返します。これが仕様上許されない場合は、`UniqueID`をキーにする設計への変更を検討してください。
② WBS階層の考慮
タスク名は重複する可能性があります。もし名前だけでなく「親タスクの名前」も条件にしたい場合は、`Find`メソッドを組み合わせるよりも、再帰関数でツリーを走査する設計に切り替えるべきです。これは「検索速度」よりも「データの整合性」が優先されるケースです。
③ パフォーマンスの「神速」チューニング
大量の書き換えを行う場合は、必ず以下の設定をコードの冒頭に入れてください。
Application.ScreenUpdating = False ‘ 画面更新停止
‘ … 処理 …
Application.ScreenUpdating = True ‘ 画面更新再開
これだけで、数秒かかる処理がコンマ数秒に短縮されます。
—
最後に:エンジニアとして持つべき視点
自動化ツールは「動けばいい」ものではありません。
「次にコードを触る誰か(あるいは未来の自分)」が、そのコードを見たときに「この設計者はProjectのAPIを理解しているな」と思わせること。それが、保守性を担保し、バグを未然に防ぐ唯一の方法です。
まずはこの `FindTaskByName` 関数をモジュールに格納し、あなたのプロジェクトを「制御可能なデータ構造」へと変貌させてください。
健闘を祈ります。
