ネットワーク越しにProjectを制御する――「ゆらぎ」を克服する極限のオープン実装
Project VBAを扱っていると、必ず直面する壁がある。それが「ネットワークの不安定さ」だ。特にNASやファイルサーバー上の`.mpp`ファイルを直接操作する場合、パケットの欠落や接続断は致命的なエラーを招く。
多くのエンジニアは単に `Projects.Open` を叩いてエラーハンドリングを怠るか、せいぜい `On Error Resume Next` で誤魔化す。しかし、真のアーキテクトは「接続のゆらぎ」を前提とした堅牢なステートマシンを構築する。
本稿では、不安定なネットワーク環境下において、ローカルキャッシュとの同期を意識した再試行ロジックの極意を伝授する。
—
1. なぜ単なる「再試行」では不十分なのか
単にループで `Open` を繰り返すだけでは、Projectのバックグラウンドプロセスがゾンビ化し、メモリリークを誘発する。特にCOMオブジェクトの解放が不完全な状態で再試行を繰り返せば、ハンドルの枯渇は時間の問題だ。
我々が実装すべきは、以下の3要素を完備した処理系である。
- 指数バックオフ戦略: 接続失敗時に待機時間を段階的に増やす。
- オブジェクトの明示的破棄: `Set obj = Nothing` だけでは足りない。`FinalReleaseComObject` を意識せよ。
- ローカルキャッシュへのフォールバック: サーバーが沈黙しているなら、直近の同期ファイルを読みに行く。
—
2. 実装コード:堅牢なプロジェクトオープン・エンジン
以下に、Windows APIを利用したミリ秒単位の制御と、オブジェクトライフサイクルを管理した再試行ロジックを示す。
Option Explicit
‘ メモリ解放の確実性を担保するためのAPI宣言
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Public Function OpenProjectRobust(ByVal filePath As String, ByVal localCachePath As String) As Project
Dim proj As Project
Dim retryCount As Integer
Dim maxRetries As Integer: maxRetries = 5
Dim delay As Long: delay = 1000 ‘ 初期待機時間(ms)
Do While retryCount < maxRetries On Error Resume Next Set proj = Application.Projects.Open(filePath) If Err.Number = 0 Then On Error GoTo 0 Set OpenProjectRobust = proj Exit Function End If ' ログ出力やエラーハンドリング Debug.Print "接続失敗: 再試行中... (" & retryCount + 1 & ")" ' COMオブジェクトのクリーンアップを試みる If Not proj Is Nothing Then proj.Close pjDoNotSave Set proj = Nothing End If ' 指数バックオフによる待機 Sleep delay delay = delay 2 retryCount = retryCount + 1 On Error GoTo 0 Loop ' 最終手段:ローカルキャッシュからの復旧 Debug.Print "サーバー応答なし。ローカルキャッシュからロードを試みます。" Set OpenProjectRobust = Application.Projects.Open(localCachePath) End Function ---
3. シニアエンジニアが守るべき「メモリの作法」
上記のコードで最も重要なのは、「エラーが発生した瞬間に、生成しかけたオブジェクトを即座に葬り去る」という規律だ。
Project VBAはバックグラウンドで `msproject.exe` のインスタンスを操作している。中途半端な状態で処理を中断すると、COMの参照カウントが正しくデクリメントされず、次に同じファイルを開こうとした際に「ファイルが既に開かれています」という偽のエラーに遭遇することになる。
回避すべきアンチパターン
- 不透明なオブジェクトの放置: ループ内で `Set proj = Nothing` を忘れると、メモリ上にゴミが蓄積し、Projectの動作が極端に重くなる。
- 同期的なIOの過信: ネットワークドライブへのアクセスは常に「非同期」であると仮定すべきだ。APIによる待機時間を挟まない再試行は、サーバー側へのDDoS攻撃と見なされる可能性がある。
—
4. システム間連携における極限の知見
もし、これがエンタープライズなシステム連携の一部であれば、ファイルオープン前に必ず `FileSystemObject` を使った「排他制御の確認」を先んじて行うべきだ。
‘ ファイルが他のプロセスによってロックされていないかを確認する関数
Public Function IsFileAvailable(filePath As String) As Boolean
Dim fso As Object
Dim ts As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
On Error Resume Next
Set ts = fso.OpenTextFile(filePath, 1, False) ‘ Read Onlyで開けるかテスト
If Err.Number = 0 Then
IsFileAvailable = True
ts.Close
Else
IsFileAvailable = False
End If
On Error GoTo 0
Set ts = Nothing
Set fso = Nothing
End Function
この「オープン前の事前チェック」をパイプラインに組み込むことで、Projectのメインプロセスがフリーズするリスクを極限まで排除できる。
—
最後に:レガシーを飼い慣らす覚悟
Project VBAはレガシーかもしれない。しかし、その裏にあるCOMインターフェースは、正しく扱えば今なお最高峰の安定性を誇る。
「コードが動くこと」と「コードが壊れないこと」の間には、深淵のような隔たりがある。再試行処理を実装することは、単なるエラー対策ではない。過酷なネットワーク環境という「現実」を、システムの「設計」の中に組み込むという意志表示なのだ。
今日からあなたのコードに、この堅牢な息吹を吹き込んでほしい。それがプロフェッショナルの仕事というものだ。
