【テクニカル・上級編】【実務中級者向け】ネットワーク共有フォルダの接続切れをFSOで検知しローカル代替フォルダへ自動退避するフォールバック処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

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

ネットワークの不確実性を制御せよ:FSOを用いた耐障害性フォールバック・アーキテクチャの構築

現場のエンジニアが直面する最も忌まわしい事象の一つは、「昨日まで正常に動いていた自動化スクリプトが、ネットワークの一時的な瞬断によってプロセスを停止(ハング)させ、後続の業務を全停止させる」ことである。

特にUNCパス(`\\Server\Share\…`)をターゲットとするVBScriptを用いたレガシーシステムにおいて、ネットワークの不通は単なる「エラー」ではない。それは、I/O待ちによるプロセスの長時間フリーズ、あるいは不適切なエラーハンドリングによる、不完全なファイル生成という「破壊的挙動」を引き起こす。

本稿では、FileSystemObject (FSO) を単なるファイル操作ツールとしてではなく、「動的な環境変化に適応するフォールバック・メカニズム」の核として活用する、極限の設計思想を伝授する。

1. 脆弱な設計:なぜ単純な `FileExists` では足りないのか

多くの初級者は、以下のような実装で満足する。

' 典型的な、しかし脆弱な実装例
If fso.FileExists(uncPath) Then
    ' 書き込み処理
Else
    ' エラー終了
End If

これは致命的に不完全である。なぜなら、`FileExists` メソッド自体がネットワークパスに対して発行される際、ネットワークが応答不能(Timeout状態)であれば、スクリプトはその応答を待機するために数秒から数十秒間、実行コンテキストをブロックするからだ。

シニアエンジニアが求めるべきは、単なる存在確認ではない。「パスへの到達可能性(Reachability)」と「書き込み権限の即時判定」、そして「失敗時の決定論的な代替経路(Fallback Path)の確保」である。

2. アーキテクチャの核心:フォールバック・パターンの実装

堅牢なスクリプトを構築するためには、以下の3層構造を実装する必要がある。

1. Attempt Layer (試行層): UNCパスへのアクセスを試みる。
2. Detection Layer (検知層): `Err.Number` を精査し、エラーが「ファイル不在」なのか「ネットワーク不通」なのかを峻別する。
3. Fallback Layer (代替層): ネットワーク不通と判定された場合、即座にローカルのセーフティディレクトリへI/Oをリダイレクトする。

実装コード:耐障害性ファイル出力モジュール

以下のコードは、実務における「プロトコル」としてそのまま利用可能なレベルまで昇華させたものである。

' =============================================================================
' Module: ResilientFileHandler
' Purpose: ネットワーク切断を検知し、ローカルへ自動退避するフォールバック処理
' Author: Legendary Chief Architect
' =============================================================================
Option Explicit

' 定数定義:マジックナンバーを排除し、保守性を高める
Const ERR_NETWORK_UNREACHABLE = 53 ' Path not found
Const ERR_ACCESS_DENIED       = 70 ' Permission denied
Const ERR_DEVICE_NOT_READY    = 76 ' Device not ready

Dim fso, uncPath, localFallbackPath, targetFile

' 初期設定
uncPath = "\\ProductionServer\Data\Output\"
localFallbackPath = "C:\LocalFallback_Storage\"
targetFile = "DailyReport_" & Year(Date) & Month(Date) & Day(Date) & ".txt"

' メインルーチン
Call ExecuteResilientOutput()

Sub ExecuteResilientOutput()
    Dim finalPath, isFallback
    isFallback = False

    ' 1. FSOオブジェクトの生成(ライフサイクル管理の開始)
    Set fso = CreateObject("Scripting.FileSystemObject")

    ' 2. ローカル退避用ディレクトリの存在確認と確保
    '    ネットワークが死んでいても、ローカルの準備はできていなければならない
    If Not fso.FolderExists(localFallbackPath) Then
        On Error Resume Next
        fso.CreateFolder(localFallbackPath)
        If Err.Number <> 0 Then
            WScript.Echo "CRITICAL ERROR: Local fallback directory creation failed."
            WScript.Quit 1
        End If
        On Error GoTo 0
    End If

    ' 3. 書き込み先パスの決定プロセス
    finalPath = DetermineWritePath(fso, uncPath, localFallbackPath, isFallback)

    ' 4. 実際の書き込み処理
    On Error Resume Next
    Dim ts
    Set ts = fso.CreateTextFile(finalPath, True)
    
    If Err.Number <> 0 Then
        ' 書き込み中にエラーが発生した場合(例:書き込み中の切断)
        WScript.Echo "FATAL: I/O Operation failed even on fallback path: " & Err.Description
        ' ここでログ出力等の緊急事態処理を行う
    Else
        ts.WriteLine "Timestamp: " & Now
        ts.WriteLine "Status: " & IIf(isFallback, "FALLBACK_MODE", "NORMAL_MODE")
        ts.WriteLine "Data content..."
        ts.Close
        WScript.Echo "SUCCESS: File written to " & finalPath & IIf(isFallback, " (FALLBACK)", "")
    End If
    On Error GoTo 0

    ' 5. オブジェクトの明示的解放(メモリリークとCOM参照の残存を防ぐ)
    Set ts = Nothing
    Set fso = Nothing
End Sub

' -----------------------------------------------------------------------------
' Function: DetermineWritePath
' Description: ネットワーク到達性を判定し、最適なパスを返す
' -----------------------------------------------------------------------------
Function DetermineWritePath(objFSO, targetUNC, fallbackDir, ByRef outIsFallback)
    Dim testPath
    testPath = targetUNC
    outIsFallback = False

    ' ネットワークパスへのアクセスを試行
    ' On Error Resume Next を局所的に適用し、エラーを制御下に置く
    On Error Resume Next
    
    ' フォルダの存在確認をトリガーとして、ネットワークの応答を確認
    ' ※単なる FileExists よりも FolderExists の方が、ディレクトリ構造へのアクセスを伴うため確実性が高い
    If Not objFSO.FolderExists(targetUNC) Then
        ' エラー番号の精査
        Select Case Err.Number
            Case ERR_NETWORK_UNREACHABLE, ERR_ACCESS_DENIED, ERR_DEVICE_NOT_READY
                ' ネットワーク不通または権限エラーの場合、フォールバックへ
                outIsFallback = True
            Case Else
                ' その他のエラー(ファイル名不正など)は致命的と判断
                WScript.Echo "ERROR: Unexpected error accessing UNC: " & Err.Description
                WScript.Quit 1
        End Select
    End If
    On Error GoTo 0

    If outIsFallback Then
        DetermineWritePath = fallbackDir & targetFile
    Else
        DetermineWritePath = targetUNC & targetFile
    End If
End Function

' 補助関数:三項演算子の代替
Function IIf(condition, truePart, falsePart)
    If condition Then IIf = truePart Else IIf = falsePart
End Function

3. 技術的深度:プロフェッショナルのための知見

オブジェクトのライフサイクルとメモリ管理

VBScriptはガベージコレクション(GC)に依存しているように見えるが、COMオブジェクト(FSOなど)の扱いにおいては、明示的な `Set obj = Nothing` が極めて重要である。
特に、長時間稼働するバッチ処理や、ループ内で大量のファイルを操作する場合、オブジェクトの解放を怠ると、メモリリークのみならず、ファイルハンドルが解放されずに残り、後続のプロセスが「ファイルが別のプロセスで使用されています」というエラーで死ぬ原因となる。

Windows API との境界線

本来、ネットワークのドライブタイプ(`DRIVE_REMOTE` か `DRIVE_FIXED` か)を厳密に判定するには、Windows APIの `GetDriveType` を呼び出すのが最短ルートである。しかし、VBScriptから直接Win32 APIを叩くことは不可能だ。
我々エンジニアは、`WScript.Shell` を介して `cmd /c fsutil` を呼び出すか、あるいは今回のように 「FSOの例外トラップ(Error Trapping)をネットワーク・プローブとして利用する」 という、OSの挙動に基づいた擬似的なAPI呼び出し手法を選択する。これは、外部依存を最小限に抑えつつ、実効性を担保する高度なテクニックである。

エラーハンドリングの極意

`On Error Resume Next` は、諸刃の剣である。これをグローバルに適用してはならない。
本コードのように、「エラーが予測される境界領域(I/O操作、ネットワークアクセス)」にのみ局所的に適用し、直後に `Err.Number` を検証して即座に `On Error GoTo 0` で復帰させる。 これが、予測不可能なランタイムエラーからシステムを保護するための、鉄則である。

結論

ネットワークは常に裏切る。その前提に立った設計こそが、真のエンタープライズ・オートメーションである。
FSOを単なる「ファイル操作用ライブラリ」として扱うか、それとも「環境の不確実性を吸収するインテリジェントなインターフェース」として扱うか。その差が、エンジニアとしての格の違いを決めるのである。

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