【実務・中級編】【コンソール画面制御】CScript実行時の自動終了防止と、画面クリア・標準入力待ちを組み込んだ対話型CLIの作成 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

CScriptを完全掌握する:自動終了を防ぎ、画面制御と入力バリデーションを極めた堅牢な対話型CLIの構築

Windowsの運用自動化や社内ツールの開発において、VBScript(Visual Basic Scripting Edition)は今なお強力な「Windows標準の接着剤」として君臨しています。しかし、多くの現場で見かけるスクリプトは、ダブルクリックで起動して一瞬で黒い画面が消え去ったり、不正な入力で沈黙(ハングアップ)したり、エラー時にリソースを抱えたまま異常終了したりと、お世辞にも「プロ仕様」とは呼べないものが散見されます。

本記事では、VBScriptをCScript環境下で完全にコントロールし、「自動終了防止」「画面クリア(VT100エスケープシーケンス)」「堅牢な標準入出力制御」「エラーハンドリング」を実装した、プロダクション環境に耐えうる対話型コマンドラインインターフェース(CLI)の構築手法を徹底的に解説します。

1. CScript CLIの宿命と、なぜ一般的なスクリプトは「使えない」のか

VBScriptの実行エンジンには、GUIホストである `WScript.exe` と、コンソールホストである `CScript.exe` の2種類が存在します。

既定のホストが `WScript.exe` になっている環境でスクリプトをダブルクリックすると、`WScript.Echo` は不格好なメッセージボックスを連発し、標準入出力(`StdIn` / `StdOut`)を参照しようとした瞬間に「無効なハンドルです (Invalid handle)」という致命的エラー(ランタイムエラー: 0x80070006)を吐いて即死します。

また、運良く `CScript.exe` で起動されたとしても、処理が終わればコンソールウィンドウは容赦なく閉じられます。ユーザーがエラー内容を確認する暇もありません。

実務で「使える」ツールにするためには、以下の3つの課題を確実にクリアしなければなりません。

1. ホスト環境の強制(WScriptからCScriptへの自己再起動)
2. 非同期なユーザー入力に対する厳密なバリデーション
3. 予期せぬ例外発生時における、ファイルやDBなどの「リソースの確実な解放」

これらを場当たり的なコードではなく、設計パターンとして定式化します。

2. 堅牢な対話型CLIを構築するための4大設計原則

原則1:ホスト環境の自己解決(Self-Relaunching)

ユーザーに「コマンドプロンプトを開いて、`cscript` と打ってから実行してください」と求めるのは不親切極まりなく、運用の現場では確実に誤操作を招きます。
スクリプト自身が「自分がどちらのホストで動いているか」を検知し、`WScript.exe` であれば自ら `CScript.exe` をバックグラウンドで起動し直し、自身を終了するという自己再起動(Self-Relaunching)ロジックをエントリポイントに仕込みます。

原則2:VT100エスケープシーケンスによる画面制御

従来のVBScript本では、画面クリア(CLS)を実現するために `WshShell.Run “cmd /c cls”` を実行するような非効率極まりない(そして実際にはカレントコンソールをクリアできない)コードが紹介されていました。
現代のWindows(Windows 10 / Server 2016以降)のコンソールは、標準でVT100(ANSI)エスケープシーケンスをサポートしています。標準出力に特定の制御コードを流し込むだけで、プロセスをフォークさせることなく、一瞬でコンソールをクリアし、カーソルをホームポジションに移動させることができます。

  • 画面クリアとカーソル初期化: `Chr(27) & “[2J” & Chr(27) & “[H”`

原則3:StdIn / StdOut のバッファ制御と「Enterキー待ち」

`WScript.StdIn.ReadLine` はブロッキングコール(入力があるまでスレッドを停止する処理)です。これを利用して対話型処理を構築しますが、ユーザーが何も入力せずにEnterを押した場合や、予期せぬ制御文字を入力した場合のハンドリングが必要です。
また、処理の最後には必ず「Press Enter to exit…」という入力を要求し、自動終了を完全に防止します。

原則4:On Error Resume Next の「局所化」と「即時評価」

VBScriptの悪名高き `On Error Resume Next` をスクリプトの先頭に記述してはなりません。それはバグを隠蔽し、障害解析を不可能にする最悪のアンチパターンです。
エラーハンドリングは、ファイルI/OやDB接続など、「失敗する可能性のある一画」のみに限定して適用し、直後に必ず `Err.Number` を評価してトラップするのが鉄則です。

3. 完全実用コード:プロダクションクオリティの対話型CLIテンプレート

以下のコードは、上記の設計原則をすべて具現化した、そのまま実務に投入できるテンプレートです。データベース接続やファイル操作を行うためのプレースホルダーも堅牢な形で組み込んでいます。

‘ ==============================================================================
‘ 対話型CLIシステム・テンプレート (Production-Ready)
‘ Encoding: Shift-JIS (WSH標準環境用)
‘ ==============================================================================
Option Explicit

‘ ——————————————————————————
‘ 1. ホスト環境の強制 (CScriptへの自己再起動)
‘ ——————————————————————————
ForceCScriptHost

‘ ——————————————————————————
‘ 2. 定数・グローバル変数の定義
‘ ——————————————————————————
Const ESC_CLEAR = &H1B ‘ VT100 Escape character (Dec: 27)
Dim objShell, objFSO
Set objShell = WScript.CreateObject(“WScript.Shell”)
Set objFSO = WScript.CreateObject(“Scripting.FileSystemObject”)

‘ ——————————————————————————
‘ 3. メインルーチン
‘ ——————————————————————————
Call Main()

Sub Main()
Dim strInput, blnLoop, strValidatedValue
blnLoop = True

Do While blnLoop
‘ 画面をクリアしてヘッダーを表示
Call ClearScreen()
Call PrintHeader(“社員情報照会システム (CLI版)”)

WScript.StdOut.Write ” [1] 社員IDを入力してください (例: EMP001): ”
strInput = WScript.StdIn.ReadLine()

‘ 入力値のサニタイズとバリデーション
strValidatedValue = Trim(strInput)

If strValidatedValue = “” Then
Call PrintError(“入力が空です。有効なIDを入力してください。”)
Call WaitAnyKey()
ElseIf UCase(strValidatedValue) = “EXIT” Then
blnLoop = False
Else
‘ バリデーションを通過した値で業務ロジックを実行
Call ExecuteBusinessLogic(strValidatedValue)

‘ 処理完了後の分岐選択
WScript.StdOut.Write vbCrLf & ” 続けて処理を行いますか? (Y/N): ”
Dim strAns
strAns = UCase(Trim(WScript.StdIn.ReadLine()))
If strAns <> “Y” Then
blnLoop = False
End If
End If
Loop

‘ 終了処理
Call ClearScreen()
WScript.StdOut.WriteLine ” [システム情報] 処理が正常に終了しました。”
Call TerminateScript(0)
End Sub

‘ ——————————————————————————
‘ 4. 業務ロジック(データベース・ファイル連携想定)
‘ ——————————————————————————
Sub ExecuteBusinessLogic(ByVal empID)
WScript.StdOut.WriteLine vbCrLf & ” >>> データベース照会中: ” & empID & ” …”
WScript.Sleep 800 ‘ 処理感を演出するウェイト

‘ — [堅牢なエラーハンドリングの実装例] —
On Error Resume Next

‘ 例: 本来はここでADODB.Connection等を用いてDBアクセスを行う
‘ このサンプルでは、ファイル読み込みを模したエラーハンドリングを実演
Dim objFile, strFilePath
strFilePath = “C:\NonExistentFolder\config.ini” ‘ 存在しないパス(エラー想定)

‘ エラーが起きる可能性のある処理をカプセル化
Set objFile = objFSO.OpenTextFile(strFilePath, 1) ‘ ForReading

If Err.Number <> 0 Then
‘ エラーを即座に補足し、ユーザーに分かりやすく提示。システムは落とさない。
Call PrintError(“データソースへのアクセスに失敗しました。”)
WScript.StdOut.WriteLine ” エラー詳細: [” & Err.Number & “] ” & Err.Description
Err.Clear
On Error GoTo 0 ‘ ハンドリングの解除
Exit Sub
End If
On Error GoTo 0
‘ —————————————–

‘ 正常終了時のダミー出力
WScript.StdOut.WriteLine ” [結果] 社員名: 技術 太郎 (開発部)”
WScript.StdOut.WriteLine ” [結果] 内線番号: 99-9999″
End Sub

‘ ——————————————————————————
‘ 5. 画面制御・ユーティリティ関数
‘ ——————————————————————————

‘ CScriptホストでの実行を強制するルーチン
Sub ForceCScriptHost()
Dim strApp, strArgs, i, objWshShell
strApp = UCase(Right(WScript.FullName, 11))

‘ WSCRIPT.EXE で起動されていた場合は、CSCRIPT.EXE で再起動
If strApp = “WSCRIPT.EXE” Then
Set objWshShell = WScript.CreateObject(“WScript.Shell”)
strArgs = “”
For i = 0 To WScript.Arguments.Count – 1
strArgs = strArgs & ” ” & Quote(WScript.Arguments(i))
Next
‘ 新しいコンソールを開いてCScriptで自身を実行
objWshShell.Run “cmd.exe /k cscript.exe //NoLogo ” & Quote(WScript.ScriptFullName) & strArgs
WScript.Quit
End If
End Sub

‘ 引数にダブルクォーテーションを付与するヘルパー
Function Quote(ByVal str)
Quote = “””” & str & “”””
End Function

‘ VT100エスケープシーケンスによる画面クリア
Sub ClearScreen()
On Error Resume Next
‘ ESC [2J (画面クリア) & ESC [H (カーソルを左上に移動)
WScript.StdOut.Write Chr(ESC_CLEAR) & “[2J” & Chr(ESC_CLEAR) & “[H”

‘ 環境がVT100に対応していない(古いOS等)場合のフォールバック
If Err.Number <> 0 Then
Dim i
For i = 1 To 50
WScript.StdOut.WriteLine “”
Next
Err.Clear
End If
On Error GoTo 0
End Sub

‘ ヘッダー描画
Sub PrintHeader(ByVal title)
WScript.StdOut.WriteLine “================================================================================”
WScript.StdOut.WriteLine ” ” & title
WScript.StdOut.WriteLine “================================================================================”
WScript.StdOut.WriteLine “”
End Sub

‘ エラーメッセージ描画(赤文字風に視認性を上げる工夫)
Sub PrintError(ByVal errMsg)
WScript.StdOut.WriteLine “”
WScript.StdOut.WriteLine ” [ERROR] —————————————————————-”
WScript.StdOut.WriteLine ” ” & errMsg
WScript.StdOut.WriteLine ” ————————————————————————”
WScript.StdOut.WriteLine “”
End Sub

‘ 任意のキー入力待ち(自動終了防止)
Sub WaitAnyKey()
WScript.StdOut.Write ” 継続するには Enter キーを押してください…”
WScript.StdIn.ReadLine
End Sub

‘ スクリプトの安全な終了処理
Sub TerminateScript(ByVal exitCode)
WScript.StdOut.Write ” [INFO] スクリプトを終了します。Enter キーを押してください…”
WScript.StdIn.ReadLine

‘ グローバルオブジェクトの解放
Set objShell = Nothing
Set objFSO = Nothing

WScript.Quit exitCode
End Sub

4. アーキテクチャ解説:なぜこの設計が堅牢なのか

1. ホスト強制(`ForceCScriptHost`)のメカニズム

`WScript.FullName` を解析し、末尾が `WSCRIPT.EXE` であるかを判定しています。
もしGUIホストであれば、`cmd.exe /k` を経由して `cscript.exe` を立ち上げ直します。ここで `/k` スイッチを使用しているため、万が一スクリプトがクラッシュしてもコマンドプロンプトのウインドウは閉じ残され、エラーメッセージが画面に保持されます。また、実行時に渡された引数(Arguments)も漏れなくエスケープして再帰的に引き継ぎます。

2. VT100シーケンスによるコンソールクリア

多くのVBScript解説記事で無視されているのが、画面クリアのオーバーヘッドです。
`WshShell.Run “cls”` は動きません(`cls` はシェルの内部コマンドであるため)。
本コードで採用した `Chr(&H1B) & “[2J”` は、Windows 10以降のコンソールバッファに直接作用する制御コマンドです。これにより、一切の外部プロセス(`cmd.exe`)を起動することなく、ミリ秒未満で画面描画をクリアします。万が一、古いレガシーなWindows環境でこのシーケンスが文字化けして表示される場合に備え、`On Error Resume Next` でトラップし、改行を50回送るフォールバック(代替処理)を実装して安全性を確保しています。

3. ブロッキングI/Oの制御

`WScript.StdIn.ReadLine` は、入力ストリームが空の間、プログラムの実行を一時停止(ブロック)させます。これを利用することで、無駄なCPUサイクルを消費するビジーループ(`While True: WScript.Sleep 100: Wend` のような悪しき実装)を回避し、CPU使用率0%で入力待ちを実現しています。

5. 実務連携(File/DB)における致命的な罠と対策

このCLIツールを、実際の業務データ(SQL Server, Oracle Database, CSVファイルなど)に接続させる場合、以下の「WSH特有の罠」に必ず対処してください。

罠A:ADODB接続のリークとゾンビプロセス

対話型ツールでは、ユーザーが途中で `Ctrl + C` を押して強制終了することが多々あります。
データベースのオープン(`objConn.Open`)は、「必要な時に開き、用が済んだら即座に閉じる(Close)」を徹底してください。グローバル変数で接続を持ち続ける設計は、強制終了時にデータベース側にゴーストセッションを大量に残す原因になります。

‘ 良い例:局所的な接続と確実なクローズ
Sub FetchData(sql)
Dim conn
Set conn = CreateObject(“ADODB.Connection”)
On Error Resume Next
conn.Open “ConnectionString…”
If Err.Number = 0 Then
‘ クエリ実行処理
conn.Close
End If
Set conn = Nothing
End Sub

罠B:Shift-JIS と UTF-8 のコンソール文字化け

Windowsの標準コマンドプロンプト(コードページ 932: Shift-JIS)において、UTF-8で保存されたスクリプトを実行すると、日本語が激しく文字化けします。

  • 対策: スクリプトファイル自体は、必ず「Shift-JIS (ANSI)」で保存してください。
  • もし外部のUTF-8ファイルを読み込んでコンソールに出力する場合は、`Scripting.FileSystemObject`(SJIS専用)ではなく、`ADODB.Stream` オブジェクトを使用して文字コードを明示的に「UTF-8」から「Shift-JIS」に変換しながら読み込む必要があります。

6. 結論:VBScriptは「設計」次第で最強のツールになる

VBScriptは古い技術であり、PowerShellに置き換わりつつあります。しかし、「いかなるWindows環境でも、事前のセットアップやポリシー変更(`Set-ExecutionPolicy`)なしに、ダブルクリックだけで100%確実に動作する」という即効性においては、今なお右に出るものはありません。

「動けばいい」というレベルのスパゲッティコードを脱却し、本記事で紹介した自己再起動・画面制御・厳密なバリデーション・局所的エラーハンドリングを取り入れることで、あなたの作成するスクリプトは、プロフェッショナルな「プロダクト」へと昇華します。

ぜひ、日々の運用ツールの設計にこのテンプレートを取り入れ、堅牢で美しいCLIツールを構築してください。

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