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

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

「動けばいい」を卒業する。FSOの例外エラーを制御し、止まらない自動化プログラムを構築する極意

こんにちは。自動化の荒波を共に渡る仲間たち、ようこそ。

マクロの記録機能を使って、「ファイルがコピーできた!」「フォルダが作れた!」と喜んでいた時期は、誰もが通る大切なステップです。でも、あなたが「業務を自動化するプロフェッショナル」へと脱皮しようとする時、必ず立ちはだかる壁があります。

それは、「実行環境によるエラー」です。

自分のPCでは完璧に動いたスクリプトが、サーバーに持っていった途端、あるいは同僚のPCで動かした瞬間に、「書き込み禁止です」「パスが見つかりません」と無慈悲に止まってしまう……。これは、プログラムが悪いのではなく、「プログラムが現実世界の不確実性に備えていない」ことが原因なのです。

今日は、FileSystemObject(FSO)を使いこなす上で避けては通れない「I/Oエラー」を徹底的に解剖し、エラーが起きてもスマートに立ち振る舞う、プロフェッショナルな例外ハンドリングの技法を伝授します。

ここをクリアすれば、あなたのVBScriptは「単なる命令の羅列」から「堅牢なシステム」へと進化しますよ。

1. なぜFSOのエラーは「厄介」なのか?

FSO(FileSystemObject)は、Windowsのファイルシステムという「巨大で予測不能な世界」と対話するための窓口です。

プログラミングにおける「文法エラー」は、あなたが書いたコードのミスですが、FSOが吐き出すエラーの多くは「環境の拒絶」です。

  • ファイルが別のプログラム(Excelなど)で開かれている(ロック状態)
  • ネットワークドライブが一時的に切断された(通信不安定)
  • 書き込もうとした場所に権限がない(セキュリティ制限)
  • ディスク容量がいっぱいになった(物理的限界)

これらは、コードをいくら綺麗に書いても防げません。だからこそ、プロは「エラーが起きる前提」で、「エラーが起きた時にどうリカバリーするか」を設計するのです。

2. 恐怖の「魔法の数字」を解読する:主要エラーコード一覧

VBScriptでエラーを捕まえるとき、`Err.Number` を確認します。ここで現れる数値(HRESULTと呼ばれる形式)は、一見すると暗号のようです。

特によく遭遇し、かつ対策が重要なエラーを整理しておきましょう。

エラーコード (Hex) 意味 原因の具体例 プロの対策方針
`0x800A0046` 書き込み禁止 ファイルが「読み取り専用」属性になっている 属性を書き換えて再試行、またはスキップ
`0x800A004C` パス未発見 指定したフォルダが存在しない、またはネットワーク切断 フォルダの自動作成、または親パスの存在確認
`0x800A0005` アクセス拒否 管理者権限が必要な場所、または権限不足 管理者へ通知、または書き込み可能なTemp領域へ退避
`0x80070070` ディスク容量不足 ストレージがいっぱい 不要ファイルの削除、またはエラーログを出して停止

これらを単に「エラーが出ました」で終わらせず、「原因ごとに挙動を変える」のが上級者への第一歩です。

3. 鉄壁の設計思想:高度な障害復旧ロジック

プロフェッショナルなコードは、エラーに対して以下の3つの戦略を使い分けます。

1. リトライ (Retry): 一時的なロックや通信エラーなら、数秒待ってからもう一度試す。
2. フォールバック (Fallback): 本来の場所がダメなら、予備の場所(ローカルのTempフォルダなど)に保存する。
3. スキップ&ログ (Skip & Log): 個別のファイルエラーで全体を止めず、エラー内容を記録して次の処理へ進む。

4. 実戦投入:堅牢なファイルコピー関数

それでは、これまでの知見を凝縮した、実戦でそのまま使える「エラーハンドリング付きファイルコピー関数」を見てみましょう。

このコードは、単にコピーするだけでなく、「リトライ」と「エラー原因の特定」を組み込んだ設計になっています。

'**
' 【プロフェッショナル仕様】堅牢なファイルコピー関数
' 概要: エラー発生時に原因を判別し、リトライおよびエラーログ記録を行う
'**
Option Explicit

Dim fso, sourceFile, destFolder, logFile
Set fso = CreateObject("Scripting.FileSystemObject")

' 設定
sourceFile = "C:\Data\TargetFile.txt"     ' コピー元
destFolder = "D:\Backup\TargetFolder\"    ' コピー先
logFile    = "C:\Logs\error_log.txt"      ' エラーログ出力先

' 実行
Call SafeCopyFile(sourceFile, destFolder, logFile)

' --- メインロジック ---

Sub SafeCopyFile(src, destDir, logPath)
    Dim fso, retryCount, maxRetries, success, errCode, targetPath
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    targetPath = destDir & fso.GetFileName(src)
    maxRetries = 3      ' 最大3回リトライ
    retryCount = 0
    success = False

    ' コピー先フォルダが存在しない場合は作成を試みる
    If Not fso.FolderExists(destDir) Then
        On Error Resume Next
        fso.CreateFolder(destDir)
        If Err.Number <> 0 Then
            Call WriteErrorLog(logPath, "FOLDER_CREATE_FAIL", "フォルダ作成失敗: " & destDir & " (Err: " & Err.Number & ")")
            Exit Sub
        End If
        On Error GoTo 0
    End If

    ' リトライループ
    Do While retryCount < maxRetries And Not success
        On Error Resume Next
        fso.CopyFile src, targetPath, True ' Trueは上書き許可
        
        If Err.Number = 0 Then
            success = True
            ' WScript.Echo "成功: " & targetPath
        Else
            errCode = Err.Number
            retryCount = retryCount + 1
            
            ' エラー内容に応じた分岐処理
            Select Case errCode
                Case 0x800A0046 ' 書き込み禁止 (Permission Denied / Read-only)
                    Call WriteErrorLog(logPath, "WRITE_PROTECTED", "書き込み禁止のためリトライ中... (" & retryCount & ")")
                    WScript.Sleep 2000 ' 2秒待機してリトライ
                    
                Case 0x800A004C ' パス未発見 (Path Not Found)
                    Call WriteErrorLog(logPath, "PATH_NOT_FOUND", "パスが見つかりません。ネットワークを確認してください。")
                    Exit Do ' パスがない場合はリトライしても無意味なので抜ける
                    
                Case 0x800A0005 ' アクセス拒否 (Access Denied)
                    Call WriteErrorLog(logPath, "ACCESS_DENIED", "権限がありません。管理者権限を確認してください。")
                    Exit Do ' 権限問題はリトライしても解決しない
                    
                Case Else
                    Call WriteErrorLog(logPath, "UNKNOWN_ERROR", "予期せぬエラーが発生: " & errCode)
                    Exit Do
            End Select
            On Error GoTo 0
        End If
    Loop

    If Not success Then
        WScript.Echo "【致命的エラー】ファイルのコピーに失敗しました。ログを確認してください。"
    Else
        WScript.Echo "【完了】ファイルのコピーが成功しました。"
    End If
End Sub

' エラーログを記録するサブ関数
Sub WriteErrorLog(path, errType, message)
    On Error Resume Next
    Dim ts
    ' ログ出力用のフォルダがなければ作成(簡易実装)
    Set ts = CreateObject("Scripting.FileSystemObject").OpenTextFile(path, 8, True)
    ts.WriteLine "[" & Now & "] [" & errType & "] " & message
    ts.Close
    On Error GoTo 0
End Sub

このコードの「プロのポイント」

1. `Select Case errCode` による意思決定:
すべてのエラーを同じように扱うのではなく、「リトライすべきエラー(書き込み禁止など)」と「即座に諦めるべきエラー(権限拒否など)」を明確に区別しています。
2. `WScript.Sleep` による待機:
ファイルがロックされている場合、一瞬待つだけで解消することが多々あります。この「ゆとり」が自動化の成功率を劇的に高めます。
3. `On Error Resume Next` の局所化:
エラーハンドリングをしたい箇所だけでピンポイントに使用し、すぐに `On Error GoTo 0` で解除しています。これにより、意図しないエラーの握り潰しを防いでいます。
4. ログへの詳細記録:
エラーが発生した時刻、種類、具体的な内容をファイルに残すことで、後からの調査(デバッグ)を容易にしています。

5. まとめ:エンジニアとしての視点を持とう

VBScriptの習得において、構文を覚えることは「基礎」です。しかし、今回学んだような「エラーを予測し、制御する」技術こそが、現場で求められる「実力」です。

「もし、ここでネットワークが切れたら?」「もし、ファイルが誰かに開かれていたら?」

常にこの問いを自分に投げかけてみてください。その思考の積み重ねが、あなたを「マクロ職人」から「システムエンジニア」へと引き上げてくれます。

一歩ずつ、着実に。あなたの自動化スキルが、より強固なものになることを応援しています!

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