【テクニカル・上級編】【上級プロフェッショナル向け】FSO操作中に発生する各種I/Oエラー(0x800A0046, 0x800A004C等)をErr.Numberで分類し高度な障害復旧ロジックを組む技法 – VBScript (Visual Basic Scripting Edition)解析バイブル

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

VBScriptにおけるFSO操作の極致:I/O例外を制御し、堅牢な自動化アーキテクチャを構築する

多くの開発者は、`FileSystemObject` (FSO) を「単なるファイル操作の道具」だと過小評価している。彼らは `On Error Resume Next` を乱用し、エラーが起きた瞬間に沈黙するか、あるいは意味のないログを吐き出して停止する。

だが、我々プロフェッショナルにとって、I/O操作とは「不確実性の塊」である。
ファイルはウイルススキャンによって一瞬ロックされる。ネットワークドライブは瞬時に切断される。ディスク容量は予期せぬタイミングで枯渇する。

本稿では、VBScriptにおけるFSO操作の真の姿、すなわちHRESULTエラーコードの解読、オブジェクトのライフサイクル管理、そして高度な例外復旧ロジック(Retry/Skip/Quarantine)の設計について、その深淵を解説する。

1. HRESULTの深淵:Err.Numberの正体を暴く

VBScriptにおける `Err.Number` は、COM(Component Object Model)が返す `HRESULT` をデシマル(10進数)で表現したものに過ぎない。FSOが直面する致命的なエラーを制御するためには、その背後にある16進数表記のHRESULTを理解しておく必要がある。

以下の主要なエラーコードは、実装において必ず「分岐条件」として組み込むべきものだ。

HRESULT (Hex) Err.Number (Dec) 原因 復旧戦略
`0x800A0046` `2147024710` Permission Denied (書き込み禁止/他プロセスによるロック) Exponential Backoff (指数関数的リトライ)
`0x800A004B` `2147024715` Path Not Found (パスが存在しない/ネットワーク切断) Path Validation & Reconnect
`0x800A004C` `2147024716` Disk Full (ディスク容量不足) Critical Halt & Cleanup
`0x800A0005` `2147024741` Access Denied (権限不足) Skip & Log (管理者通知)

単に「エラーが起きた」と判定するのではなく、「このエラーは一時的なものか、それとも致命的なものか」を判別すること。これがアーキテクトの責務である。

2. オブジェクトのライフサイクルとハンドルリークの回避

VBScriptはガベージコレクション(GC)を持つが、その挙動は非決定的である。
特にFSOでファイルストリームを開いた際、エラーハンドリング内でオブジェクトの解放(`Set obj = Nothing`)を怠ると、ファイルハンドルが解放されず、次回の実行やリトライが「Permission Denied」で失敗するという悪循環(デッドロック)を引き起こす。

エラーが発生した際、必ず `Err.Clear` と共に、関与したすべてのオブジェクトを明示的に破棄する「クリーンアップ・セクション」を設計しなければならない。

3. 実践:高度な例外ハンドリング・エンジン

以下に、リトライアルゴリズム、エラー分類、および隔離(Quarantine)機能を備えたプロフェッショナル向けのコード・テンプレートを示す。

' ==============================================================================
' Module: FSO_Resilient_Engine
' Description: 高度なエラー復旧ロジックを備えたファイル操作エンジン
' Author: Legendary Chief Architect
' ==============================================================================

Option Explicit

' 定数定義:エラーコードのデシマル表現
Const ERR_PERMISSION_DENIED = 2147024710 ' 0x800A0046
Const ERR_PATH_NOT_FOUND    = 2147024715 ' 0x800A004B
Const ERR_DISK_FULL         = 2147024716 ' 0x800A004C

' 設定値
Const MAX_RETRIES = 3
Const RETRY_DELAY_MS = 1000 ' ミリ秒

Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

' ------------------------------------------------------------------------------
' Main Execution Logic
' ------------------------------------------------------------------------------
Call Main()

Sub Main()
    Dim targetPath, content
    targetPath = "C:\Work\CriticalData.txt"
    content = "Architect's Log: System integrity verified."

    ' ファイル書き込みの実行
    If SafeWriteFile(targetPath, content) Then
        WScript.Echo "Operation Successful."
    Else
        WScript.Echo "Operation Failed after all recovery attempts."
    End If
End Sub

' ------------------------------------------------------------------------------
' Function: SafeWriteFile
' Description: リトライとエラー分類を内包した堅牢な書き込み関数
' ------------------------------------------------------------------------------
Function SafeWriteFile(strFilePath, strContent)
    Dim objFile, retryCount, success, errNum
    success = False
    retryCount = 0
    
    On Error Resume Next ' エラーハンドリングの開始

    Do While retryCount <= MAX_RETRIES And Not success
        ' 1. 実行
        Set objFile = fso.CreateTextFile(strFilePath, True)
        
        If Err.Number = 0 Then
            objFile.WriteLine strContent
            objFile.Close
            success = True
        Else
            ' 2. エラー分析
            errNum = Err.Number
            WScript.Echo "[ERROR] Attempt " & (retryCount + 1) & " failed. Code: " & errNum
            
            ' 3. 戦略的判断
            Select Case errNum
                Case ERR_PERMISSION_DENIED
                    ' ロックされている場合は、指数関数的バックオフでリトライ
                    WScript.Echo "  -> Reason: File Locked. Retrying..."
                    Call ExponentialBackoff(retryCount)
                    retryCount = retryCount + 1
                    
                Case ERR_PATH_NOT_FOUND
                    ' パス喪失はリトライしても無意味。ディレクトリ再構築を試みるか即時終了
                    WScript.Echo "  -> Reason: Path Not Found. Critical Failure."
                    Exit Do 
                    
                Case ERR_DISK_FULL
                    ' ディスク満杯はリトライしても無意味。システム全体への通知が必要
                    WScript.Echo "  -> Reason: Disk Full. Immediate Halt Required."
                    Call CriticalAlert("DISK FULL: " & strFilePath)
                    Exit Do
                    
                Case Else
                    ' 未知のエラーはログを残してスキップ(Quarantine)
                    WScript.Echo "  -> Reason: Unknown Error. Moving to Quarantine."
                    Call QuarantineFile(strFilePath)
                    Exit Do
            End Select
        End If

        ' 4. オブジェクトの明示的解放(ハンドルリーク防止の要)
        Set objFile = Nothing
        Err.Clear
    Loop

    SafeWriteFile = success
    On Error GoTo 0 ' エラーハンドリングの解除
End Function

' ------------------------------------------------------------------------------
' Sub: ExponentialBackoff
' Description: リトライの間隔を徐々に広げるアルゴリズム
' ------------------------------------------------------------------------------
Sub ExponentialBackoff(attempt)
    Dim waitTime
    ' 2^attempt * base_delay
    waitTime = (2 ^ attempt) * RETRY_DELAY_MS
    WScript.Sleep waitTime
End Sub

' ------------------------------------------------------------------------------
' Sub: QuarantineFile
' Description: 処理不能なファイルを隔離フォルダへ移動
' ------------------------------------------------------------------------------
Sub QuarantineFile(strPath)
    Dim quarantineDir
    quarantineDir = "C:\Work\Quarantine\"
    
    On Error Resume Next
    If Not fso.FolderExists(quarantineDir) Then fso.CreateFolder(quarantineDir)
    
    ' ファイルを隔離ディレクトリへ移動(既存ファイルとの衝突を避けるためタイムスタンプ付与)
    fso.MoveFile strPath, quarantineDir & fso.GetBaseName(strPath) & "_" & Timer & ".bak"
    If Err.Number <> 0 Then
        WScript.Echo "  [FATAL] Failed to quarantine file."
    End If
    On Error GoTo 0
End Sub

' ------------------------------------------------------------------------------
' Sub: CriticalAlert
' Description: 管理者への緊急通知(実務ではメール送信やイベントログ書き込みを行う)
' ------------------------------------------------------------------------------
Sub CriticalAlert(msg)
    ' ここに WSH.Shell によるイベントログ書き込みや、SMTPによるメール送信ロジックを実装する
    WScript.Echo "[CRITICAL ALERT] " & msg
End Sub

4. 結論:エンジニアの資質は「例外」に宿る

優れたコードとは、正常な入力を処理するコードではない。予期せぬ異常事態において、システムをいかにエレガントに、かつ最小限の被害で停止(あるいは継続)させられるかを設計したコードである。

FSOを操作する際、単に `CreateTextFile` を呼び出すだけでは素人だ。その背後にあるOSの挙動、HRESULTの重み、そしてオブジェクトのライフサイクルを完全に掌握して初めて、我々は「自動化エンジニア」と名乗ることができる。

レガシーな環境であればあるほど、この「防御的プログラミング」の価値は高まる。君の書くコードが、深夜のシステムダウンによる呼び出しを防ぐ盾となることを願う。

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