【引数処理の極意】WScript.Argumentsで制す、ドラッグ&ドロップ自動化の要塞設計
業務自動化の現場において、VBScript(Visual Basic Scripting Edition)いまだその価値を失っていない。GUIを作り込むまでもない、しかし手動オペレーションではミスが頻発する単一・複数ファイルのバッチ処理。この領域において、VBScriptとWSH(Windows Script Host)の組み合わせほど、素早く、環境依存なくデプロイできるツールはない。
特に、スクリプトのアイコンへファイルやフォルダを「ドラッグ&ドロップ」するだけで起動する仕組みは、非エンジニアの現場担当者に「最高に使いやすい自動化ツール」を提供するための黄金パターンだ。
しかし、雑に書かれた `WScript.Arguments` の処理は、空白入りのパスで容易にクラッシュし、複数選択時の文字コード問題や、存在しないパスの指定といった実務の罠で簡単に沈没する。
今回は、プロダクション環境(現場の実務)に耐えうる、極限まで堅牢なドラッグ&ドロップ自動化スクリプトの設計思想と実装を伝授する。
—
1. なぜ「雑な引数処理」は現場で破綻するのか?
多くの解説記事では、`WScript.Arguments` の使い方として以下のようなコードが平然と紹介されている。
‘ 【アンチパターン】絶対に真似してはいけないコード
Set args = WScript.Arguments
For i = 0 to args.Count – 1
MsgBox args(i)
Next
この実装が現場で一蹴される理由は明確だ。
1. 空白を含むパスの扱いの脆弱性: コマンドライン経由、あるいはWSHの仕様において、スペースを含むファイル名(例: `C:\My Documents\data.csv`)が渡された際、適切に引用符が処理されないケースや、動的な配列操作の不備でパスが分断される。
2. 型とコレクションの曖昧さ: `WScript.Arguments` は通常のコレクションとは異なり、厳密な型安全性が欠如している。要素が0個の時の挙動や、予期せぬオブジェクト型枠の混入に対する防御がない。
3. エラーハンドリングの欠如: ドラッグされたのがファイルなのかフォルダなのか、あるいはアクセス権限のないリソースなのかを判定せず処理に突入するため、予期せぬスクリプトエラー(VBScript特有の容赦ないダイアログ)で業務が止まる。
プロフェッショナルたるもの、「入力値は必ず汚染されている、あるいは予期せぬ形式で渡される」という性悪説に基づいた設計を行わなければならない。
—
2. 堅牢な引数処理のアーキテクチャ
実務で使えるスクリプトには、以下の3層構造が必須となる。
1. ガード節(Guard Clauses): 引数が1つも渡されなかった場合の優雅な離脱(使い方ガイドの提示)。
2. 正規化とパス検証(Validation): 渡された引数が本当に有効なファイル/フォルダか、FileSystemObjectを用いて厳密に判定する。
3. 一括・個別ディスパッチ(Dispatcher): 単一ファイル、複数ファイル、あるいはフォルダドロップといった異なるユースケースを分岐処理する。
—
3. 【プロダクションコード】コピペで動く最強のD&D自動化テンプレート
以下のコードは、デスクトップ上に配置したVBScriptのアイコンへ、任意のファイル群(またはフォルダ)をドラッグ&ドロップすることで、ログ出力付きで安全に一括処理を行うテンプレートである。
‘ ==============================================================================
‘ スクリプト名: DndBatchProcessor.vbs
‘ 概要: ドラッグ&ドロップされたファイル/フォルダを安全に一括処理するプロダクションコード
‘ 依存関係: WSH, Scripting.FileSystemObject
‘ ==============================================================================
Option Explicit
‘ メイン処理の実行
Main()
Sub Main()
Dim fso, args, targetPath
Dim successCount, errorCount
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set args = WScript.Arguments
‘ ————————————————————————–
‘ [第1層] ガード節: 引数が渡されていない場合は使い方を表示して終了
‘ ————————————————————————–
If args.Count = 0 Then
MsgBox “【使い方】” & vbCrLf & _
“処理したいファイルまたはフォルダを、このスクリプトのアイコンにドラッグ&ドロップしてください。”, _
vbInformation, “D&Dバッチプロセッサ”
Exit Sub
End If
successCount = 0
errorCount = 0
‘ ————————————————————————–
‘ [第2層] 引数のループ処理とバリデーション
‘ ————————————————————————–
Dim i, itemPath
For i = 0 To args.Count – 1
itemPath = args(i)
‘ パスの存在確認
If fso.FileExists(itemPath) Then
‘ — [ファイルがドロップされた場合の処理] —
If ProcessFile(fso, itemPath) Then
successCount = successCount + 1
Else
errorCount = errorCount + 1
End If
ElseIf fso.FolderExists(itemPath) Then
‘ — [フォルダがドロップされた場合の処理] —
If ProcessFolder(fso, itemPath) Then
successCount = successCount + 1
Else
errorCount = errorCount + 1
End If
Else
‘ 存在しない、またはアクセス不可のパス
errorCount = errorCount + 1
‘ 実務ではここでログ出力関数を呼ぶのが望ましい
End If
Next
‘ ————————————————————————–
‘ [第3層] 処理結果のサマリー通知
‘ ————————————————————————–
MsgBox “一括処理が完了しました。” & vbCrLf & _
“———————————-” & vbCrLf & _
” 成功: ” & successCount & ” 件” & vbCrLf & _
” 失敗: ” & errorCount & ” 件”, _
vbInformation, “処理完了”
Set fso = Nothing
Set args = Nothing
End Sub
‘ ==============================================================================
‘ 個別ファイル処理関数
‘ ==============================================================================
Function ProcessFile(fso, filePath)
On Error Resume Next
Dim fileObj, ext
Set fileObj = fso.GetFile(filePath)
ext = LCase(fso.GetExtensionName(filePath))
‘ 例として、CSVファイルのみを処理対象とする条件分岐
Select Case ext
Case “csv”
‘ — ここに実際の業務ロジック(CSVの解析・加工など)を記述 —
‘ 便宜上、ここではコンソール出力(cscript実行時)の代わりに処理成功とみなす
Case Else
‘ 対象外の拡張子はスキップ(必要に応じてエラー扱いにする)
ProcessFile = True
Exit Function
End Select
If Err.Number <> 0 Then
‘ エラーログ書き込み等の処理をここに実装
ProcessFile = False
Err.Clear
Else
ProcessFile = True
End If
On Error GoTo 0
End Function
‘ ==============================================================================
‘ フォルダ処理関数(配下のファイル一括処理への応用)
‘ ==============================================================================
Function ProcessFolder(fso, folderPath)
On Error Resume Next
Dim folderObj, subFile
Set folderObj = fso.GetFolder(folderPath)
‘ フォルダ直下のファイルをすべて舐めて処理する場合のサンプル
For Each subFile in folderObj.Files
‘ 必要に応じて再帰処理や拡張子フィルターを実装
Next
If Err.Number <> 0 Then
ProcessFolder = False
Err.Clear
Else
ProcessFolder = True
End If
On Error GoTo 0
End Function
—
4. チーフアーキテクトが教える「現場で活きる」実装の急所
このコードを実務に投入する際、エンジニアとして押さえておくべき知見をいくつか共有しよう。
1. `Option Explicit` は宗教ではなく法規
VBScriptにおいて変数の宣言を強制しないのは百害あって一利なしだ。タイポによるバグをコンパイル(実行時初期)段階で100%弾くために、必ずファイルの先頭に `Option Explicit` を記述すること。
2. エラーハンドリングのスコープ管理
VBScriptの `On Error Resume Next` は毒薬であり、グローバルに有効にするとバグの温床になる。上記のテンプレートのように、「リスクのある局所的な関数内」でのみエラーをトラップし、直後に `On Error GoTo 0` で標準挙動に戻すのがモダンな例外処理の作法である。
3. 文字コード(BOM)の罠への備え
ドラッグ&ドロップされたファイルを読み込む際、VBScriptの `ADODB.Stream` や `TextStream` を使うことになるが、UTF-8(BOMなし)やShift-JISが混在する環境では文字化けが起きる。ファイル内容を読み込ませて処理するモジュールを拡張する場合は、文字コードの自動判定またはUTF-8指定(`Adodb.Stream` の `Charset = “UTF-8″`)を徹底すること。
—
総括
VBScriptはレガシーな言語と揶揄されることもあるが、Windows環境において「インストールの手間なく、エクスプローラから直結で動く自動化ツール」を作るためのフットワークの軽さは、いまだ他の追随を許さない。
`WScript.Arguments` を制する者は、ローカル自動化のUX(ユーザーエクスペリエンス)を制する。今回紹介した堅牢な設計パターンをベースに、あなたの現場のノンプログラマーたちへ「最高にストレスフリーな自動化ツール」を届けてほしい。
