計算エンジンが「嘘」をつく前に。MS Project VBAにおける再計算状態の完全掌握術
エンジニア諸君、現場で動いている「動けばいいだけのツール」に、どれだけの命運を預けているか?
Microsoft Projectを操作するVBA開発において、最も致命的なバグは、構文エラー(Syntax Error)ではない。「計算エンジンが古いデータのまま、正常な値を返している」という、静かなるデータの不整合だ。
プロジェクトの計算設定(Calculation Mode)が「手動(Manual)」に設定されているとき、君がコードでタスクの期間(Duration)や制約(Constraints)を書き換えたとしても、スケジュールの整合性は一切保たれない。終了日(Finish Date)も、クリティカルパスも、リソースの負荷も、すべては「過去の遺物」だ。
その状態で、もし君のツールがその「嘘のデータ」を基にデータベースへ書き込みを行ったり、進捗報告書を出力したりしたらどうなるか? プロジェクトマネージャーは誤った意思決定を下し、現場は大混乱に陥る。
今回は、プロフェッショナルとして絶対に避けるべき「計算状態の不一致」を防ぐため、計算エンジンの状態を監視し、ユーザーに安全な実行を強いる「ガード・パターン」の設計思想を伝授する。
1. なぜ「Application.Calculate」を乱用してはいけないのか
初心者は、計算モードが手動であることを検知すると、即座に `Application.Calculate` を呼び出す。
「とりあえず計算させればいいんだろ?」という思考だ。これは極めて怠慢であり、パフォーマンスを無視した設計だ。
大規模プロジェクト(タスク数が数千、数万に及ぶもの)において、`Calculate` メソッドは極めて重い処理だ。コードの至る所にこれを埋め込めば、ツールは「重くて使い物にならない」と切り捨てられる。
真のアーキテクトは、以下の3点を峻別する。
1. 検知 (Detection): 現在の計算モードが「手動」であるかを判定する。
2. 判断 (Decision): 再計算が必要な操作(期間、制約、リソースの変更)を行おうとしているかを判断する。
3. 介入 (Intervention): ユーザーに再計算の是非を問い、安全な状態へ遷移させる。
2. 堅牢な設計思想:ガード・パターンの導入
業務アプリケーションを設計する際、ビジネスロジック(計算や更新)を直接呼び出すのではなく、必ず「状態のバリデーション層」を挟むべきだ。
具体的には、以下のような階層構造を持たせる。
1. UI/Trigger層: ボタンクリックなどのイベント。
2. Guard層 (本稿の核心): `Application.Calculation` をチェックし、不適切な状態ならユーザーに警告、または再計算を促す。
3. Logic層: 実際のデータ更新処理。
この設計により、Logic層は「常に計算済みの正しいデータが存在する」という前提で記述でき、コードの複雑性が劇的に低下する。
3. 実装例:プロダクション・クオリティの監視モジュール
以下に、そのまま業務ツールに組み込めるレベルの、保守性の高いコード例を示す。単なる関数ではなく、エラーハンドリングとユーザーへの通知を統合した設計にしている。
Option Explicit
' ==============================================================================
' Module: modProjectGuard
' Description: MS Projectの計算状態を監視し、データの整合性を保証するガードモジュール
' ==============================================================================
''' <summary>
''' プロジェクトの計算モードが安全(自動計算)であることを確認する。
''' 手動計算モードの場合は、ユーザーに再計算の実行を促す。
''' </summary>
''' <returns>安全な状態(自動計算またはユーザーが再計算に同意した)であればTrue</returns>
Public Function EnsureCalculationIntegrity() As Boolean
On Error GoTo ErrorHandler
Dim app As MSProject.Application
Set app = MSProject.Application
' 1. 現在の計算モードを確認
' pjManual = 0, pjAutomatic = 1
If app.Calculation = pjAutomatic Then
EnsureCalculationIntegrity = True
Exit Function
End If
' 2. 手動モードの場合、ユーザーに介入を求める
Dim response As VbMsgBoxResult
response = MsgBox( _
"【警告】現在のプロジェクトは「手動計算モード」に設定されています。" & vbCrLf & vbCrLf & _
"データの整合性を保つため、計算を実行しますか?" & vbCrLf & _
"「いいえ」を選択した場合、古いデータに基づいた処理が行われる可能性があります。", _
vbExclamation + vbYesNo + vbDefaultButtonYes, _
"計算状態の不一致を検知")
If response = vbYes Then
' 3. 再計算の実行(重い処理であることを考慮し、ログ等を出せればベスト)
Debug.Print "Calculation triggered by user intervention at: " & Now
app.Calculate
EnsureCalculationIntegrity = True
Else
' ユーザーが拒否した場合、処理を中断させる
Debug.Print "User declined recalculation. Proceeding with risk."
EnsureCalculationIntegrity = False
End If
Exit Function
ErrorHandler:
MsgBox "計算状態の確認中に予期せぬエラーが発生しました: " & Err.Description, vbCritical
EnsureCalculationIntegrity = False
End Function
''' <summary>
''' 実際の業務ロジックを実行するためのラッパー関数
''' </summary>
Public Sub ExecuteSafeBusinessLogic(ByRef logicRoutine As Variant)
' ガード層によるチェック
If EnsureCalculationIntegrity() Then
' 安全が確認された場合のみ、ロジックを実行
' logicRoutine は CallByName や Execute を利用して実行可能
On Error Resume Next
Application.Run logicRoutine
If Err.Number <> 0 Then
MsgBox "業務ロジックの実行中にエラーが発生しました: " & Err.Description, vbCritical
End If
On Error GoTo 0
Else
MsgBox "処理を中断しました。計算状態が不適切です。", vbInformation, "中断"
End If
End Sub
使い方(呼び出し側)
' --- ユーザーがボタンを押した時のイベント ---
Public Sub btnUpdateDuration_Click()
' 直接ロジックを書かず、ラッパー経由で呼び出す
' ここでは例として、"MyCoreLogic" という名前のサブプロシージャを呼び出す
ExecuteSafeBusinessLogic "MyCoreLogic"
End Sub
' --- 実際の業務ロジック(ここには「計算されていること」を前提としたコードを書く) ---
Public Sub MyCoreLogic()
' 計算が保証されているため、安心して期間を変更できる
Dim t As Task
Set t = ActiveProject.Tasks(1)
t.Duration = "10d"
' ここで t.Finish が自動的に更新されることを期待して処理を進められる
Debug.Print "Task Finish Date: " & t.Finish
End Sub
4. プロフェッショナルとしての注意点:外部連携の罠
この仕組みを実装する際、さらに一歩先の設計として考慮すべき点が2つある。
A. データベース連携時の「トランザクション」
もし君のツールが、ProjectのデータをSQL ServerやAccessなどの外部データベースへ同期する機能を持っているなら、`EnsureCalculationIntegrity` は「絶対条件」だ。
手動計算モードのままDBに書き込むことは、汚染されたデータをマスターに流し込むことに他ならない。DBへの書き込み処理(`INSERT/UPDATE`)の直前には、必ずこのガードを配置せよ。
B. ネットワーク越し、あるいはアドインとしての挙動
アドイン(COM Add-in)として配布する場合、ユーザーが自身の判断で計算モードを切り替える可能性がある。
「自分のコード内で設定を `pjAutomatic` に強制的に書き換える」という実装も一つの手だが、これはユーザーの環境設定を侵害する行為だ。
「設定を勝手に変えるのではなく、状態を監視し、ユーザーに選択権を与える」。この礼節こそが、現場で信頼されるツールを生む。
まとめ:コードの「正しさ」は「前提条件」の管理にある
「計算が終わっていること」を前提にロジックを書くのは簡単だ。しかし、その前提が崩れた瞬間にシステムは崩壊する。
1. 状態を疑え。
2. ガード層を分離せよ。
3. ユーザーに責任と選択権を与えよ。
この3原則を徹底することで、君の作るVBAツールは、単なる「便利なスクリプト」から、プロフェッショナルが安心して使える「業務基盤」へと進化する。
健闘を祈る。
