共有環境の「泥沼」を回避する:VBAによる堅牢な排他制御とロックファイルの深淵
大規模なプロジェクト管理において、Project VBA(MS Project)をネットワーク共有ドライブに配置し、複数ユーザーが「同時に」触れることの恐怖を、君たちは知っているはずだ。Microsoft Projectの標準機能による排他制御は、ネットワーク遅延やプロセスの異常終了に対してあまりに無力だ。
今日は、標準機能が提供する「甘い」ロック機構に依存せず、OSのファイルシステムレベルで強固な排他制御を実装する方法を伝授する。これは小手先のテクニックではない。システムがクラッシュしてもデータの整合性を守り抜くための、アーキテクトの必修科目だ。
—
1. ロックファイルの生存戦略:隠蔽とアトミック性
共有サーバー上に単なるテキストファイルを置くだけでは不十分だ。プロセスが異常終了した際、その「ゾンビロック」が永遠にシステムを停止させる。
我々が実装すべきは、「PID(プロセスID)を書き込んだロックファイル」の生存確認と、`FileSystemObject`を用いたアトミックな作成・削除ロジックだ。
実装の核となる概念
- アトミックな作成: ファイルの存在確認と作成を分割してはならない(競合の隙間が生じるため)。`Open … For Output As #1` は排他モードでファイルを開くため、これを活用する。
- 生存確認: ロックファイル内に書き込まれたPIDが、現在のクライアント環境で稼働しているかを確認する。
—
2. 極限の排他制御ロジック(VBA実装)
以下のコードは、Windows APIを駆使してプロセス生存確認を行い、ロックファイルの整合性を担保する。
Option Explicit
‘ Windows API: プロセスの存在確認に使用
Private Declare PtrSafe Function OpenProcess Lib “kernel32” (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib “kernel32″ (ByVal hObject As LongPtr) As Long
Private Const PROCESS_QUERY_LIMITED_INFORMATION = &H1000
”’
”’
Public Function TryAcquireLock(lockFilePath As String) As Boolean
Dim fso As Object
Dim fileNum As Integer
Dim pid As Long
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 既にロックが存在する場合、ゾンビファイルかを確認
If fso.FileExists(lockFilePath) Then
If IsLockAlive(lockFilePath) Then
MsgBox “現在、別のユーザーが編集作業中です。”
TryAcquireLock = False
Exit Function
Else
‘ ゾンビファイルを削除
Kill lockFilePath
End If
End If
‘ アトミックに作成(排他ロックで開く)
fileNum = FreeFile
Open lockFilePath For Output Lock Write As #fileNum
Print #fileNum, CStr(GetCurrentProcessId())
Close #fileNum
TryAcquireLock = True
Set fso = Nothing
End Function
”’
”’
Private Function IsLockAlive(lockPath As String) As Boolean
Dim fileNum As Integer
Dim pid As Long
Dim hProcess As LongPtr
fileNum = FreeFile
Open lockPath For Input As #fileNum
Input #fileNum, pid
Close #fileNum
‘ プロセス生存確認
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid)
If hProcess <> 0 Then
CloseHandle hProcess
IsLockAlive = True
Else
IsLockAlive = False
End If
End Function
—
3. シニアエンジニアが意識すべき「メモリの行儀」
VBAはガベージコレクションが甘い。特にネットワークIOが絡む処理では、オブジェクトの解放タイミングが致命的なメモリリークを招く。
1. オブジェクトの明示的破棄: `Set obj = Nothing` はサブルーチンの最後で必ず行うこと。特に`FileSystemObject`や`ADODB.Stream`等のリソースは、OS側のハンドルを握り続けるため、ループ内でインスタンス化してはならない。
2. APIハンドルの解放: `OpenProcess` で取得したハンドルは、必ず `CloseHandle` で閉じること。これを怠ると、クライアントPCのGDI/Userオブジェクトが枯渇し、システムが不安定になる。
—
4. 運用のための「バックドア」
完全に強固なロックを組むと、自分がPCをフリーズさせた時にデバッグができなくなる。必ず「強制解除ボタン」を隠し持っておくことだ。
- 管理者モード: 特定のユーザー(または特定のレジストリキーが存在する環境)のみ、他人のロックファイルを強制削除できるメソッドを追加しておく。
- 監査ログ: ロック作成時にサーバー上のログファイルに「いつ・誰が・どのプロジェクトを・どのPIDで」ロックしたかを追記するロジックを噛ませる。これはトラブルシュート時の命綱となる。
最後に:アーキテクチャの本質
VBAでこれを実装することに「レガシーだ」と嘲笑する者もいるだろう。だが、現場のネットワーク環境において、「今、目の前にある不安定な共有ドライブ」でいかにビジネスを止めないかを考えることこそが、真のエンジニアリングだ。
このロック機構を組み込めば、君の管理するProject VBAシステムは、単なるスクリプトの集まりから、堅牢な排他制御を備えた「システム」へと昇華する。健闘を祈る。
