VBScriptの限界を突破せよ:C#動的コンパイルによる「ハイブリッド・ランタイム」の構築
VBScriptは、シンプルであるがゆえにレガシーだ。COMオブジェクトの操作には長けているが、現代的な暗号化処理、複雑なJSONパース、あるいはマルチスレッドが必要な並列処理に直面した瞬間、その設計は「袋小路」を迎える。
多くの開発者はここで「外部EXE」を呼ぶという安易な選択をするが、それでは配布や環境構築のコストが跳ね上がる。真の自動化エンジニアは、「手元のVBScriptから、OSに標準搭載されたPowerShellの.NETコンパイラ(Add-Type)を制御し、メモリ上でC#コードをコンパイルして実行する」という解を選ぶ。
本稿では、VBScriptの制約をC#で塗り替える、実務レベルの「ハイブリッド・ランタイム」設計論を伝授する。
—
なぜ「PowerShell経由のAdd-Type」なのか
VBScript単体では、メモリリークの制御や高度な型定義ができない。そこで、Windowsに標準搭載された`PowerShell`を中間層(ブリッジ)として利用する。
- インメモリ実行: 一時ファイルを作成せず、パイプラインを通じてコードを渡すことで、セキュリティと機密性を担保する。
- 型定義の拡張: `Add-Type`を用いることで、C#の強力なライブラリ(System.Security.CryptographyやSystem.Net.Httpなど)を直接VBScriptのコンテキストから呼び出せるようになる。
- オーバーヘッドの最小化: 外部プロセスを何度も起動するのではなく、必要なロジックをC#側で完結させることで、I/O待ちの時間を劇的に短縮する。
—
実装:堅牢な「C#インメモリ・ブリッジ」
以下のコードは、VBScriptからC#のコードを流し込み、その結果を標準出力で受け取るためのアーキテクチャだ。
‘ — VBScript: C# Dynamic Compiler Bridge —
Option Explicit
Dim psScript, csharpCode, result
‘ 1. 実行したいC#コードをヒアドキュメント形式で定義
csharpCode = “using System; ” & _
“public class Logic { ” & _
” public static string Execute(string input) { ” & _
” return “”Hello from C#! Input received: “” + input; ” & _
” } ” & _
“}”
‘ 2. PowerShell経由でAdd-Typeを呼び出すコマンドを組み立てる
‘ -NoProfile: 起動コスト削減
‘ -ExecutionPolicy Bypass: セキュリティ制限の回避
‘ -Command: Base64エンコードして渡すのが、引用符の衝突を避けるプロの流儀
psScript = “powershell -NoProfile -ExecutionPolicy Bypass -Command ” & _
“\”$code = ‘” & csharpCode & “‘; ” & _
“Add-Type -TypeDefinition $code; ” & _
“[Logic]::Execute(‘ProductionData’)\””
‘ 3. WScript.Shell を利用して実行し、結果をキャプチャ
Dim shell, exec
Set shell = CreateObject(“WScript.Shell”)
Set exec = shell.Exec(psScript)
‘ 4. 標準出力を読み取る
result = exec.StdOut.ReadAll()
WScript.Echo “Execution Result: ” & result
Set exec = Nothing
Set shell = Nothing
—
プロダクション環境における「3つの鉄則」
コードを動かして満足してはいけない。実務環境では以下の設計思想を注入せよ。
1. 文字列のエスケープは「Base64」で行え
上記のコードでは説明のために単純な文字列結合を行っているが、実際の業務コードではダブルクォーテーションや改行コードが混入し、PowerShell側のパーサーを破壊する。複雑なコードは必ずBase64エンコードしてPowerShellに渡せ。 これがバグをゼロにする唯一の手段だ。
2. 標準エラー出力(StdErr)の監視を怠るな
VBScriptの`Exec`は、`StdOut`だけでなく`StdErr`も提供する。C#のコンパイルエラーは`StdErr`に吐き出される。これを無視すると、サイレントエラーにより「なぜか動かない」という悪夢のような調査に時間を費やすことになる。
If Not exec.StdErr.AtEndOfStream Then
Err.Raise 1, “C# Compile Error”, exec.StdErr.ReadAll()
End If
3. 実行環境のライフサイクル管理
`WScript.Shell`の`.Exec`は非同期実行が可能だが、VBScript側で処理を完了させたい場合は、`Do While exec.Status = 0` のループでステータスを確認し、適切に待機させる必要がある。非同期処理をそのままにすると、プロセスがゾンビ化し、メモリを浪費する原因となる。
—
結びに:エンジニアの誇り
VBScriptは古いが、その上で動くWindowsの基盤は依然として強力だ。C#の動的コンパイルを組み合わせることで、君のツールは「動けばいいだけのスクリプト」から「堅牢なミドルウェア」へと進化する。
もし、将来的に処理がさらに重くなることが予想されるのであれば、その時は素直にC#でDLLを作成し、COMコンポーネントとして登録する設計に移行すべきだ。しかし、まずはこの「PowerShellブリッジ」で、既存の自動化の壁を軽々と超えていってほしい。
技術とは、制約を言い訳にするためにあるのではない。制約をハックして、価値を最大化するためにあるのだ。健闘を祈る。
