【テクニカル・上級編】【実務中級者向け】GetDriveとVolumeNameを活用した「特定のラベル名を持つUSBメモリ」の自動検出と特定ファイル退避 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握する極限の知見:ボリューム名ベースのUSB自動検出と堅牢なファイル退避アーキテクチャ

レガシーシステムの現場において、VBScriptは今なおインフラの裏側で静かに、しかし強固に稼働し続けている。
特に「OSの標準機能だけで動作する」「追加のランタイムインストールが不要である」という特性は、閉じたネットワーク環境や専用端末の自動化において代替不可能な価値を持つ。

本稿で取り上げるのは、業務自動化における永遠の課題――「ドライブレター(E:, F: 等)の動的な変動問題」の完全な克服である。

USBメモリや外付けHDDを差し込むたびに割り当てが変わるドライブレターに依存したスクリプトは、実務において爆弾を抱えているようなものだ。
今回は、`FileSystemObject (FSO)` の `GetDrive` メソッドと `VolumeName` プロパティを極限まで活用し、特定のボリュームラベルを持つ物理メディアを動的に特定、安全にファイルを同期・退避させるプロダクション品質のVBScriptアーキテクチャを解説する。

—

1. アーキテクチャの核心:なぜ `GetDrive` なのか

多くの初学者は、ドライブの走査において `FSO.Drives` コレクションを安易にループさせ、エラーハンドリングを怠る。
しかし、VBScriptによる堅牢なシステム構築において重要なのは、「未接続メディアへのアクセスによるI/O例外の完全な回避」と「オブジェクトのライフサイクル管理」だ。

ドライブレター(`A:` から `Z:`)をハードコーディングするのは論外だが、単に `Drives` を回すだけでは、光学ドライブやネットワークドライブへの無駄なアクセスが発生し、最悪の場合、メディア未挿入のリムーバブルドライブに対してシステムダイアログ(「ドライブにディスクを入れてください」)がポップアップし、スクリプトが完全停止する。

これを防ぐためには、以下の鉄則を守る必要がある。
1. `DriveType = 1`(Removable:リムーバブルディスク)に限定して走査する。
2. `IsReady` プロパティでメディアの物理的な応答性を確認してから `VolumeName` にアクセスする。
3. 取得したオブジェクトは、スコープを抜け次第、明示的に `Nothing` を代入してメモリリークを防ぐ。

—

2. 実装コード:プロダクション仕様のUSB自動バックアップスクリプト

以下のコードは、実務の現場でそのまま投入できる完全版のVBScriptである。
特定のボリューム名(例: `BACKUP_USB`)を持つデバイスを動的に探し出し、ローカルの特定ディレクトリからファイルをミラーリング(退避)する。

‘ ==============================================================================
‘ Script Name : AutoBackupToSpecificUSB.vbs
‘ Description : ボリューム名でUSBを動的特定し、指定ファイルを安全に退避する
‘ Architecture: FSO (FileSystemObject) & Shell.Application
‘ ==============================================================================
Option Explicit

‘ 定数定義
Const TARGET_VOLUME_NAME = “BACKUP_USB” ‘ ターゲットとするUSBのボリューム名
Const SOURCE_DIR = “C:\AppLogs\Export” ‘ 退避元ローカルフォルダ
Const LOG_FILE_PATH = “C:\AppLogs\backup_execution.log”

Sub Main()
Dim fso, targetDriveLetter, sourcePath, destPath
Dim logger

‘ 1. オブジェクトの生成
Set fso = WScript.CreateObject(“Scripting.FileSystemObject”)
Set logger = New SimpleLogger

logger.Write “=== バックアッププロセス開始 ===”

‘ 2. 退避元パスの検証
If Not fso.FolderExists(SOURCE_DIR) Then
logger.Write “エラー: 退避元フォルダが存在しません -> ” & SOURCE_DIR
CleanUp fso, logger, Nothing
WScript.Quit 1
End If

‘ 3. ターゲットボリュームを持つドライブレターの動的解決
targetDriveLetter = FindDriveByVolumeName(fso, TARGET_VOLUME_NAME, logger)

If targetDriveLetter = “” Then
logger.Write “警告: ターゲットボリューム ‘” & TARGET_VOLUME_NAME & “‘ を持つUSBが見つかりません。”
CleanUp fso, logger, Nothing
WScript.Quit 0 ‘ 媒体未接続は異常終了とせず正常終了(タスクスケジューラ対策)
End If

logger.Write “ターゲットUSBを検出しました。ドライブ: ” & targetDriveLetter

‘ 4. 退避先パスの構築とディレクトリ存在確認
destPath = targetDriveLetter & “\AutoBackup_” & FormatDateYYYYMMDD(Now)
If Not fso.FolderExists(destPath) Then
fso.CreateFolder(destPath)
logger.Write “退避先フォルダを作成しました: ” & destPath
End If

‘ 5. ファイルのコピー実行(エラーハンドリング付き)
On Error Resume Next
fso.CopyFolder SOURCE_DIR & “\”, destPath & “\”, True
If Err.Number <> 0 Then
logger.Write “致命的エラー: ファイルコピー中に障害が発生しました. ವಿವರ: ” & Err.Description
Err.Clear
Else
logger.Write “ファイル退避が正常に完了しました -> ” & destPath
End If
On Error GoTo 0

‘ 6. クリーンアップ
CleanUp fso, logger, Nothing
logger.Write “=== バックアッププロセス終了 ===”
End Sub

‘ ——————————————————————————
‘ ボリューム名からリムーバブルドライブのレターを特定する関数
‘ ——————————————————————————
Function FindDriveByVolumeName(fso, targetName, logger)
Dim drives, d
FindDriveByVolumeName = “”

Set drives = fso.Drives
For Each d In drives
‘ ドライブタイプ 1 = Removable (USBメモリ等)
If d.DriveType = 1 Then
‘ メディアが挿入されており、アクセス可能な状態かチェック
If d.IsReady Then
‘ 比較時に大文字小文字の揺れを防止するため UCase を推奨
On Error Resume Next
If UCase(d.VolumeName) = UCase(targetName) Then
FindDriveByVolumeName = d.DriveLetter ‘ 例: “E”
On Error GoTo 0
Exit For
End If
On Error GoTo 0
End If
End If
Next

‘ コレクションの解放
Set drives = Nothing
End Function

‘ ——————————————————————————
‘ 日付フォーマット関数 (YYYYMMDD)
‘ ——————————————————————————
Function FormatDateYYYYMMDD(dt)
Dim y, m, d
y = CStr(Year(dt))
m = Right(“0” & CStr(Month(dt)), 2)
d = Right(“0” & CStr(Day(dt)), 2)
FormatDateYYYYMMDD = y & m & d
End Function

‘ ——————————————————————————
‘ 簡易ロガークラス(ファイル出力用)
‘ ——————————————————————————
Class SimpleLogger
Private fso, ts

Private Sub Class_Initialize()
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 追記モード (ForAppending = 8), 存在しない場合は作成 (True = -1)
On Error Resume Next
Set ts = fso.OpenTextFile(LOG_FILE_PATH, 8, True)
On Error GoTo 0
End Sub

Public Sub Write(msg)
Dim logLine
logLine = “[” & Now & “] ” & msg
WScript.Echo logLine ‘ コンソール実行時の出力
If Not ts Is Nothing Then
ts.WriteLine logLine
End If
End Sub

Private Sub Class_Terminate()
If Not ts Is Nothing Then
ts.Close
Set ts = Nothing
End If
Set fso = Nothing
End Sub
End Class

‘ ——————————————————————————
‘ メモリ最適化のための確実なオブジェクト解放
‘ ——————————————————————————
Sub CleanUp(fso, logger, dummy)
Set logger = Nothing
Set fso = Nothing
End Sub

‘ 実行のエントリーポイント呼び出し
Main

—

3. チーフアーキテクトが解説するコードの急所

上記のスクリプトは単に動くだけでなく、エンタープライズ環境での運用に耐えうるよう、いくつかの高度な設計パターンを取り入れている。

① `DriveType` と `IsReady` の二重防壁

FSOの `Drives` コレクションを走査する際、ネットワークドライブ(DriveType = 3)やCD-ROMドライブ(DriveType = 4)が含まれていると、未接続やタイムアウトによってスクリプトがフリーズするリスクがある。
`DriveType = 1`(リムーバブル)で絞り込んだ上で、必ず `IsReady` プロパティを評価している点がこのコードの生命線である。これにより、物理メディアが挿入されていないカードリーダーやUSBポートに起因するCOMエラーを完全にシャットアウトする。

② VBScriptにおけるメモリ管理とガベージコレクションの罠

VBScript(COMコンポーネント)は参照カウント方式によってメモリ管理を行っている。スクリプトが終了すればプロセスごとメモリは解放されるが、長時間の常駐プロセスやタスクスケジューラで高頻度にキックされるバッチにおいては、明示的な `Set x = Nothing` の記述が不可欠である。
特に `FileSystemObject` やコレクションオブジェクト、独自に定義したClass(今回の `SimpleLogger`)は、スコープを抜けるタイミングで意図的に参照を切ることで、COMの解放漏れによるメモリリークを防ぐ。

③ タスクスケジューラ運用を意識した終了コード設計

タスクスケジューラからバッチとして実行する場合、対象メディアが挿入されていない状態(=「本日分はまだUSBが刺さっていない」)は、システム的には「正常系(異常終了ではない)」として扱うべきである。
エラーとして終了コード `1` を返すと、管理者に不要なアラートメールが飛ぶ原因になる。そのため、ターゲットが見つからない場合は `WScript.Quit 0` で静かに終了する設計としている。

—

4. レガシー環境の保守と拡張性

このアーキテクチャをベースに、さらなる実務的拡張を行うための指針を記す。

  • 暗号化・圧縮の統合:

退避前に `Shell.Application` を用いてフォルダをZIP圧縮し、AES等の強力なパスワード保護をかけたい場合も、特定されたドライブレター(`targetDriveLetter`)を動的パスのルートとして渡すだけで容易に統合できる。

  • ログのローテーション:

`SimpleLogger` クラスを拡張し、ログファイルが一定サイズを超えた場合に `.bak` へ退避するロジックを組み込むことで、ディスクフル障害を未然に防ぐことが可能だ。

VBScriptは「古い言語」ではない。OSの深層をダイレクトに叩き、余計なオーバーヘッドなしにインフラを自動化する「研ぎ澄まされた刃」である。
オブジェクトの挙動とOSの制約を完全に理解した上で記述されたコードは、現代の複雑なフレームワーク群をも凌駕する圧倒的な安定性を現場にもたらすだろう。

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