【入門編】【プロセス間通信】環境変数と一時ファイルを用いない、WScript.Shell.Exec の標準入出力を介した双方向データ交換 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

【極限のVBScript】プロセス間通信の真髄:WScript.Shell.Execによる双方向パイプ制御

「マクロの記録」や「単純なコマンドの実行」の段階を卒業し、真の自動化エンジニアへの一歩を踏み出そうとしている皆さん、こんにちは。

これまで皆さんは、VBScriptを使って「Excelを操作する」「ファイル名を一括変更する」「特定のアプリを起動する」といったタスクをこなしてきたことでしょう。それは素晴らしい第一歩です。しかし、実務の現場で求められるのは、単なる「実行」ではありません。「動いているプログラムと、リアルタイムに会話(対話)しながら制御する」という高度な技術です。

例えば、あるCLI(コマンドライン)ツールを呼び出したとき、ツールが「パスワードを入力してください」と求めてきたら? あるいは「処理を続行しますか? (Y/N)」と聞いてきたら?

多くの人はここで、一時ファイルを作成してそこに書き込んだり、環境変数を書き換えたりといった、少し「泥臭い」方法を選びがちです。しかし、プロのアーキテクトはそんなことはしません。

今日は、`WScript.Shell.Exec` メソッドが提供する「標準入出力(StdIn / StdOut)」という名のパイプを使い、環境変数も一時ファイルも一切使わずに、プロセス間でスマートにデータをやり取りする極限の技法を伝授します。

1. なぜ「ファイル」や「環境変数」ではいけないのか?

技術の深淵へ踏み込む前に、なぜわざわざ難しい「標準入出力」を使うのか、その理由を理解しておきましょう。ここをクリアすれば、設計思想が変わります。

手法 メリット デメリット(プロが避ける理由)
一時ファイル 実装が簡単 I/Oオーバーヘッドが大きい。 ディスクへの書き込みが発生するため遅い。また、削除し忘れるとゴミが残るし、セキュリティリスクにもなる。
環境変数 設定が楽 スコープ(有効範囲)の問題。 プロセス全体に影響を与えてしまい、他のプログラムの挙動を壊す可能性がある。また、一度に送れるデータ量に制限がある。
標準入出力 (Pipe) 高速・安全・動的 実装に「プロセスの状態」を意識した高度な制御が必要。

標準入出力(パイプ)とは、メモリ上で直接データを流し込む「土管」のようなものです。ファイルを経由せず、メモリ上で完結するため、極めて高速で、使い終われば跡形もなく消え去ります。これこそが、プロフェッショナルが選ぶ道です。

2. 核心のメカニメント:`WScript.Shell.Exec` の構造

VBScriptでプロセスを制御する際、よく使われるのは `Run` メソッドですが、今回は使いません。使うのは `Exec` メソッド です。

`Exec` メソッドが返すのは、単なる実行結果ではなく、`WshExec` オブジェクト です。このオブジェクトこそが、子プロセスとの「通信窓口」となります。

通信の3つの窓口

1. `StdIn` (Standard Input): あなた(親プロセス)から、子プロセスへ「命令」を送り込む窓口。
2. `StdOut` (Standard Output): 子プロセスから、あなた(親プロセス)へ「返答」が流れてくる窓口。
3. `StdErr` (Standard Error): 子プロセスで「エラー」が発生した際、その叫びが流れてくる窓口。

3. 実践:双方向対話型スクリプトの構築

では、実際に動くコードを見てみましょう。
今回は、「ユーザーの入力に対して、特定のルールで返答を返す、擬似的な対話型CLIツール(バッチファイル)」と、「それを制御するVBScript」の2段構成で作ります。

ステップ1:対話型子プロセス (`child_tool.bat`)

まず、メモ帳などで以下の内容を `child_tool.bat` という名前で保存してください。これは「命令を受け取って、それに応答する」という、対話型の動きをシミュレートしたものです。

@echo off
:loop
set /p input=
if "%input%"=="EXIT" goto end
if "%input%"=="HELLO" echo HELLO_BACK_TO_YOU
if "%input%"=="STATUS" echo SYSTEM_OK
goto loop
:end
echo SESSION_CLOSED

ステップ2:マスター制御スクリプト (`master.vbs`)

次に、このバッチファイルを操るメインのVBScriptを書きます。

' =================================================================
' プロセス間通信マスター・スクリプト
' 概要: Execオブジェクトを用いて、子プロセスと双方向通信を行う
' =================================================================

Option Explicit

Dim objShell, objExec
Dim strCommand, strInput, strResponse

Set objShell = CreateObject("WScript.Shell")

' 1. 子プロセスの起動 (child_tool.bat を実行)
' ※パスは環境に合わせて調整してください
strCommand = "cmd /c child_tool.bat"
Set objExec = objShell.Exec(strCommand)

On Error Resume Next ' 予期せぬエラーによる停止を防ぐ

' --- 対話パターンA: 命令を送って返答を待つ ---
WScript.Echo "--- Pattern A: Sending 'HELLO' ---"
SendToChild objExec, "HELLO"
strResponse = ReadFromChild(objExec)
WScript.Echo "Child said: " & strResponse

' --- 対話パターンB: 状態を確認する ---
WScript.Echo vbCrLf & "--- Pattern B: Sending 'STATUS' ---"
SendToChild objExec, "STATUS"
strResponse = ReadFromChild(objExec)
WScript.Echo "Child said: " & strResponse

' --- 対話パターンC: 終了命令を送る ---
WScript.Echo vbCrLf & "--- Pattern C: Sending 'EXIT' ---"
SendToChild objExec, "EXIT"
strResponse = ReadFromChild(objExec)
WScript.Echo "Child said: " & strResponse

' 2. 後処理
WScript.Echo vbCrLf & "Master: Session finished."
Set objExec = Nothing
Set objShell = Nothing

' =================================================================
' サブルーチン: 子プロセスへ標準入力を書き込む
' =================================================================
Sub SendToChild(objExec, strText)
    ' StdIn を介して文字列を送り込む。末尾に改行(vbCrLf)が必要。
    objExec.StdIn.WriteLine strText
End Sub

' =================================================================
' サブルーチン: 子プロセスからの標準出力を読み取る
' =================================================================
Function ReadFromChild(objExec)
    Dim strLine
    ' StdOut の末尾に到達していないか確認し、1行読み込む
    ' ※注意: 子プロセスが応答を返さない場合、ここでフリーズする可能性がある
    If Not objExec.StdOut.AtEndOfStream Then
        strLine = objExec.StdOut.ReadLine
        ReadFromChild = strLine
    Else
        ReadFromChild = ""
    End If
End Function

4. 伝説のエンジニアが教える「陥りやすい罠」と回避策

このコードを動かすと感動するはずですが、実はここには非常に危険な罠が潜んでいます。これを知っているかどうかが、初学者とプロの境界線です。

⚠️ 致命的な罠:デッドロック (Deadlock)

`Exec` を使った通信で最も恐ろしいのがデッドロックです。

何が起きるのか?
子プロセスの出力バッファ(データを溜めておくメモリ領域)がいっぱいになったのに、親プロセス(VBScript)が `StdIn.WriteLine` で書き込み続けようとしたり、逆に親が `StdOut.ReadLine` で読み込もうとしているのに、子プロセスが「入力を待っている」状態になると、両者がお互いの完了を待ち続けて、プログラムが永久に停止(フリーズ)します。

回避するための心得:
1. 「送ったら、読む」を徹底する:
大量のデータを送る場合は、一度にドカッと送らず、`Write` → `Read` のサイクルを細かく回してください。
2. `AtEndOfStream` を活用する:
「データがあるかどうか」を確認せずに `ReadLine` を呼ぶのは、暗闇を歩くようなものです。必ずストリームの状態を確認しましょう。
3. タイムアウトの概念を持つ:
VBScript単体では高度なタイムアウト制御は難しいですが、実務では「一定時間応答がなければプロセスを強制終了(`objExec.Terminate`)する」といった設計を必ず組み込みます。

5. まとめ:ここをクリアすれば、あなたはもう一人前

今回の内容を振り返りましょう。

  • `Exec` メソッド は、単なる実行ではなく、子プロセスとの「対話窓口」を得るためのもの。
  • `StdIn` / `StdOut` を使うことで、ファイルや環境変数に頼らない、高速でクリーンな通信が可能になる。
  • デッドロック は最大の敵。データの流れ(パイプの詰まり)を常に意識すること。

「マクロを動かす」レベルから、「プロセスを操る」レベルへ。
この `Exec` による双方向通信をマスターすれば、あなたは既存のツールを組み合わせて、まるで一つの巨大なシステムのように動かす「オーケストレーター」になれるはずです。

一歩ずつ、着実に。コードの裏側にある「オブジェクトの動き」を感じ取ってください。応援していますよ!

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