こんにちは!現場でバリバリ自動化コードを書いていると、「VBScriptの軽快さは好きだけど、最新のWindows機能やJSONのパース、複雑なネットワーク処理をしたいときはPowerShellのコマンドレットが使えたらなぁ…」と悩む瞬間、ありませんよね?
実は、VBScriptからPowerShellを裏側で呼び出し、リアルタイムで会話(双方向連携)するという、ちょっと痺れるテクニックがあるんです。
今回は、VBScriptの限界を軽々と突破し、WSH(Windows Script Host)の真髄を味わう「`WScript.Shell.Exec`を用いたPowerShell双方向連携」の世界へご案内します。ここをクリアすれば、あなたの自動化スキルは間違いなく一段上のステージに到達しますよ!
—
なぜ `Run` ではなく `Exec` なのか?
VBScriptから外部コマンドを叩くとき、多くの人が使うのは `WScript.Shell` の `Run` メソッドでしょう。しかし、`Run` には致命的な弱点があります。
- `Run` の限界: コマンドが「終わるまで」VBScript側が完全にフリーズするか、非同期にしても標準出力(StdOut)をリアルタイムでキャッチできません。
- `Exec` の真価: `Exec` メソッドは、実行したプロセスの「オブジェクト」を返します。これにより、以下のことが可能になります。
- 実行中のPowerShellに対して、データを流し込む(`StdIn`)
- PowerShellが吐き出す結果を、処理が完了する前にリアルタイムで読み取る(`StdOut` / `StdErr`)
- プロセスの生存確認や終了コードの取得
つまり、`Exec` はVBScriptとPowerShellを繋ぐ「特命の通信回線」なのです。
—
3分でわかる!全体像のイメージ図
まずは、今回目指すアーキテクチャのイメージを頭に叩き込みましょう。
[ VBScript (親) ]
│
├─(StdIn: 書き込み)──> [ PowerShell (子プロセス) ]
│ (最新のコマンドレットやAPIを実行)
│<──(StdOut: 読み取り)─┤
▼
(リアルタイムで結果をコンソールやログに出力)
VBScript側が親、PowerShellが子プロセスとして起動し、パイプラインを介して文字のキャッチボールを行います。
---
実践!リアルタイム出力取得&データ送信コード
それでは、実戦投入できるサンプルコードを見てみましょう。
メモ帳に貼り付けて、拡張子を `.vbs` (例: `pwsh_link.vbs`)にして保存し、実行してみてください。
このスクリプトは、PowerShellを起動し、VBScript側から「処理してほしいデータ」を送り、PowerShell側で加工されたリアルタイムの出力をVBScriptが受け取るというデモです。
‘ =================================================================0
‘ テーマ: WScript.Shell.Execを用いたPowerShell双方向連携
‘ 概要: VBScriptからPowerShellを起動し、ストリーム経由で対話する
‘ =================================================================0
Option Explicit
Dim wshShell, execObj
Dim command, sendData, receivedLine
‘ 1. WScript.Shell オブジェクトの生成
Set wshShell = CreateObject(“WScript.Shell”)
‘ 2. 実行するPowerShellコマンドの組み立て
‘ -NoProfile : プロファイル読み込みをスキップして高速化
‘ -Command – : 標準入力からコマンドを受け取るモード
command = “powershell.exe -NoProfile -ExecutionPolicy Bypass -Command -”
WScript.Echo “=== PowerShellとの通信回線をオープンします ===”
‘ 3. Exec メソッドでプロセスを起動 (これが今回の主役!)
Set execObj = wshShell.Exec(command)
‘ 4. PowerShell側へコマンドやデータを送信 (StdIn の利用)
‘ 今回は、受け取ったテキストを大文字に変換して返す簡単なスクリプトを流し込みます
execObj.StdIn.WriteLine “$inputData = [Console]::In.ReadLine();”
execObj.StdIn.WriteLine “Write-Output “”[PowerShellからの返答] 受信データ -> $inputData””;”
execObj.StdIn.WriteLine “Start-Sleep -Seconds 1;” ‘ 処理のタメを表現
execObj.StdIn.WriteLine “Write-Output “”[PowerShellからの返答] 処理が完了しました!””;”
‘ データを送信(ここでPowerShell側のReadLineがウェイクアップする)
sendData = “Hello from VBScript! 連携テスト成功です。”
execObj.StdIn.WriteLine sendData
‘ ※注意: StdInへの書き込みが終わったら、ストリームを閉じることで
‘ PowerShell側に「入力はここまで」と伝えます
execObj.StdIn.Close
‘ 5. リアルタイムで出力結果を受け取る (StdOut の利用)
‘ プロセスが終了するまで、行単位で出力を監視し続けます
Do While execObj.Status = 0
‘ まだ実行中であれば、出力があるかチェック
If Not execObj.StdOut.AtEndOfStream Then
receivedLine = execObj.StdOut.ReadLine
WScript.Echo “VBScriptがキャッチ: ” & receivedLine
End If
‘ CPUの焼き付きを防ぐためのわずかなウェイト(WSHのお作法)
WScript.Sleep 50
Loop
‘ 6. ループ抜け後の残余ストリームの回収(取りこぼし防止)
Do While Not execObj.StdOut.AtEndOfStream
receivedLine = execObj.StdOut.ReadLine
WScript.Echo “VBScriptがキャッチ(残余): ” & receivedLine
Loop
WScript.Echo “=== すべてのプロセスが正常終了しました ===”
‘ 7. オブジェクトの解放
Set execObj = Nothing
Set wshShell = Nothing
—
コードの核心を深掘り解説!
初心者のうちは難しく見えるかもしれませんが、ポイントを押さえれば非常にシンプルです。
① `wshShell.Exec(command)` の魔法
`Run` メソッドと違い、`Exec` は実行した瞬間に `WshProcess` オブジェクトを返します。このオブジェクトが持っている `StdIn`(入力)、`StdOut`(出力)、`Status`(状態)という3つのプロパティが、双方向制御の鍵となります。
② `execObj.StdIn.WriteLine` と `.Close`
VBScriptからPowerShellへ「言葉」を投げ込むのが `StdIn.WriteLine` です。
ここで重要なのは、データを送り終わったら必ず `execObj.StdIn.Close` を呼ぶこと。これを忘れると、PowerShell側は「まだ次の命令が来るかもしれない…」と待ちぼうけを食らい、スクリプトがデッドロック(永遠に終わらない状態)に陥ります。
③ `Do While execObj.Status = 0` による監視
`execObj.Status` は、プロセスが動いている間は `0`(WshRunning)、終了すると `1`(WshFinished)になります。
このループの中で `execObj.StdOut.AtEndOfStream` を監視し続けることで、PowerShellが画面(標準出力)に文字を吐き出した瞬間、タイムラグなしでVBScript側がそれを拾い上げることができるのです。これが「リアルタイム出力取得」の正体です。
—
現場でよくある「ハマりどころ」と対策
この高度な連携を行う際、初心者が必ずと言っていいほど直面する罠があります。先輩からのアドバイスとして頭の片隅に置いておいてください。
罠1:日本語が文字化けする
- 原因: VBScript(Shift-JIS環境が多い)と、PowerShell(デフォルトはUTF-8など)の間で文字コードの食い違いが発生するため。
- 対策: PowerShellの起動コマンドの最初に `[Console]::OutputEncoding = [System.Text.Encoding]::UTF8` などを挟むか、やり取りするテキストは極力半角英数やURLエンコード済みのものにする、あるいはVBScript側で適切にストリームを扱う配慮をします。
罠2:無限ループ(フリーズ)から抜け出せない
- 原因: 先ほども触れた `StdIn.Close` のし忘れや、PowerShell側でユーザー入力を待つ状態(プロンプトで止まるなど)になってしまっているケース。
- 対策: `-NonInteractive` や `-Command -` を正しく使い、対話的入力を必要とするコマンドレット(例: 確認プロンプトが出るもの)は使わないようにしましょう。
—
おわりに
お疲れ様でした!今回は `WScript.Shell.Exec` を使った、VBScriptとPowerShellの双方向連携という少しディープな世界を解説しました。
「古い言語」と片付けられがちなVBScriptですが、こうしたWSHの奥義を理解していれば、最新のPowerShellのパワーを自在に引き出すキッチリとした自動化ツールを組むことができます。「VBScriptの軽快なフットワーク」と「PowerShellの圧倒的な機能美」を組み合わせれば、あなたの業務自動化の可能性は無限大に広がりますよ。
ここをクリアしたあなたなら、もうVBScriptの基礎はバッチリです!ぜひ、日々の業務効率化に役立ててくださいね。
