【排他制御】ロックファイルを利用したVBScriptの二重起動防止(多重実行ガード)パターンの実装
業務の自動化において、VBScriptとWSH(Windows Script Host)はいまだに現場のインフラとして強力な選択肢だ。インストール不要で動き、OSの深部にまでアクセスできる。
しかし、ここで開発者が必ず直面する「呪い」がある。
それが「タスクスケジューラの重複実行」や「ユーザーの連打による二重起動」だ。
処理が終わっていないにもかかわらず同じスクリプトがもう一本走り出した瞬間、ファイルへの同時書き込みによるロック競合(Permission Denied)、データベースのデッドロック、そして何より出力データの破損という致命的な災厄が幕を開ける。
今回は、この悪夢を完全に断ち切るための「ロックファイルを用いた堅牢な排他制御(多重実行ガード)パターン」を、プロダクションコードと共に授けよう。
—
なぜ「プロセス数チェック」では不十分なのか?
世の中の解説記事を見ると、「WMIを使って実行中のプロセス数を数えろ」というアプローチが散見される。確かに一見スマートに見えるが、プロの現場においては悪手だ。
‘ 【アンチパターン】プロセス名で二重起動を検知する愚行
Dim colProcesses, objProcess, count
count = 0
Set colProcesses = GetObject(“winmgmts:\\.\root\cimv2”).ExecQuery(“Select from Win32_Process Where Name = ‘cscript.exe'”)
‘ ここで自プロセスの判定や引数の判定が曖昧になり、誤検知や抜け穴が生じる
このアプローチには以下の致命的な欠陥がある。
1. WMIのコストが重い: 実行のたびにWMIクエリを投げるのはオーバーヘッドが大きすぎる。
2. 同一スクリプトの区別がつかない: 別の目的で動いている `cscript.exe` まで巻き込んで誤判定するか、同じVBScriptを別名で同時に動かした際に検知できない。
3. タイムラグの存在: プロセスが起動し、WMIに登録されるまでのコンマ数秒の間に「隙」が生まれる。
プロの結論:ファイルシステムのアトミック(不可分)な排他制御を使え。
「ファイルを作成する」というWindowsの基本動作の排他性を利用するのが、最も軽快かつ確実なアプローチである。
—
堅牢な排他制御パターンの全体像
今回実装するパターンのライフサイクルは以下の通りだ。
1. 起動: スクリプト開始時に専用の「ロックファイル(`.lock`)」の生成を試みる。
2. 判定: 生成に成功すれば(=誰も使っていなければ)、処理続行。失敗すれば(=先客がいれば)、即座にログを残して安全に終了(Exit)。
3. 解放: スクリプトの終了時(正常終了・異常終了を問わず)、必ずロックファイルを削除する。
ここで最も重要なのが、「異常終了時(エラー発生時)のクリーンアップ」だ。`On Error Resume Next` を駆使し、どんなエラーでスクリプトがクラッシュしようとも、確実にロックが解除される仕組みを構築しなければ、次回の実行が永久に阻害される「デッドロック状態」に陥る。
—
【コピペ即運用可能】プロダクションコード
以下のコードは、エラーハンドリング、ロギング、そして確実なクリーンアップのライフサイクルを完全に網羅した、実務仕様のVBScriptテンプレートである。
Option Explicit
‘ ==============================================================================
‘ スクリプト名: SecureJobRunner.vbs
‘ 概要: ロックファイルによる厳密な排他制御を備えたバッチ処理テンプレート
‘ ==============================================================================
Dim objFSO, strScriptDir, strLockFilePath, strLogFilePath
Dim objLockFile, objLogFile
Dim blnLocked
‘ オブジェクトの初期化
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
strScriptDir = objFSO.GetParentFolderName(WScript.ScriptFullName)
‘ パスの定義(スクリプトと同じ場所に作成)
strLockFilePath = strScriptDir & “\process.lock”
strLogFilePath = strScriptDir & “\execution.log”
blnLocked = False
‘ メイン処理の実行(エラー監視下で行う)
On Error Resume Next
‘ ——————————————————————————
‘ 1. 排他制御(ロックファイルの排他作成)
‘ ——————————————————————————
‘ CreateTextFileの第2引数(Overwrite)を False にすることで、
‘ すでにファイルが存在する場合はエラー(Err.Number <> 0)が発生する仕組みを利用する。
Set objLockFile = objFSO.CreateTextFile(strLockFilePath, False)
If Err.Number <> 0 Then
‘ ロックファイルの作成に失敗 = すでに別のプロセスが実行中
Call WriteLog(“【警告】すでに別のプロセスが実行中のため、処理を中断します。”)
WScript.Quit 1
End If
‘ ロック取得成功
blnLocked = True
Err.Clear ‘ エラー情報をクリア
‘ ロックファイルに自身のプロセス情報を書き込んでおく(保守性向上)
objLockFile.WriteLine “Locked at: ” & Now & ” | PID/Session: ” & WScript.ScriptName
objLockFile.Close
Set objLockFile = Nothing
‘ ——————————————————————————
‘ 2. メインビジネスロジック
‘ ——————————————————————————
Call WriteLog(“【情報】メイン処理を開始します。”)
‘ — ここに実際の業務処理(Excel操作、API連携、ファイルコピー等)を記述 —
‘ サンプルとして5秒間スリープする擬似処理
WScript.Sleep 5000
‘ ————————————————————————
Call WriteLog(“【情報】メイン処理が正常に完了しました。”)
‘ ——————————————————————————
‘ 3. 終了処理 & クリーンアップ(finallyブロックの概念)
‘ ——————————————————————————
Cleanup:
‘ エラーが発生していなくても、中断ルートからでも必ずここを通る
If blnLocked Then
If objFSO.FileExists(strLockFilePath) Then
objFSO.DeleteFile strLockFilePath, True
Call WriteLog(“【情報】ロックファイルを正常に解放しました。”)
End If
End If
‘ オブジェクトの開放
Set objFSO = Nothing
‘ スクリプトの終了
If Err.Number <> 0 Then
WScript.Quit 99
Else
WScript.Quit 0
End If
‘ ==============================================================================
‘ 補助関数: ログ出力関数
‘ ==============================================================================
Sub WriteLog(strMessage)
Dim f
On Error Resume Next
Set f = objFSO.OpenTextFile(strLogFilePath, 8, True) ‘ 8 = ForAppending
f.WriteLine “[” & Now & “] ” & strMessage
f.Close
Set f = Nothing
End Sub
—
アーキテクトからの重要な実装アドバイス
このコードを実務に投入する際、さらに運用リスクヘッジを高めるための知見を共有しよう。
1. 「ゾンビロック」への対策(ハングアップ時の自爆装置)
もしスクリプトが無限ループに陥ったり、外部APIの応答待ちでフリーズした場合、ロックファイルが永遠に残るため、翌日以降のタスクもすべてスキップされる事態が発生する。
これを防ぐため、「ロックファイルの作成日時が現在時刻より2時間以上古い場合は、強制的に削除して処理を続行する」といったタイムアウト判定を冒頭に組み込む設計に昇華させると、システムは完全に「自律稼働型」となる。
2. ネットワークドライブ上での運用注意点
NASやファイルサーバー共有フォルダ上でこのVBScriptを動かす場合、SMBプロトコルの仕様上、ファイルロックの反映にコンマ数秒のタイムラグや、ファイル共有競合エラー(`Permission Denied`)が発生しやすい。
排他制御を行うスクリプトは、極力ローカルディスク(`C:\Automation\` など)上で実行し、成果物だけをサーバーに転送するアーキテクチャを強く推奨する。
—
まとめ
VBScriptはレガシーと言われることもあるが、こうした「枯れた技術ならではの堅牢な設計手法」を正しく適用すれば、ミドルウェアの導入すら不要で、インフラの堅牢な自動化基盤を作り上げることができる。
二重起動によるデータ破損というリスクに怯える日々は、今日で終わりにしよう。アトミックなファイルロックの思想をあなたのスクリプトに組み込み、真に信頼できるプロフェッショナルな自動化を実現してほしい。
