Project VBAの深淵:ScreenUpdatingとCalculationを制御し、計算リソースを極限まで絞り出す
諸君、MS ProjectのVBA環境において、標準的なループ処理がいかに「鈍重な重石」と化すかを理解しているか。
数千行のタスクを回す際、デフォルトの挙動に身を任せるのは素人のやることだ。MS ProjectのGUIは、一つ一つのTaskオブジェクトの変更に対し、イベントを拾い、再描画を行い、さらにはスケジューリングエンジンを再計算しようと試みる。これらを放置することは、砂利道でスポーツカーを走らせるようなものだ。
本稿では、レガシーなアーキテクチャの制約を逆手に取り、計算リソースを完全に掌握するための極限の最適化手法を伝授する。
—
1. 物理的なコスト:なぜ「画面更新」と「再計算」がボトルネックになるのか
MS Projectにおいて、`Application.ScreenUpdating` は単なる描画のフラグではない。これは、COMインターフェースを介した「GUIプロセスとの同期」を制御するゲートだ。これをONにしたままの状態では、各Taskオブジェクトのプロパティ変更のたびに、UIスレッドがロックされ、再描画のキューが溜まる。
同様に、`Calculation` を自動に設定したままでは、1つのタスクを書き換えるたびにクリティカルパスの再計算が走り、CPUのサイクルをドブに捨てることになる。
これを制御するのは、単なる「作法」ではなく「生存戦略」だ。
—
2. 【核心】計算リソースを掌握する最適化パターン
以下のコードは、高負荷なタスク操作を行う際の「定石」である。重要なのは、例外処理(Error Handling)を確実に配置し、強制終了時にも確実にシステム設定を復元させることだ。これを行わないコードは、バグの温床となる。
Public Sub OptimizeTaskOperations()
Dim app As MSProject.Application
Set app = Application
‘ 現行の計算モードと更新設定を退避
Dim origCalc As PjCalculation
Dim origScreen As Boolean
origCalc = app.Calculation
origScreen = app.ScreenUpdating
‘ パフォーマンス最適化の開始
On Error GoTo Cleanup
app.ScreenUpdating = False
app.Calculation = pjManual ‘ 手動計算モードへ強制遷移
‘ — ここに重厚なタスク処理を記述 —
Call ProcessHeavyTasks(app.ActiveProject)
‘ ———————————-
Cleanup:
‘ 異常終了時も必ず設定を戻す(重要:ここを疎かにするとシステムが半壊する)
app.Calculation = origCalc
app.ScreenUpdating = origScreen
If Err.Number <> 0 Then
MsgBox “処理中に致命的なエラーが発生しました: ” & Err.Description, vbCritical
End If
End Sub
—
3. メモリの深淵:オブジェクトの明示的解放とポインタの意識
VBAはガベージコレクションを搭載しているが、MS ProjectのCOMオブジェクトは巨大だ。特に `Tasks` コレクションや `Resources` コレクションをループ内で安易に生成・保持し続けると、メモリリークの温床となる。
- オブジェクトは参照のみ保持せよ: `Set tsk = proj.Tasks(i)` と書く際、ループの先頭で必ず `Set tsk = Nothing` を意識するレベルの規律が必要だ。
- Late Bindingを検討せよ: もし外部ライブラリとの連携がメインであれば、`Object` 型によるレイトバインディングを使い、コンパイル時のオーバーヘッドを削減するのも一つの手だ。ただし、IntelliSenseが効かなくなるため、それ相応の設計スキルが求められる。
—
4. レガシー環境を生き抜くための「隠し味」:Windows APIの活用
さらに突き詰めたいのであれば、`Sleep` APIを用いてシステムにわずかな猶予を与える手法がある。高速化を追求するあまり、CPUを100%専有し、OSのレスポンスを奪ってしまうのはアーキテクトとして三流だ。
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
‘ 大規模なバッチ処理の合間に挟む
‘ DoEventsを使うより遥かに低コストで、OSへのリソース返却が可能
Sleep 10
—
最後に:シニアエンジニアとしての矜持
VBAを「古い言語」と揶揄する輩は、その真のポテンシャルを知らない。MS Projectの深い階層にあるオブジェクトモデルを操作し、計算エンジンを手のひらの上で転がす作業は、極めて高次元な計算資源の管理そのものだ。
コードを記述する際は、常に「この行はCOMインターフェースを何回叩いているか?」を自問自答せよ。無駄な呼び出しを排し、システムに余計な負荷をかけない設計こそが、大規模案件を完遂させる真のシニアエンジニアの流儀である。
次回の記事では、`Project_BeforeTaskChange` イベントをフックして、更新対象を最小限に絞り込む「差分更新アルゴリズム」について深掘りするとしよう。準備はいいか。
