プロジェクトの死を回避せよ:MS Project VBAにおける「原子性」を担保するトランザクション制御の極意
現場のプロジェクトマネージャーや自動化エンジニア諸君。君たちは「保存処理の失敗」という悪夢に直面したことがあるか?
MS Project(MSP)の保存処理は、単純な`FileSave`メソッドではない。大規模なプロジェクトファイルになればなるほど、I/Oのレイテンシやネットワークの瞬断、あるいはアドインによる干渉で、ファイルが半壊した状態で保存されるリスクが常につきまとう。
本稿では、VBA単体では到達不可能な「保存のトランザクション制御」を、COMアドインの視点から実装する極限の設計論を授ける。
—
1. なぜ「標準の保存」では不十分なのか
通常のVBAコードで`ActiveProject.Save`を実行するだけでは、以下のリスクを制御できない。
- 非アトミックな書き込み: 保存プロセス中にプロセスがクラッシュした場合、MSPは「部分的に書き込まれたゴミファイル」を残して終了する。
- 整合性の欠如: 外部データベースや他のアドインと連携している場合、片方の更新だけが成功し、プロジェクトファイルとデータの不整合が発生する。
我々が目指すべきは、「すべてが成功するか、あるいは何も起きなかったことにするか」という、データベーストランザクションのような堅牢性である。
—
2. アーキテクチャの設計思想:シャドウコピー戦略
「保存に失敗したらロールバックする」という理想をVBAで実現するためには、直接ファイルを上書きするのではなく、以下の手順を踏む必要がある。
1. ステージング: 現在のプロジェクトを「一時的な作業用ファイル」として別名保存する。
2. 検証: 作業用ファイルが正しく保存されたことを確認する。
3. スイッチ: 作業用ファイルを元のファイル名にリネーム(上書き移動)する。
4. クリーンアップ: 成功時は一時ファイルを削除、失敗時は例外をキャッチして元の状態を維持する。
—
3. 実装:堅牢なトランザクション制御プロシージャ
以下に、実務でそのまま運用可能な、エラーハンドリングを完備した設計を示す。
‘ @brief プロジェクトを安全に保存するためのトランザクション・ラッパー
‘ @param targetProject 保存対象のProjectオブジェクト
‘ @param targetPath 保存先のフルパス
Public Sub SafeSaveProject(ByRef targetProject As Project, ByVal targetPath As String)
Dim tempPath As String
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 一時ファイル名(作業用)の生成
tempPath = targetPath & “.tmp”
On Error GoTo ErrorHandler
‘ 1. ステージング:一時ファイルへ保存
‘ 保存処理が失敗した場合、ここで即座にエラーハンドラへ飛ぶ
targetProject.SaveAs Name:=tempPath
‘ 2. スイッチ:一時ファイルを正式なパスへ移動(上書き)
‘ FileSystemObjectのMoveFileはOSレベルでアトミックに近い動作を行う
fso.MoveFile tempPath, targetPath
Debug.Print “保存完了: ” & targetPath
Exit Sub
ErrorHandler:
‘ 3. ロールバック:一時ファイルが残っていれば削除
If fso.FileExists(tempPath) Then
fso.DeleteFile tempPath
End If
MsgBox “保存プロセスで致命的なエラーが発生しました。データは更新されていません。” & vbCrLf & _
“エラーコード: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “トランザクション失敗”
Err.Clear
End Sub
—
4. 運用上の極めて重要な注意点
このコードをただコピペするだけでは、真のエンジニアとは言えない。以下の「境界条件」を必ず考慮せよ。
- 排他制御の罠: プロジェクトが既に他のユーザーによって開かれている場合、`MoveFile`は失敗する。`fso.FileExists`のチェックだけでなく、ファイルがロックされていないかを事前に検証するロジック(`Open … For Output Lock Read Write` を試行する等)を組み込むことが理想だ。
- COMアドインのライフサイクル: この処理をアドインの`BeforeSave`イベントでフックする場合、`Cancel = True`を設定し、手動で上記の`SafeSaveProject`を回すこと。MSP標準の保存プロセスと競合させてはならない。
- ログ出力: 失敗時は必ずイベントビューアやログファイルへ「どのタイミングで」「何が原因で」失敗したかを書き出すこと。事後検証できないシステムは、存在しないのと同じだ。
—
5. 最後に:プロフェッショナルの矜持
ツールを作ることは、誰かの業務の「安全」を預かることだ。
VBAは簡便な言語だが、その裏側にはOSのI/O管理やメモリの制約が深く関わっている。プロジェクトファイルの保存は、システムの心臓部だ。そこを安易なコードで済ませるな。
君たちの書くコードが、明日の誰かの仕事を守る盾となることを期待している。
もし更なる高度なアドイン設計(C# / .NET連携による非同期保存)が必要なら、いつでも扉を叩け。道は用意してある。
