「デバイスの準備ができていません」を根絶せよ:Drive.IsReadyによる堅牢なリムーバブルメディア制御の極意
現場のエンジニアが最も嫌うもの、それは「環境依存による予測不能なランタイムエラー」だ。
開発環境のローカルディスクでは完璧に動作するスクリプトが、工場の制御PCや現場の端末にデプロイされた途端、光学メディアの抜き差しやUSBメモリの未挿入によって `Runtime Error ’76’: Path not found` や `Error ’70’: Permission denied` を吐いて沈黙する。これはバグではない。「ハードウェアの状態という非決定論的な要素」を考慮に入れていない、設計の欠陥である。
本稿では、VBScriptにおけるFileSystemObject(FSO)を使い、リムーバブルメディアへのアクセスを極めて安全に行うための、プロフェッショナルな実装手法を詳解する。
1. 抽象化の罠:なぜエラーは発生するのか
FileSystemObjectは、Windowsのファイルシステムを操作するための強力なCOMオブジェクトである。しかし、FSOはあくまで「論理的なファイルシステム」を抽象化しているに過ぎない。
物理的なドライブ(CD-ROMやUSBメモリ)は、OSの論理階層には存在していても、メディア(ディスク)が挿入されていない状態では、その中身(ルートディレクトリやファイルリスト)にアクセスしようとした瞬間に、OSレベルのI/Oリクエストが失敗する。この失敗がCOMレイヤーを通じてVBScriptに伝播したとき、我々は無慈悲なランタイムエラーに直面することになる。
この「論理的な存在」と「物理的な準備完了」のギャップを埋めるのが、`Drive.IsReady` プロパティである。
2. `Drive.IsReady` の真価と実装の作法
`IsReady` プロパティは、単なるBoolean値を返すだけではない。それは「そのドライブが、ファイルシステム操作を受け入れられる物理的な状態にあるか」を判定する門番である。
多くの初級者は、`On Error Resume Next` でエラーを握りつぶすことで解決を図ろうとする。しかし、それは技術的負債を積み上げる行為に他ならない。真のエンジニアは、エラーが起きる前に、状態を検知して回避する。
堅牢なドライブスキャン・テンプレート
以下に、システム管理者が現場でそのまま利用できる、極限まで堅牢性を高めた実装例を示す。このコードは、利用可能なリムーバブルメディアを特定し、安全にそのパスを取得することを目的としている。
'**
' Module: SecureDriveAccess
' Description: リムーバブルメディアの安全な検出とパス取得
' Author: Chief Architect
'**
Option Explicit
Dim fso, drv, targetPath
Set fso = CreateObject("Scripting.FileSystemObject")
' 実行プロセス
targetPath = GetReadyRemovableDrivePath(fso)
If targetPath <> "" Then
WScript.Echo "Ready to process: " & targetPath
' ここに後続のファイル操作ロジックを記述する
Else
WScript.Echo "Error: No ready removable media detected."
WScript.Quit 1
End If
' オブジェクトの明示的解放(メモリ管理の鉄則)
Set fso = Nothing
'------------------------------------------------------------------------------
' Function: GetReadyRemovableDrivePath
' Purpose: 準備が完了しているリムーバブルドライブのパスを返す
'------------------------------------------------------------------------------
Function GetReadyRemovableDrivePath(objFSO)
Dim objDrive, strPath
strPath = ""
' エラーハンドリングの局所化
' 意図しないエラーの伝播を防ぐため、極力範囲を限定する
On Error Resume Next
For Each objDrive In objFSO.Drives
' 1. ドライブタイプによるフィルタリング (2: Removable, 5: CD-ROM)
' ネットワークドライブや固定ディスクを排除し、対象を絞り込む
If objDrive.DriveType = 2 Or objDrive.DriveType = 5 Then
' 2. 【重要】IsReadyによる物理状態の確認
' これを行わずに objDrive.Path や RootFolder に触れることは自殺行為である
If objDrive.IsReady Then
strPath = objDrive.Path
Exit For ' 最初に見つかった準備完了ドライブを返す
End If
End If
Next
If Err.Number <> 0 Then
' 万が一、ループ内で予期せぬエラーが発生した場合のログ出力
' WScript.Echo "Critical Error: " & Err.Description
Err.Clear
End If
On Error GoTo 0
' 3. オブジェクトのライフサイクル管理
' ループ内で生成されたオブジェクトは、スコープを抜ける際に適切に処理される必要がある
' ただし、Drivesコレクションの要素はFSOが管理しているため、個別のNothingは不要
GetReadyRemovableDrivePath = strPath
End Function
3. アーキテクトの視点:メモリとオブジェクトのライフサイクル
VBScriptはガベージコレクション(GC)を持つが、COMオブジェクトの解放に関しては、我々エンジニアが「明示的」に振る舞うことが、長期稼働するシステムにおいては不可欠である。
なぜ `Set obj = Nothing` が必要なのか
1. リソースの即時解放: `FileSystemObject` は、OSのファイルハンドルやメモリリソースを大量に消費する可能性がある。特に、ループ内で繰り返しオブジェクトを生成する場合、GCが走る前にメモリが枯渇するリスクがある。
2. 参照カウントの制御: COMオブジェクトは参照カウント方式で管理されている。スクリプトが終了するまでオブジェクトを保持し続けることは、システムの安定性を損なう。
3. デッドロックの回避: 稀なケースだが、解放されていないオブジェクトがファイルハンドルを掴み続けることで、他のプロセスによる操作がブロックされる事態を招く。
プロフェッショナルなコードでは、使い終わったオブジェクトに対しては、スコープの終わり、あるいは論理的な区切りにおいて、迷わず `Set obj = Nothing` を宣言すべきである。
4. レガシー環境における保守と拡張性
この手法は、Windows 2000/XPといったレガシーな環境から、最新のWindows 11に至るまで、一貫した挙動を示す。これは、FSOがWindowsのコアなCOMコンポーネントとして極めて高い互換性を維持しているからである。
将来的に、このスクリプトをVB.NETやC#へと移植する場合でも、ロジックの根幹(`DriveType` の判定 $\rightarrow$ `IsReady` の確認 $\rightarrow$ `Path` の取得)を変える必要はない。この「設計思想」こそが、技術の世代を超えて受け継がれるべき資産である。
結論
「動けば良い」というコードは、現場では「凶器」となる。
`Drive.IsReady` を活用した事前判定は、単なるエラー回避テクニックではない。それは、不確実なハードウェア環境に対して、ソフトウェアが「予測可能な挙動」を保証するための、エンジニアリングにおける誠実さの現れである。
堅牢なコードを書け。エラーを予測し、オブジェクトを統制せよ。それが、伝説と呼ばれるアーキテクトへの唯一の道である。
