盲目的なI/Oを排せ:WMIとFSOを同期させた、ストレージの「死」を予兆する防衛的ファイル操作術
現場のエンジニアは、しばしば「エラーハンドリング」を、実行後の`On Error Resume Next`による事後処理だと勘違いしている。だが、それはエンジニアリングではない。ただの「事後報告」だ。
真に堅牢なシステムを設計するアーキテクトが注視すべきは、I/Oが発生する前の状態だ。物理セクタの劣化、ファイルシステムのDirty Bit、あるいは書き込み保護状態。これらを無視して`FileSystemObject`(FSO)の`CopyFile`や`CreateTextFile`を叩き込む行為は、地雷原を全速力で疾走するに等しい。
本稿では、VBScriptの限界を極限まで押し広げ、WMI(Windows Management Instrumentation)を用いてストレージの健全性を事前に監査し、FSOによるファイル操作を「決定論的」に行うための高度な実装パターンを解説する。
1. なぜ FSO 単体では「不十分」なのか
`FileSystemObject`は、ファイル操作における抽象化レイヤーとして極めて優秀だ。しかし、その設計思想は「ファイルシステム上の論理的な操作」に特化しており、その背後にある「物理デバイスの健康状態」には一切関知しない。
もし、書き込み先のディスクが物理的なセクタエラーを抱えていたり、ファイルシステムが不整合(Dirty Bit)を起こしていたりする場合、FSOは書き込みを実行しようとして、OSレベルのI/Oエラーを吐き出すまで気づけない。最悪の場合、書き込みの途中でプロセスがハングアップするか、不完全なデータ(Partial Write)を書き込み、データの整合性を破壊する。
我々が目指すべきは、「書き込む前に、その大地が踏み込める場所かどうかを判定する」アーキテクチャである。
2. WMIによるストレージ監査:Win32_LogicalDisk の深淵
ディスクの健全性を検知するためには、WMIの `Win32_LogicalDisk` クラスを介して、論理ドライブの `Status` プロパティを精査する必要がある。
監査すべき主要パラメータ
1. `Status`: `OK` 以外の値(`Error`, `Degraded` 等)が返ってきた場合、そのドライブは即座に「非信頼」とみなすべきである。
2. `FileSystem`: 対象のドライブが期待通りのファイルシステム(NTFS等)であることを確認する。
3. `DriveType`: ネットワークドライブやリムーバブルメディアなど、特性の異なるデバイスを区別する。
さらに高度な検知を行うなら、`Win32_DiskDrive` を参照し、物理的なSMART情報のステータスを追う手法もあるが、VBScriptの実行コンテキスト(権限)を考慮すると、まずは `Win32_LogicalDisk` による論理的な整合性チェックが最も実用的かつ確実な防衛線となる。
3. 実装:防衛的ファイル退避エンジン
以下に、FSOによるファイル操作の直前に、WMIによるストレージ監査を組み込んだプロフェッショナルな実装例を示す。このコードは、単なるスクリプトではない。オブジェクトのライフサイクル管理と、厳格なエラーハンドリングを組み込んだ、一つの「部品」として設計されている。
'**
' Module: SecureFileMover
' Description: WMIによるストレージ監査を伴う、安全なファイル操作エンジン
' Author: Chief Architect
'**
Option Explicit
' 定数定義:エラーコードの定義
Const ERR_DISK_UNHEALTHY = 1001
Const ERR_WRITE_PROTECTED = 1002
Const ERR_IO_FAILURE = 1003
' メインエントリーポイント
Call Main()
Sub Main()
Dim strSource, strDest, strDriveLetter
strSource = "C:\Data\Critical_Log.txt"
strDest = "D:\Backup\Critical_Log_Backup.txt"
strDriveLetter = "D:" ' 監査対象のドライブレター
On Error Resume Next
' 1. ストレージの健全性監査
If CheckDiskHealth(strDriveLetter) Then
' 2. 健全な場合のみ、ファイル退避を実行
If SafeFileCopy(strSource, strDest) Then
WScript.Echo "Operation Successful: File evacuated safely."
Else
WScript.Echo "Operation Failed: FSO Error occurred during copy."
End If
Else
WScript.Echo "Critical Error: Target drive " & strDriveLetter & " is unhealthy or write-protected."
End If
If Err.Number <> 0 Then
WScript.Echo "Unexpected System Error: " & Err.Description
End If
On Error GoTo 0
End Sub
'**
' Function: CheckDiskHealth
' Purpose: WMIを使用してドライブの論理的健全性と書き込み可能性を監査する
'**
Function CheckDiskHealth(strDrive)
Dim objWMIService, colDisks, objDisk
Dim blnIsHealthy
blnIsHealthy = False
' WMIサービスへの接続
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
If Err.Number <> 0 Then
WScript.Echo "WMI Connection Failed."
CheckDiskHealth = False
Exit Function
End If
' Win32_LogicalDisk クラスから対象ドライブを抽出
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk Where DeviceID = '" & strDrive & "'")
For Each objDisk In colDisks
' Statusプロパティが "OK" であること、かつ書き込み可能であることを確認
' Note: WMIのStatusは、ファイルシステムの不整合や物理的エラーを反映する
If objDisk.Status = "OK" Then
' 書き込み保護状態の簡易チェック(読み取り専用属性の確認はFSOで行うが、
' ここでは論理的なドライブの状態に集中する)
blnIsHealthy = True
Else
WScript.Echo "WMI Alert: Drive " & strDrive & " Status is [" & objDisk.Status & "]"
End If
Next
' オブジェクトの明示的解放(メモリリーク防止の鉄則)
Set colDisks = Nothing
Set objWMIService = Nothing
CheckDiskHealth = blnIsHealthy
End Function
'**
' Function: SafeFileCopy
' Purpose: FSOを用いてファイルをコピーする。実行直前に属性を最終監査する。
'**
Function SafeFileCopy(strSrc, strDst)
Dim objFSO, objDestFolder
Dim blnSuccess
blnSuccess = False
Set objFSO = CreateObject("Scripting.FileSystemObject")
' 事前チェック:ソースの存在
If Not objFSO.FileExists(strSrc) Then
WScript.Echo "Source file not found: " & strSrc
SafeFileCopy = False
Exit Function
End If
' 事前チェック:デスティネーションの書き込み権限(属性レベル)
' ドライブ全体のStatusがOKでも、フォルダがRead-Onlyの場合がある
Dim strDstDir
strDstDir = objFSO.GetParentFolderName(strDst)
' 書き込み権限の検証(簡易的な実装:フォルダが存在するか、および作成可能か)
On Error Resume Next
If Not objFSO.FolderExists(strDstDir) Then
WScript.Echo "Destination directory does not exist."
SafeFileCopy = False
Exit Function
End If
' 実際のコピー処理
' 既存ファイルがある場合は上書きする設定
objFSO.CopyFile strSrc, strDst, True
If Err.Number = 0 Then
blnSuccess = True
Else
WScript.Echo "FSO Copy Error: " & Err.Description
blnSuccess = False
End If
On Error GoTo 0
' オブジェクトの明示的解放
Set objFSO = Nothing
SafeFileCopy = blnSuccess
End Function
4. アーキテクトの視点:極限の最適化と保守性
このコードを実戦投入するにあたり、以下の3点を深く理解しておく必要がある。
① オブジェクトのライフサイクル管理
VBScriptにおけるCOMオブジェクトの管理は、メモリリークとリソース競合の温床だ。`Set obj = Nothing` を怠ることは、プロフェッショナルとしてあるまじき行為である。特にWMIのクエリ結果(`colDisks`)は、ループ内で大量に生成される可能性があるため、スコープを抜ける前に必ず明示的に解放せよ。
② 「決定論的」なエラーハンドリング
`On Error Resume Next` は、エラーを無視するための道具ではない。「エラーが発生し得る境界線(Boundary)」を定義するための道具だ。本実装では、WMI接続、クエリ実行、FSO操作の各フェーズにおいて、エラーが発生した瞬間にその文脈を特定し、安全に抜ける構造をとっている。
③ レガシー環境への配慮
本スクリプトは、Windows Server 2008 R2 から最新の Windows Server 2022 まで、WMI/FSOの標準的な実装が維持されている限り動作する。ただし、クラウド環境(Azure VM等)や仮想化環境においては、WMIが返す `Status` が物理層の劣化を必ずしも正確に反映しない場合がある。その場合は、OSのイベントログ(Event Log)をWMI経由で監視するレイヤーを追加検討すべきだ。
結論
ファイル操作の自動化において、最も価値があるのは「成功させること」ではなく、「失敗する条件を事前に察知し、システムを壊さないこと」である。
FSOという便利な道具を、WMIという「眼」で補完する。この組み合わせこそが、レガシーな技術スタックを使いこなしつつ、現代的な堅牢性を担保するための、我々シニアエンジニアが持つべき知見である。
