Project VBAを掌握せよ:タスク検索の「正道」とメモリの深淵
VBAでProjectのタスクを操作する際、多くの開発者が陥る罠がある。それは「ループによる全走査」だ。`For Each`で数千のタスクを回し、名前を比較する。小規模なスケジュールならそれで十分だろう。だが、数万行規模のWBSを扱うエンタープライズ環境において、それは「死」を意味する。
今回は、Project VBAにおけるタスク検索の最適解、すなわち`Find`メソッドを使いこなし、メモリの断片化を回避しつつ、高速にオブジェクトを特定する技術を伝授する。
—
1. なぜ「Findメソッド」なのか:O(N)からの脱却
`Tasks.Item(“タスク名”)`という直感的な参照は、裏側で毎回インデックスの検索が走る。繰り返せばパフォーマンスは崩壊する。ここで活用すべきは、Projectの`Task`オブジェクトが持つネイティブな`Find`メソッドだ。
このメソッドは、Projectの内部エンジンが管理するインデックスを直接叩くため、手動でループを回すよりも桁違いに速い。
高速検索の実装例
‘ 伝説的なチーフアーキテクトが推奨する、高速タスク検索関数
Public Function FindTaskByName(ByVal taskName As String) As Task
Dim t As Task
‘ ActiveProject.Tasks.Find は検索条件に合致する最初のタスクを返す
‘ 速度の要諦は、無駄なオブジェクト生成を避けることにある
Set t = ActiveProject.Tasks.Find(Field:=”Name”, Value:=taskName)
If Not t Is Nothing Then
Set FindTaskByName = t
End If
‘ メモリの解放:VBAにおいてSet Nothingは必須の作法ではないが、
‘ オブジェクトの参照カウントを意識する姿勢が、大規模開発ではバグを未然に防ぐ
Set t = Nothing
End Function
—
2. メモリの深淵:オブジェクトのライフサイクルと解放の真実
VBAはガベージコレクションを備えていない。オブジェクト参照を放置することは、メモリリークの温床であり、特に`Project.Application`を外部から制御するようなアーキテクチャでは、ExcelやProjectのゾンビプロセスを生成する原因となる。
安定性を高めるための「明示的解放」
大量のタスクを更新するループ処理では、必ず参照変数を初期化せよ。
Sub UpdateTaskDependencies()
Dim t As Task
Dim targetTask As Task
‘ カレントタスクを走査する際、参照カウントに注意を払う
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
‘ プロパティ操作後に即座に参照を解除する意識を持つ
‘ これにより、大規模なメモリリークを未然に防ぐ
t.Predecessors = “1,2,3”
End If
Next t
‘ スコープを抜ける前に明示的に解放
Set t = Nothing
End Sub
—
3. レガシー環境への配慮:Windows APIとの連携
Project VBAの限界に直面したとき、Windows APIの呼び出しは、最後の切り札だ。例えば、大規模なタスク処理中に画面の描画を停止させ、パフォーマンスを極限まで引き上げる手法がある。
これは`LockWindowUpdate` APIを使用する手法だが、これをVBAで制御することで、ユーザー体験(UIのチラつき防止)と処理速度の両立を図る。
‘ API宣言(モジュールレベルで記述)
If VBA7 Then
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hwndLock As LongPtr) As LongPtr
Else
Private Declare Function LockWindowUpdate Lib “user32” (ByVal hwndLock As Long) As Long
End If
‘ 処理開始時にLockWindowUpdate(Application.hWnd)を呼び出し、
‘ 終了時にLockWindowUpdate(0)で解放する。
‘ これにより、再描画のオーバーヘッドが劇的に減少する。
—
4. 最後に:アーキテクトからの助言
技術とは「動くこと」がスタートラインであり、「いかに美しく、いかに堅牢に、いかに予測可能であるか」がゴールだ。
- 名前をキーにするな: 可能な限り `UniqueID` をキーにせよ。名前は人間が読むためのメタデータであり、システムが識別するためのIDではない。
- 例外処理は必須: `Find`がNothingを返すケースを常に想定せよ。`If Not t Is Nothing`を省略するコードは、プロの書くものではない。
- アーキテクチャを見据えよ: プロジェクト単体で完結するVBAを書くのではなく、将来的に外部DLLや.NETコンポーネントへ移植可能な抽象度を保つことが、真の技術的負債を防ぐ唯一の道だ。
コードは嘘をつかない。君が書いたその一行が、数年後の誰かの運命を決定するかもしれない。その気概を持って、Projectを掌握せよ。
