「動けばいい」を卒業する。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の習得において、構文を覚えることは「基礎」です。しかし、今回学んだような「エラーを予測し、制御する」技術こそが、現場で求められる「実力」です。
「もし、ここでネットワークが切れたら?」「もし、ファイルが誰かに開かれていたら?」
常にこの問いを自分に投げかけてみてください。その思考の積み重ねが、あなたを「マクロ職人」から「システムエンジニア」へと引き上げてくれます。
一歩ずつ、着実に。あなたの自動化スキルが、より強固なものになることを応援しています!
