【テクニカル・上級編】【上級プロフェッショナル向け】FSOとWMI(Win32_LogicalDisk)を連携させたセクタエラー・書き込み保護状態の事前検知と安全なファイル退避処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

盲目的な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という「眼」で補完する。この組み合わせこそが、レガシーな技術スタックを使いこなしつつ、現代的な堅牢性を担保するための、我々シニアエンジニアが持つべき知見である。

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