【実務・中級編】【上級者向け】大規模プロジェクトにおける「画面更新停止」と「計算モード制御」によるVBA高速化の極意 – Project VBA解析バイブル

スポンサーリンク

【上級者向け】大規模プロジェクトにおける「画面更新停止」と「計算モード制御」によるVBA高速化の極意

開発現場でMS ProjectのVBA(Project VBA)を扱い、数千から数万行に及ぶWBS(タスク階層構造)の生成や、複雑な前提条件・依存関係の自動設定ツールを構築した者なら、誰もが一度は絶望する瞬間がある。

「……終わらない。プログレスバーがまったく進まない」

1件ずつタスクを追加し、リンクを張り、カスタムフィールドを書き換えるたびに、Projectの計算エンジンが走り、画面が再描画され、Undoバッファが肥大化していく。素朴なループ処理で数万タスクを処理させれば、コーヒーを飲み干しても終わらず、最悪の場合はメモリリークや「応答なし」による強制終了の憂き目に遭う。

今回は、Project VBAの内部アーキテクチャ(オブジェクトのライフサイクルと計算エンジンの挙動)を極限までハックし、大規模WBSの構築・更新を「瞬殺」するための実行環境制御テクニックを伝授する。

—

1. なぜMS ProjectのVBAは遅いのか?(根本原因の解明)

多くのプログラマが陥る罠は、Excel VBAの感覚のままProject VBAを書いてしまうことだ。
Excelにとってのセルは単なるデータコンテナだが、MS Projectのタスク(Task)は、スケジュール計算エンジン、リソース割り当て、制約条件の検証、そしてUIのガントチャート描画と密に結合された「重厚なオブジェクト」である。

デフォルトの状態では、VBAから1つのタスクプロパティ(例: `Task.Start` や `Task.Predecessors`)を変更するたびに、以下の処理が自動実行される。

1. 全体スケジュールの再計算(クリティカルパスの再導出、依存関係の伝播)
2. 画面(UI)の再描画(ガントチャート、リソースグラフの更新)
3. データベース/Undoスタックへの状態同期

数万行のタスクに対しこれを繰り返せば、CPU時間の90%以上が無駄な再計算と描画に費やされるのは当然の理である。

—

2. 爆速化の三種の神器:実行環境制御のアーキテクチャ

この無慈悲なオーバーヘッドを断ち切るためには、VBAの実行前にプロジェクトの「神経系」を一時的に麻痺させ、処理完了後に一括して覚醒させるアプローチを取る。

具体的には、以下の3つの制御を同時に行う。

1. 画面更新の完全停止 (`ScreenUpdating = False`)

  • UIの再描画を完全にシャットアウトする。

2. 計算モードの手動化 (`Calculation = pjManual`)

  • 自動スケジュール・依存関係の連鎖計算を停止し、バッチ処理化する。

3. エラー・イベントハンドリングの堅牢化

  • 処理途中で異常終了した場合でも、必ず環境が元の状態に復帰する「トランザクション構造」の担保。

—

3. 【プロダクションコード】堅牢かつ超高速なWBS一括生成テンプレート

以下のコードは、数万件規模のタスク追加・依存関係設定を行う際に用いる、実務レベルの完全なテンプレートである。
「例外が発生しても確実に環境を元に戻す(Failsafe)」設計を取り入れているため、そのまま現場のツールに組み込んでほしい。

Option Explicit

‘ ==============================================================================
‘ 処理名: 大規模WBS一括生成・依存関係設定プロシージャ
‘ 概要 : 計算モードと画面更新を完全に抑制し、数万タスクの構築を高速化する
‘ ==============================================================================
Sub ExecuteHighSpeedWBSGeneration()
Dim startTime As Double
startTime = Timer

‘ — 1. 実行前の環境退避と安全装置 —
Dim originalScreenUpdating As Boolean
Dim originalCalculation As Long

On Error GoTo ErrorHandler

‘ アプリケーションの動作をロック(パフォーマンス最優先モードへ移行)
originalScreenUpdating = App.ScreenUpdating
originalCalculation = Application.Calculation

App.ScreenUpdating = False
Application.Calculation = pjManual ‘ 計算エンジンを手動モードに切り替え

‘ ユーザーへの余計なプロンプトを抑制
App.DisplayAlerts = False

‘ ==============================================================================
‘ — 2. メイン処理(ここに数万件のタスク追加・依存関係設定を書く) —
‘ ==============================================================================
Debug.Print “WBS構築を開始…”

Call GenerateMassiveTasks(5000) ‘ 例: 5000件のタスクと階層・リンクを生成

‘ ==============================================================================

‘ — 3. 処理完了後のファイナライズ —
‘ 計算モードを戻す前に、一度強制的にプロジェクト全体を再計算させる
Application.Calculation = pjAutomatic
App.CalculateAll

‘ 環境を元通りに復元
App.ScreenUpdating = originalScreenUpdating
App.DisplayAlerts = True

MsgBox “処理が正常終了しました。 実行時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, vbInformation, “完了”
Exit Sub

ErrorHandler:
‘ — 異常系ハンドリング(確実に環境を復旧させる) —
Dim errDesc As String
errDesc = Err.Description

‘ 復旧処理中にエラーが起きても無視して復元を試みる
On Error Resume Next
App.ScreenUpdating = originalScreenUpdating
Application.Calculation = pjAutomatic
App.DisplayAlerts = True
On Error GoTo 0

MsgBox “致命的なエラーが発生しました。” & vbCrLf & errDesc, vbCritical, “エラー”

End Sub

‘ ==============================================================================
‘ 内部プロシージャ: タスクのモック生成と依存関係の構築
‘ ==============================================================================
Private Sub GenerateMassiveTasks(ByVal totalTasks As Long)
Dim i As Long
Dim t As Task
Dim prevTask As Task

‘ アクティブプロジェクトのタスクコレクション
Dim tskColl As Tasks
Set tskColl = ActiveProject.Tasks

For i = 1 to totalTasks
‘ タスクの追加
Set t = tskColl.Add(“タスク ” & i)

‘ 期間の設定(計算モードが手動なので、ここではスケジュールは再計算されない)
t.Duration = “8d”

‘ 階層構造(WBSインデント)の付与例(5つごとに階層を下げる)
If i Mod 5 = 1 And i > 1 Then
t.OutlineIndent
End If

‘ 前提条件(依存関係)の動的設定例(FS関係)
If Not prevTask Is Nothing Then
‘ Predecessorsコレクションへの追加
t.Predecessors.Add PrevTask.UniqueID & “FS”
End If

Set prevTask = t

‘ 進捗をイミディエイトウィンドウに出力(必要に応じてプログレスバーへ連携)
If i Mod 500 = 0 Then
Debug.Print i & ” 件処理完了…”
End If
Next i

End Sub

—

4. プロダクション環境における「絶対の注意点」

このアプローチは劇的な高速化をもたらすが、一歩誤ると深刻なデータ破損やデバッグ困難なバグを生む。チーフアーキテクトとして、以下の「罠」を厳に戒めておく。

① `Application.Calculation = pjAutomatic` の戻し忘れ

処理の途中でエラーが発生し、`ErrorHandler` を経由せずにVBAが中断された場合、MS Project全体が「手動計算モードのまま」凍結される。ユーザーが手動でタスクを追加してもスケジュールが一切動かなくなるため、現場は大パニックに陥る。
必ず `On Error GoTo ErrorHandler` によるトランザクション保証を実装すること。

② 依存関係(Link)追加時のプレフィックスの罠

`t.Predecessors.Add` を用いる際、タスクの指定には `ID` ではなく `UniqueID` を使用することを強く推奨する。
通常の `ID` は行の並び替えや削除によって動的に変動するため、ループ内でタスクを生成・操作する文脈では破綻しやすい。`UniqueID` は永続的かつユニークであるため、依存関係の自動構築においては常にキーとして信頼できる。

③ 画面更新停止中の「ユーザーインタラクション」の排除

`ScreenUpdating = False` の間は、ユーザーに対してメッセージボックス(`MsgBox`)やインプットボックスを出してはならない。画面がフリーズしたまま背後でダイアログモーダル待ちになり、VBAが完全にハングアップしたように見える。警告や確認は必ず処理の「前」か「後」に完了させること。

—

5. 総括

大規模プロジェクトのVBA開発において、コードの美しさやアルゴリズムの効率性と同じくらい重要なのが、「ホストアプリケーションの実行コンテキストの支配」である。

今回紹介した「画面更新の停止」と「計算モードの制御」は、単なる小手先のテクニックではなく、Projectという巨大な計算エンジンをVBAから手懐けるための必須の作法である。

数万行のWBSを秒単位で構築するツールをあなたの手で完成させ、現場のエンジニアたちを圧倒してほしい。

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