【テクニカル・上級編】【デバイス監視】DriveTypeプロパティでリムーバブルメディアやネットワークドライブを識別・自動処理するVBScript – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【デバイス監視】DriveTypeプロパティでリムーバブルメディアやネットワークドライブを識別・自動処理するVBScript

Windowsシステム管理の現場において、レガシーと切り捨てられがちなVBScript(Visual Basic Scripting Edition)は、今なお「フットプリントの軽さ」と「Active DirectoryやWMIとの極めて高い親和性」において無類の強みを発揮する。特に、PowerShellの起動オーバーヘッドや実行ポリシー制限を回避しつつ、バックグラウンドでデバイスの挿入を監視し、特定の処理をキックするような軽量エージェントを構築するケースにおいて、VBScript+FSO(FileSystemObject)+WMIの組み合わせは現在でも最適解となり得る。

本稿では、`Scripting.FileSystemObject`の`DriveType`プロパティを利用したドライブ判別ロジックの極限を解説する。単なる入門コードに留まらず、実務で耐えうる「メモリリーク対策」「CPU高負荷回避」「WMIイベント駆動によるデバイス監視」の設計手法を提示する。

—

1. FileSystemObjectにおける「DriveType」の真実

FSOの`Drives`コレクションを走査する際、各`Drive`オブジェクトが持つ`DriveType`プロパティは、ストレージメディアの物理的・論理的性質を識別する最も安価な手段である。

DriveType定数一覧

| 定数名 | 値 | 意味 | 実務における挙動と注意点 |
| :— | :— | :— | :— |
| Unknown | `0` | 判別不能 | 初期化中、あるいは特殊な仮想デバイス。通常は無視する。 |
| Removable | `1` | リムーバブルメディア | USBメモリ、SDカードリーダー、一部の外付けHDD。 |
| Fixed | `2` | 固定ディスク | 内蔵HDD/SSD。システムドライブ(C:)やデータドライブ。 |
| Network | `3` | ネットワークドライブ | NASやファイルサーバ上の共有フォルダ(UNCパスへのマウント)。 |
| CDRom | `4` | CD-ROM/DVD-ROM | 光学ドライブ。メディアが未挿入の場合の実行時エラーに注意。 |
| RamDisk | `5` | RAMディスク | メモリ上に作成された高速一時領域。 |

FSO単体運用の限界と「メディア未挿入」トラップ

FSOは非常にシンプルにドライブ情報を取得できるが、重大な罠が存在する。例えば、SDカードリーダーなどの「スロットはあるがメディアが挿入されていないリムーバブルドライブ」や「光学ドライブ」に対して、不用意に`Path`や`VolumeName`、`FreeSpace`などのプロパティにアクセスすると、「書き込み準備ができていません(エラーコード: 71)」や「デバイスの準備ができていません」というランタイムエラーが発生し、スクリプトが異常終了する。

これを防ぐためには、プロパティ参照前に必ず`IsReady`プロパティを検証しなければならない。

‘ エラーを誘発する危険なコード
For Each objDrive In objFSO.Drives
‘ メディアが挿入されていない場合、ここでランタイムエラーが発生する
WScript.Echo objDrive.VolumeName
Next

‘ 堅牢なプロフェッショナルコード
For Each objDrive In objFSO.Drives
If objDrive.IsReady Then
WScript.Echo objDrive.VolumeName
Else
WScript.Echo objDrive.Path & ” [未挿入/準備未完了]”
End If
Next

—

2. 実践コード:USB挿入を検知して自動バックアップを実行する(WMIイベント駆動)

一般的にデバイスの監視というと、`Do…Loop`によるポーリング(一定時間ごとの巡回)を想像しがちだが、FSOを無限ループで回す設計は、CPUクロックを無駄に消費し、ディスクI/Oを発生させ続けるため避けるべきである。

ここでは、Windowsのカーネルイベント(WMIのインスタンス作成イベント)を捕捉し、「USBメモリが挿入された瞬間」にのみ実処理を起動する、イベント駆動型(ノン・ポーリング)の極小フットプリント・スクリプトを提示する。

実装コード(DeviceMonitor.vbs)

以下のコードは、バックグラウンドプロセスとして常駐し、USBドライブ(Removable)が接続されたことを検知すると、そのドライブ内に特定のシグネチャファイルが存在するか確認し、存在する場合のみ機密データの自動バックアップ(または同期処理)を開始する。

Option Explicit

‘ —————————————————————————–
‘ 構造定義および定数定義
‘ —————————————————————————–
Const DRIVE_TYPE_REMOVABLE = 1
Const SIGNATURE_FILE = “.backup_target” ‘ 誤動作防止用のシグネチャファイル
Const TARGET_DIR = “C:\LocalBackupStore” ‘ バックアップ元ローカルディレクトリ

‘ —————————————————————————–
‘ メインエントリポイント
‘ —————————————————————————–
Call BlockAndMonitorDevice()

Sub BlockAndMonitorDevice()
Dim objWMIService, colMonitoredEvents, objLatestEvent
Dim objFSO, objDrive
Dim strDriveLetter, strBackupPath

‘ エラーハンドリングの定義
On Error Resume Next

‘ WMIサービスへの接続(ローカル名前空間へのアクセス)
Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2”)
If Err.Number <> 0 Then
Call WriteLog(“WMI初期化エラー: ” & Err.Description, True)
WScript.Quit 1
End If

‘ FSOのインスタンス化
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

‘ WQL (WMI Query Language) を用いた、論理ディスク作成イベントの監視
‘ 2秒間隔でハードウェアの変更をクエリする(イベントドリブン型ポーリング)
Set colMonitoredEvents = objWMIService.ExecNotificationQuery(_
“Select From __InstanceCreationEvent Within 2 Where ” _
& “TargetInstance ISA ‘Win32_LogicalDisk'”)

If Err.Number <> 0 Then
Call WriteLog(“WQLクエリ実行エラー: ” & Err.Description, True)
Set objFSO = Nothing
WScript.Quit 1
End If
On Error GoTo 0

Call WriteLog(“デバイス監視サービスを開始しました。USBの挿入を待機しています…”, False)

‘ 永久監視ループ(イベント受信までスレッドはブロックされ、CPU負荷はほぼゼロとなる)
Do
‘ 次のインシデント(デバイス挿入)が発生するまでここでスレッドをサスペンド
Set objLatestEvent = colMonitoredEvents.NextEvent()

‘ 挿入されたドライブのドライブレター(例: “E:”)を取得
strDriveLetter = objLatestEvent.TargetInstance.DeviceId

‘ FSOを用いて該当ドライブのオブジェクトを取得
If objFSO.DriveExists(strDriveLetter) Then
Set objDrive = objFSO.GetDrive(strDriveLetter)

‘ ドライブタイプの厳密な判定
If objDrive.DriveType = DRIVE_TYPE_REMOVABLE Then
If objDrive.IsReady Then
Call WriteLog(“リムーバブルデバイスを検出: ” & strDriveLetter, False)

‘ 特定のUSBメモリのみを対象にするためのシグネチャ検証
If objFSO.FileExists(strDriveLetter & “\” & SIGNATURE_FILE) Then
strBackupPath = strDriveLetter & “\AutoBackup_” & GetFormattedTimestamp()
Call WriteLog(“シグネチャ確認成功。バックアップを開始します。宛先: ” & strBackupPath, False)

‘ 同期処理の実行
Call ExecBackup(objFSO, TARGET_DIR, strBackupPath)
Else
Call WriteLog(“シグネチャファイルが存在しないため、このデバイスは無視されます。”, False)
End If
End If
End If

‘ メモリリーク防止:スコープ内オブジェクトの明示的解放
Set objDrive = Nothing
End If

‘ イベントオブジェクトの解放
Set objLatestEvent = Nothing

‘ インターバル(OSへのコンテキストスイッチを明示的に譲渡)
WScript.Sleep 500
Loop

‘ デストラクタ処理(通常は到達しないが、構造的整合性のために記述)
Set objFSO = Nothing
Set colMonitoredEvents = Nothing
Set objWMIService = Nothing
End Sub

‘ —————————————————————————–
‘ サブルーチン:バックアップ処理(FSOベースのセーフコピー)
‘ —————————————————————————–
Sub ExecBackup(ByRef objFSO, ByVal strSource, ByVal strDestination)
On Error Resume Next
‘ バックアップ先フォルダの作成
If Not objFSO.FolderExists(strDestination) Then
objFSO.CreateFolder(strDestination)
End If

‘ FSOによるフォルダコピー(True = 上書き許可)
objFSO.CopyFolder strSource, strDestination, True

If Err.Number = 0 Then
Call WriteLog(“バックアップ処理が正常に完了しました。”, False)
Else
Call WriteLog(“バックアップエラー発生: ” & Err.Description, True)
End If
On Error GoTo 0
End Sub

‘ —————————————————————————–
‘ ユーティリティ関数群
‘ —————————————————————————–
Function GetFormattedTimestamp()
Dim dt
dt = Now()
GetFormattedTimestamp = Year(dt) & _
Right(“0” & Month(dt), 2) & _
Right(“0” & Day(dt), 2) & “_” & _
Right(“0” & Hour(dt), 2) & _
Right(“0” & Minute(dt), 2) & _
Right(“0” & Second(dt), 2)
End Function

Sub WriteLog(ByVal strMessage, ByVal isError)
Dim objShell, strLogType
‘ WshShellを用いてイベントログ(Application)へ書き込み
Set objShell = CreateObject(“WScript.Shell”)
If isError Then
strLogType = 1 ‘ エラー
Else
strLogType = 4 ‘ 情報
End If
objShell.LogEvent strLogType, “[DeviceMonitor] ” & strMessage
Set objShell = Nothing
End Sub

—

3. チーフアーキテクトが説く、実務運用の「極限」知見

① WMI `ExecNotificationQuery` によるCPU負荷ゼロの実現

上記のスクリプトは、単純な`Do…Loop`でドライブ一覧を走査(ポーリング)していない。
`colMonitoredEvents.NextEvent()` を呼び出すと、OSのI/Oサブシステムが「新しい論理ディスク(`Win32_LogicalDisk`)の追加インスタンス」を検出するまで、スクリプトのスレッドを完全にサスペンド(待機状態)にする。
これにより、ポーリング処理で発生しがちな「CPU使用率常時15%~30%」といったリソースの無駄遣いを防ぎ、常時起動していてもパフォーマンスに一切の影響を与えない、真に実用的なサービスプロセスと同等の挙動を実現している。

② VBScriptにおける「メモリ管理(COM参照カウント)」の厳密な制御

VBScriptは参照カウント方式のガベージコレクション(GC)を採用しているが、特に無限ループ処理やWMIイベントハンドラ内において、インスタンス化したCOMオブジェクト(`objDrive`、`objLatestEvent`など)を明示的に解放しない場合、メモリリークを誘発し、最悪の場合はシステムリソースを食いつぶしてプロセスがクラッシュする。

‘ ループ内での明示的解放は必須
Set objDrive = Nothing
Set objLatestEvent = Nothing

上記のように、ループの末尾で必ず `Nothing` を代入し、COM参照カウントをデクリメントしてメモリを即座に解放することが、数ヶ月連続で稼働させる管理スクリプトにおける大前提である。

③ セキュリティ上のアプローチ:シグネチャ検証

USBデバイスが接続されたからといって、無条件でバックアップなどの処理を走らせる設計は極めて危険である。第三者の私有USBや、無関係な外部メディアが挿入された場合に機密情報が混入・流出するリスクを孕む。
本実装では、特定のシグネチャファイル(例: 空の隠しファイル `.backup_target`)がドライブ直下に存在するかどうかを検証し、許可された特定の物理デバイスのみを処理対象とする「簡易的な検疫ホワイトリスト方式」を採用している。

④ エラー処理とイベントログへの還元

エンタープライズの運用では、コンソールへの `WScript.Echo` は厳禁である。バックグラウンドでサイレントに動作させるため、エラーメッセージや進捗は `WScript.Shell` の `LogEvent` メソッドを利用して、Windowsイベントログ(アプリケーションログ)へ直接書き込む。これにより、統合監視ツール(ZabbixやDatadog、SIEM等)によるイベントログ監視の網に引っ掛けることが可能となる。

—

4. 総括

VBScriptによるデバイス監視とFSO操作は、枯れた技術だからこそ、その「挙動の予測可能性」と「動作の確実性」において現代でも非常に高い価値を持つ。

本稿で解説した「WMIイベント駆動によるFSOドライブ情報の取得と制御」をベースに、要件に応じてxcopy、Robocopy、あるいはPowerShellスクリプトのセキュアな呼び出しなどと組み合わせることで、完全ノンインテリジェント、かつ堅牢な自動化インフラを構築していただきたい。

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