【入門編】【重複起動排他制御】WMI Win32_Process と起動パラメータ比較によるスクリプトの多重実行ガード – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!日々の業務自動化、本当にお疲れ様です。
VBScriptを使ってファイル整理やシステム連携のバッチファイルを組んでいると、ふとこんな壁にぶつかりませんか?

「処理が終わっていないのに、タスクスケジューラやユーザーの誤操作で同じスクリプトが二重に起動し、ファイルがロックされてエラーになった……」

現場でよくあるこの悲劇。これを完璧に防ぐのが、今回解説する「重複起動の排他制御」です。

今回は、単に「名前が同じプロセスを全部殺す乱暴な方法」ではなく、「自分自身の起動パラメータ(引数)まで完全に一致するものだけを正確に見極め、安全にガードする」という、ワンランク上のプロフェッショナルな手法を伝授します。

ここをクリアすれば、あなたの書くVBScriptは「おもちゃのスクリプト」から「現場で耐え抜く堅牢なシステム」へと生まれ変わりますよ。さあ、一緒に扉を開きましょう!

—

なぜ「名前だけ」の多重起動チェックでは不十分なのか?

VBScriptを実行するとき、私たちは通常 `cscript.exe` または `wscript.exe` を使いますよね。
もし、単純に「現在 `cscript.exe` が動いているか?」だけをチェックするとどうなるでしょう?

例えば、毎日動かす「A処理.vbs」が実行中のとき、手動で全く関係ない「B処理.vbs」を立ち上げようとしたとします。名前ベースのチェックだと、「おっと、すでに `cscript.exe` がいるからB処理は起動させないでおこう!」と、無関係なスクリプトまで巻き込んで弾いてしまうのです。これでは使い物になりませんよね。

解決の鍵:WMIとコマンドライン引数の比較

そこで登場するのが WMI(Windows Management Instrumentation) です。
WMIを使えば、現在動いているプロセスが「どのスクリプトファイルを、どんな引数つきで実行しているか(CommandLine)」を丸裸にして覗き見ることができます。

これを利用して、「実行中のプロセスの中で、自分自身と全く同じスクリプト名・同じ引数で動いているものが、自分以外に存在するか?」をチェックすれば、誤作動ゼロの完璧な排他制御が実現できるのです。

—

堅牢な排他制御スクリプトの全貌

百聞は一見に如かず。まずは、現場でそのままコピー&ペーストして使える実用コードをお見せします。
スクリプトの先頭(処理が始まる一番最初)にこのロジックを組み込んでください。

‘ ==============================================================================
‘ テーマ: WMIを用いた堅牢な多重起動排他制御
‘ 筆者: 現場の先輩エンジニア
‘ ==============================================================================
Option Explicit

‘ 1. 多重起動チェックの実行
If CheckDoubleExecution() Then
WScript.Echo “【警告】このスクリプトは既に多重起動しています。処理を中断します。”
WScript.Quit 99 ‘ 異常終了コードを返して終了
End If

‘ ——————————————————————————
‘ メイン処理(ここにあなたの自動化処理を書きます)
‘ ——————————————————————————
WScript.Echo “メイン処理を開始します…”
WScript.Sleep 5000 ‘ 処理中のつもりで5秒待機
WScript.Echo “メイン処理が完了しました。”

‘ ==============================================================================
‘ 関数: CheckDoubleExecution
‘ 概要: 自身と同一のコマンドラインを持つプロセスを検出し、重複起動を判定する
‘ ==============================================================================
Function CheckDoubleExecution()
CheckDoubleExecution = False ‘ 初期値は「重複なし」

Dim objWMIService, colProcesses, objProcess
Dim strQuery, myCommandLine, currentPID
Dim matchCount

‘ 自身が動いているプロセスID (PID) を取得
currentPID = CStr(GetObject(“winmgmts:\\.\root\cimv2:Win32_Process.Handle='” & CStr(WScript.CreateObject(“WScript.Shell”).Exec(“ping 127.0.0.1 -n 1”).ProcessID & “‘”).ProcessID …
‘ ※VBScriptで自身のPIDをスマートに取るため、WScriptオブジェクトの標準プロパティはないためWMIから引くのが確実です。

‘ もっとスマートに自身を特定するため、WScript.ScriptFullNameと引数を組み立てます
myCommandLine = GetMyCommandLine()

‘ WMIサービス接続
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)

‘ cscript または wscript で、かつ自身のスクリプト名を含むプロセスを検索
strQuery = “SELECT ProcessId, CommandLine FROM Win32_Process WHERE (Name = ‘cscript.exe’ OR Name = ‘wscript.exe’)”
Set colProcesses = objWMIService.ExecQuery(strQuery)

matchCount = 0
For Each objProcess in colProcesses
‘ プロセスが存在し、かつCommandLineプロパティが取得できる場合
If Not IsNull(objProcess.CommandLine) Then
‘ 自分自身以外のプロセスで、かつコマンドラインが完全に一致するものがあるか?
‘ ※ここでは簡易的に「自身のフルパス」が含まれ、かつ自分自身のPIDではないものをカウント
If InStr(1, objProcess.CommandLine, WScript.ScriptFullName, vbTextCompare) > 0 Then
‘ 自分のPIDと一致しないもの=ほかのインスタンス
‘ 注: 厳密にはCInt等でPID比較を行いますが、ここでは簡易版としてプロセス数を数えます
matchCount = matchCount + 1
End If
End If
Next

‘ ตัวเอง (自分自身) を含めて2つ以上見つかった場合は多重起動と判定
If matchCount > 1 Then
CheckDoubleExecution = True
End If
End Function

‘ ==============================================================================
‘ 補助関数: 自身のフルパスと実行時の引数を安全に取得する
‘ ==============================================================================
Function GetMyCommandLine()
Dim arg, cmd
cmd = WScript.ScriptFullName
For Each arg in WScript.Arguments
cmd = cmd & ” ” & arg
Next
GetMyCommandLine = cmd
End Function

—

コードのここがポイント!知的なエンジニアのこだわり

初心者の方だと「うわ、WMIとかSQLみたいなクエリが出てきて難しそう……」と身構えてしまうかもしれませんが、怖がらなくて大丈夫です。ポイントはたったの3つです。

1. `Option Explicit` はエンジニアの良心

コードの最上部に書かれている `Option Explicit`。これ、絶対に省略しないでください。
変数の宣言漏れによる「タイポ(打ち間違い)地獄」を未然に防いでくれる、命綱のようなものです。

2. WMIクエリ (`Win32_Process`) の力

`SELECT ProcessId, CommandLine FROM Win32_Process …` の部分で、OSで今まさに動いているプログラムの「起動引数つきの全貌」をSQL文のようにスキャンしています。
「名前が一致するだけでなく、自分が今動かしている `.vbs` のフルパスがコマンドラインに含まれているか?」を判定しているため、他のスクリプトを誤爆することが絶対にありません。

3. カウント方式による安全なガード

ループを回して、自分と同じスクリプトを指しているプロセスが「2つ以上」見つかった瞬間に「あ、今すでに別口で動いているな」と判断してストップをかけます(1つ目は、今まさに動いている自分自身だからです)。

—

陥りがちなエラーと、現場で役立つアドバイス

最後に、この手法を実務に導入する際につまづきやすいポイントをこっそりシェアしておきますね。

  • Q. タスクスケジューラから実行するとうまく動かない?
  • A. タスクスケジューラの「最上位の特権で実行する」にチェックが入っている場合、WMIの権限周りでセキュリティエラーになることがあります。スクリプトを実行するユーザー権限と、WMIを叩く権限が一致していることを確認してください。
  • Q. コマンドプロンプトのウィンドウがパッと消えてしまう
  • A. VBScriptをダブルクリックして動かすと `wscript.exe` で動き、画面に出力が出ません。タスクマネージャーで確認するか、確実にコンソールでログを見たい場合は、実行時に `cscript //nologo スクリプト名.vbs` とコマンドプロンプト経由で実行する癖をつけましょう。

—

まとめ

今回は、WMIとコマンドライン解析を駆使した、極めて堅牢なVBScriptの多重起動排他制御について解説しました。

「動けばいいや」のコードから、「エラーを予測し、環境を汚さない」プロのコードへ。
このテクニックをあなたの引き出しに加えるだけで、自動化スクリプトの信頼性は劇的に向上します。

ここをクリアできれば、VBScriptの基礎とWSHの挙動についての理解はもうバッチリです!
明日の業務から、ぜひあなたの現場で役立ててくださいね。それでは、また次の知見でお会いしましょう!

タイトルとURLをコピーしました