【外部コマンド戻り値制御】Shell.Run の bWaitOnReturn による ERRORLEVEL 完全キャッチ
開発現場でVBScriptを扱う際、避けて通れないのが「外部プロセス(EXEやバッチファイル)の呼び出し」だ。
業務自動化ツールを作っていると、「特定のバッチ処理を実行し、その結果(正常終了か異常終了か)に応じて後続の処理を分岐させたい」という要件に必ず直面する。
ここで多くの初学者、あるいは設計思想を理解していないプログラマーが罠にハマる。
`WScript.Shell` の `Run` メソッドを適当に呼び出し、「プロセスが勝手に動いて失敗しているのに、VBScript側は成功したものとして後続処理を続行し、夜間バッチが大爆発を起こした」 という事故を、私は幾度となく目撃してきた。
今回は、VBScriptから外部コマンドの終了コード(ERRORLEVEL)を完全にキャッチし、1ミリの狂いもなく制御するための極限の知見を授けよう。
—
なぜ `WScript.Shell` のデフォルトは信用できないのか
外部コマンドを実行する際、多くの人が次のようなコードを書く。
‘ 【アンチパターン】これでは終了コードが取れない、あるいは制御が崩壊する
Dim shell
Set shell = CreateObject(“WScript.Shell”)
shell.Run “C:\Tools\data_process.bat”
Set shell = Nothing
このコードには2つの致命的な欠陥がある。
1. 非同期実行(Fire and Forget)の罠:
デフォルトでは、`Run` メソッドは外部プロセスを起動した瞬間、VBScript側に制御を戻す。つまり、外部バッチが実行中であっても、VBScriptは次の行へ進んでしまう。
2. 終了コードの闇:
非同期で走っている以上、外部プロセスが何番の終了コード(ERRORLEVEL)を返したかすらキャッチしようがない。
外部プロセスの完了を待ち、その生死(終了コード)を判定するためには、`Run` メソッドの引数を完全に支配しなければならない。
—
決定版:`Shell.Run` の完全構文と `bWaitOnReturn`
`WScript.Run` メソッドの構文は以下の通りだ。
object.Run(strCommand, [intWindowStyle], [bWaitOnReturn])
この第3引数 `bWaitOnReturn` こそが、今回の主役である。
- `False`(省略時のデフォルト): 非同期実行。プロセス起動後、即座にVBScriptへ制御が戻る。
- `True`: 同期実行。外部プロセスの実行が完全に終了するまで、VBScriptの実行をブロック(待機)し、プロセスが返した終了コード(Integer)を戻り値として返す。
この `bWaitOnReturn = True` を指定し、戻り値を変数に受けることで、バッチファイルの `ERRORLEVEL` を完全に手中に収めることができるのだ。
—
プロダクション品質:堅牢な外部コマンド実行テンプレート
実務の現場でそのまま使える、ログ出力と例外処理を網羅したプロダクションコードを提示する。
単にコマンドを叩くだけでなく、「実行ファイルが存在するか」「戻り値に応じた厳密なハンドリングを行っているか」がプロのエンジニアの分水嶺となる。
Option Explicit
‘ ==============================================================================
‘ スクリプト名: RunExternalCommand_Sample.vbs
‘ 概要: 外部コマンドを同期実行し、終了コードをキャッチして制御を分岐する
‘ ==============================================================================
Sub Main()
Dim objShell, fso
Dim targetCommand, intExitCode
Dim logMessage
Set objShell = CreateObject(“WScript.Shell”)
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 実行するコマンド(例: 存在しないバッチ、または任意のEXE/バッチパス)
‘ ※スペースを含むパスの場合は、ダブルクォーテーションの囲みに注意すること
targetCommand = “cmd.exe /c “”C:\MyTools\process.bat”” arg1″
‘ 【重要】実行前存在チェック(FSOによる事前検証で予期せぬクラッシュを防ぐ)
‘ ※cmd.exeなどのシステム組込コマンド以外、実体を伴うバッチやEXEの場合に有効
‘ If Not fso.FileExists(“C:\MyTools\process.bat”) Then
‘ WScript.Echo “CRITICAL: 実行ファイルが見つかりません。”
‘ WScript.Quit 99
‘ End If
WScript.Echo “INFO: 外部プロセスの実行を開始します…”
‘ Run(command, windowStyle, bWaitOnReturn)
‘ intWindowStyle: 0 = ウィンドウ非表示 (Hidden)
‘ bWaitOnReturn : True = 処理完了まで待機し、終了コードを返す
On Error Resume Next
intExitCode = objShell.Run(targetCommand, 0, True)
If Err.Number <> 0 Then
WScript.Echo “ERROR: プロセスの起動に失敗しました。Error: ” & Err.Description
Set objShell = Nothing
Set fso = Nothing
WScript.Quit Err.Number
End If
On Error GoTo 0
‘ 終了コードに基づく厳密な制御分岐
Select Case intExitCode
Case 0
WScript.Echo “SUCCESS: 外部プロセスが正常終了しました。(ExitCode: 0)”
‘ ここに後続の正常系処理を記述
Case 1
WScript.Echo “WARNING: 警告終了を検知しました。(ExitCode: 1)”
‘ 必要に応じたリカバリ処理
Case Else
WScript.Echo “ERROR: 異常終了を検知しました。ExitCode: ” & intExitCode
‘ ログ出力、管理者への通知、処理中断などの異常系処理
Call WriteErrorLog(intExitCode)
WScript.Quit intExitCode
End Select
‘ クリーンアップ
Set objShell = Nothing
Set fso = Nothing
WScript.Echo “INFO: すべての処理が完了しました。”
End Sub
‘ 異常発生時のログ出力モジュール(ダミー)
Sub WriteErrorLog(exitCode)
‘ 実務ではここでテキストログやDBへエラー内容を記録する
WScript.Echo “[LOG] 異常終了コード: ” & exitCode & ” をシステムログに記録しました。”
End Sub
‘ メイン処理の呼び出し
Call Main()
—
ジニアとしてこのコードで注目してほしいポイントを解説しよう。
1. ウィンドウタイルの制御 (`intWindowStyle = 0`)
バックグラウンドで自動化タスクを走らせる場合、突然黒いコマンドプロンプト画面がユーザーの画面にポップアップするのはUI/UXとして最悪だ。第2引数に `0`(`vbHide` 相当)を指定することで、完全に隠蔽された状態でプロセスを走らせることができる。
2. `On Error` との二重防壁
`objShell.Run` 自体は、指定したパスの実行ファイルが存在しない場合などにVBScriptのランタイムエラーを発生させる。そのため、`On Error Resume Next` でトラップしつつ、`Err.Number` を監視することで、「OSレベルでの起動失敗」と「プログラム内部での異常終了(ERRORLEVEL非0)」を明確に切り分けている。
—
データベース・ファイル連携における実践的注意点
この「ERRORLEVEL完全キャッチ技法」を業務システム(ファイルサーバー監視やDB連携バッチ)に組み込む際、シニアアーキテクトとして以下の鉄則を遵守してほしい。
1. パスのクォーテーション地獄への対策
パスや引数にスペースが含まれる場合、`Run` の第1引数の文字列構築は非常に繊細になる。
`targetCommand = “cmd.exe /c “”C:\Program Files\App\tool.exe”” “”D:\My Folder\input.csv”””` のように、`cmd.exe /c` を挟んでダブルクォーテーションを適切にエスケープするテクニックが、環境依存のバグを防ぐ鍵となる。
2. タイムアウト制御の実装(発展)
`bWaitOnReturn = True` は強力だが、もし外部プロセスが無限ループに陥った場合、VBScript側も永遠にフリーズする。
極めてクリティカルな業務自動化では、`Run` だけで待つのではなく、WMI(`Win32_Process`)や専用の非同期ラッパーを組み合わせるか、外部バッチ側でタイムアウト制御を持たせる設計が不可欠となる。
—
総括
VBScriptはレガシーな言語と揶揄されることもあるが、Windows環境においてミドルウェアのインストール不要で動作する最強の軽量自動化スクリプトであることには変わりない。
そのポテンシャルを極限まで引き出し、堅牢なシステムを構築するか否かは、こうした「APIの仕様(引数の挙動)をどこまで深く理解しているか」にかかっている。
今回解説した `bWaitOnReturn` による戻り値制御をマスターし、あなたの管理する自動化バッチを「絶対に止まらない、あるいは止まっても確実に検知できる」強靭なものへと進化させてほしい。
