Project VBAを掌握する極限の知見:大量タスク処理でメモリを食いつぶさないための「オブジェクト解放術」
こんにちは。現場のコードを、もっと美しく、もっと速く。
今日は、Microsoft Project VBAの深淵へと足を踏み入れてみましょう。
「マクロの記録」でコードを書き始め、少しずつ複雑な処理ができるようになった。そんなあなたが次にぶち当たる壁――それは「処理がどんどん重くなる」「タスクが増えるとProjectが落ちる」という、目に見えない恐怖です。
実はこれ、プログラムの書き方ひとつで回避できる「メモリ管理」の問題なんです。今回は、カスタムフィールド(Task/Resource)を大量に操作する際に避けては通れない、メモリリーク対策について、その本質を伝授します。
ここをクリアすれば、あなたはもう「動けばいいマクロ」を書く人ではなく、「プロフェッショナルな自動化エンジニア」への階段を登ることになりますよ。
1. なぜ、あなたのProjectは「重く」なるのか?
まず、根本的な原理を理解しましょう。
Project VBAでタスク(`Task`)やリソース(`Resource`)を操作するとき、私たちは単に数字を書き換えているのではありません。裏側では、「COMオブジェクト」と呼ばれる、非常に巨大で複雑なデータの塊を、VBAから呼び出して操作しています。
イメージ図:図書館の貸出管理
想像してみてください。あなたは図書館の司書です。
- タスクオブジェクト = 1冊の本
- 変数への代入 = 本を借りて、机に置くこと
もし、あなたが「本を借りる(変数に代入する)」動作を1,000回繰り返したとします。読み終わったあとに「本を棚に戻す(メモリを解放する)」ことを忘れたらどうなるでしょうか?
図書館の机は本で溢れかえり、新しい本を置くスペースがなくなってしまいます。これがメモリリーク(メモリの漏洩)の正体です。
Projectのような巨大なアプリケーションでは、この「机の上の本」がメモリを占有し、最終的には「メモリ不足」で強制終了を引き起こすのです。
2. 初心者が陥る「メモリを食いつぶすコード」の典型例
まずは、よくある「惜しい」コードを見てみましょう。
' ❌ 良くない例:メモリ管理が疎かなコード
Sub BadExample()
Dim t As Task
Dim myTask As Task
' 大量のタスクをループ
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
' オブジェクトを変数にセット
Set myTask = t
' カスタムフィールド(Text1)に値を書き込む
myTask.Text1 = "処理済み"
' ここで myTask を解放していない!
' ループが回るたびに、メモリ上に「参照の残りカス」が溜まっていく可能性がある
End If
Next t
MsgBox "完了しました"
End Sub
何が問題なのか?
このコードの最大の問題は、`Set myTask = t` で取得したオブジェクトへの参照を、明示的に「使い終わったよ」とコンピュータに伝えていない点です。
ループの回数が多い場合、VBAのガベージコレクション(自動掃除機能)が追いつかず、Projectのメモリ使用量が右肩上がりに増えていく現象が発生します。
3. プロフェッショナルが実践する「メモリ解放の黄金律」
では、どう書けばいいのか。極限の安定性を求めるなら、以下の3つのステップを徹底してください。
1. オブジェクト変数は、使い終わったら即座に `Nothing` を代入する。
2. 計算処理(Calculate)のタイミングを制御する。
3. 画面更新と再計算を一時停止し、負荷を最小限にする。
これらを盛り込んだ、実戦的なコードを見てみましょう。
🚀 決定版:最適化されたカスタムフィールド操作コード
' ✅ プロフェッショナルな例:メモリ管理を徹底したコード
Sub OptimizedCustomFieldUpdate()
Dim t As Task
Dim targetTask As Task
Dim taskCount As Long
' --- 準備:パフォーマンス向上のための設定 ---
' 画面更新を止めて、処理中のチラつきと負荷を軽減
Application.ScreenUpdating = False
' 自動再計算を止める(これが重要!ループ中に毎回計算させない)
Application.Calculation = pjCalculationManual
On Error GoTo ErrorHandler ' エラー時に設定を元に戻すためのトラップ
taskCount = 0
' --- メイン処理 ---
For Each t In ActiveProject.Tasks
' タスクが存在しない(空行など)場合はスキップ
If Not t Is Nothing Then
' 1. オブジェクトの参照をセット
Set targetTask = t
' 2. カスタムフィールド(Text1)への書き込み
' ここでは例として、タスク名に接頭辞を付ける
targetTask.Text1 = "[PROCESSED] " & targetTask.Name
' 3. 【重要】オブジェクトのライフサイクルを管理
' 処理が終わったら、即座に参照を解放する
Set targetTask = Nothing
taskCount = taskCount + 1
End If
Next t
' --- 後処理 ---
' 最後に一度だけ再計算を実行(ループ内で行わないのがコツ)
Application.Calculate
MsgBox taskCount & " 件のタスクを最適に処理しました。", vbInformation
CleanUp:
' 設定を元の状態に戻す(これを忘れるとProjectの挙動がおかしくなります)
Application.ScreenUpdating = True
Application.Calculation = pjCalculationAutomatic
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Resume CleanUp
End Sub
4. ここがプロの分かれ道:技術解説
なぜこのコードが「強い」のか。ポイントを整理しておきましょう。
① `Set targetTask = Nothing` の威力
ループの最後で `Nothing` を代入することで、コンピュータに対して「この本(オブジェクト)はもう使い終わりました。棚に戻していいですよ」と明確に伝えています。これにより、メモリの空き容量が常に一定に保たれます。
② `pjCalculationManual` による「計算爆発」の防止
これが最も見落としがちなポイントです。
カスタムフィールドの値を書き換えるたびに、Projectは「あ、値が変わった!プロジェクト全体を再計算しなきゃ!」と動いてしまいます。
1万件のタスクがある場合、1万回フル再計算が走る計算になります。これはメモリとCPUを極限まで消費します。
`Application.Calculation = pjCalculationManual` でこれを止め、最後に一括で `Calculate` する。この差が、処理時間を「数分」から「数秒」へと変えるのです。
③ エラーハンドリング(`On Error GoTo`)
メモリ管理や設定変更を行うコードでは、エラーが起きたときに「画面更新停止」や「手動計算モード」のまま放置されるのが一番の恐怖です。
`ErrorHandler` を用意し、どんなエラーが起きても必ず `CleanUp`(設定を元に戻す処理)を通るように設計するのが、一流の作法です。
まとめ:エンジニアとしての第一歩
いかがでしたか?
「ただ値を書き込むだけ」のコードと、「メモリのライフサイクルを意識した」コード。
その違いは、プロジェクトの規模が大きくなればなるほど、決定的な差となって現れます。
1. オブジェクトを使い終わったら `Nothing` で解放する。
2. 大量処理のときは `Calculation` を手動に切り替える。
3. エラーが起きても「元の設定に戻す」責任を持つ。
この3点を守るだけで、あなたの作るツールは劇的に安定します。
「動く」のその先にある、「安定して、速い」コードを目指して。
ここをクリアすれば、Project VBAの基本はバッチリですよ。
さあ、自信を持って次の自動化に挑戦してください!応援しています。
