【入門編】【実務中級者向け】他プロセスによるファイル使用中の解放を待機しタイムアウト付きで確実に処理するFSOループ – VBScript (Visual Basic Scripting Edition)解析バイブル

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

【実務中級者への登竜門】「ファイルが使用中です」を克服する――堅牢なリトライ・ループの実装術

こんにちは。現場の自動化設計を指揮しているアーキテクトです。

VBScriptを学び始め、「マクロの記録」やネットの断片的なコードを組み合わせて、ファイル操作(コピーや移動)ができるようになった……。そこまで到達したあなたなら、きっと一度はこの「壁」にぶつかったことがあるはずです。

「実行時エラー ’70’: 書き込み禁止です」
「実行時エラー ’70’: ファイルが使用中のため、アクセスできません」

せっかく完璧なロジックを組んだのに、Excelが裏で保存中だったり、ウイルス対策ソフトがスキャン中だったりするだけで、プログラムが非情にも止まってしまう。これは、プログラミング初学者が「自動化のプロ」へと脱皮するための、最初にして最大の関門です。

今日は、このエラーをスマートに回避し、実務で通用する「止まらないスクリプト」を書くためのリトライ・ループ・パターンを伝授しましょう。ここをクリアすれば、あなたのVBScriptは「ただ動くコード」から「信頼されるシステム」へと進化します。

なぜ「ファイル使用中」は起きるのか?

まず、敵を知ることから始めましょう。

OS(Windows)の世界では、あるプロセス(Excelやメモ帳、あるいはシステム)がファイルを開いている間、そのファイルに「ロック」をかけます。これは、複数のプログラムが同時に同じファイルを書き換えてデータが壊れるのを防ぐための、非常に重要な安全装置です。

FSO(FileSystemObject)は、このロックがかかっているファイルに対して「移動して」「消して」と命令を出しても、OSから「今は無理だよ」と拒絶されてしまいます。これがエラーの正体です。

プロのエンジニアは、ここで「エラーが出たから終了」とは考えません。
「今は使われているのか? では、少し待ってからもう一度試してみよう。それでもダメなら、諦めて報告しよう」
という、「粘り強さ(リトライ)」と「諦め時(タイムアウト)」の設計を行うのです。

堅牢なリトライ・ロジックの設計図

単にループさせるだけでは不十分です。無限ループに陥ればシステムはフリーズします。私たちが実装すべきは、以下の4要素を備えたアルゴリズムです。

1. 試行(Attempt): ファイル操作を実行してみる。
2. 待機(Sleep): 失敗したら、OSがファイルを解放する時間を数秒与える。
3. 経過時間の監視(Timeout): 「待ちすぎ」を防ぐため、開始時刻からの経過時間を測る。
4. ログ出力(Logging): 最終的に失敗した際、「なぜダメだったのか」を記録に残す。

実践:タイムアウト付きリトライ・ループの完全コード

さあ、実際に使えるコードを見てみましょう。このコードは、ファイルを安全に移動させる処理を例にしています。そのままコピーして、ファイルパスを書き換えるだけで実務に投入できます。

' ==========================================================================
' 堅牢なファイル移動スクリプト (Retry Loop Pattern)
' 概要: 他プロセスによるファイルロックを考慮し、一定時間リトライを行う
' ==========================================================================

Option Explicit

Dim fso, sourcePath, destPath, timeoutSec, retryIntervalSec
Dim startTime, elapsedTime, isSuccess, errMessage

' --- 設定エリア ---
sourcePath      = "C:\Temp\target_file.xlsx"  ' 操作したいファイル
destPath        = "C:\Temp\archive\target_file.xlsx" ' 移動先
timeoutSec      = 30  ' 最大何秒間待機するか
retryIntervalSec = 2  ' 何秒おきにリトライするか
' ------------------

Set fso = CreateObject("Scripting.FileSystemObject")

' 実行開始時間を記録(Timer関数は午前0時からの経過秒数を返す)
startTime = Timer

isSuccess = False

' ログ出力用サブルーチン(簡易版)
Sub WriteLog(msg)
    ' 本来はファイルに書き出すのがベストですが、ここではデバッグ用にMsgBox/WScript.Echoを使用
    WScript.Echo "[" & Now & "] " & msg
End Sub

WriteLog "処理を開始します: " & sourcePath

' --- メイン・リトライ・ループ ---
Do
    ' エラーハンドリングを開始
    On Error Resume Next
    
    ' ファイル移動の試行
    fso.MoveFile sourcePath, destPath
    
    ' エラーが発生したかチェック
    If Err.Number = 0 Then
        ' 成功!
        isSuccess = True
        On Error GoTo 0 ' エラーハンドリングを通常に戻す
        Exit Do
    Else
        ' 失敗した場合のエラー内容を保持
        errMessage = Err.Description
        Err.Clear
        On Error GoTo 0 ' エラーハンドリングを通常に戻す
        
        ' 経過時間を計算
        elapsedTime = Timer - startTime
        
        ' タイムアウト判定
        If elapsedTime > timeoutSec Then
            WriteLog "【エラー】タイムアウトしました。ファイルが解放されません。"
            WriteLog "最終エラー内容: " & errMessage
            Exit Do
        Else
            ' まだ時間があるなら、少し待機してリトライ
            WriteLog "ファイルが使用中のようです。リトライします... (" & Int(elapsedTime) & "秒経過)"
            WScript.Sleep (retryIntervalSec * 1000) ' ミリ秒指定なので1000倍する
        End If
    End If
Loop

' --- 最終結果の判定 ---
If isSuccess Then
    WriteLog "成功: ファイルを正常に移動しました。"
Else
    WriteLog "失敗: 処理を中断しました。ログを確認してください。"
    ' ここでWScript.Quit 1 などを使って、呼び出し元(バッチ等)に失敗を伝えるのがプロの作法
    WScript.Quit 1
End If

' オブジェクトの解放(メモリ管理の基本)
Set fso = Nothing

アーキテクトの視点:コードの「深層」を読み解く

このコードには、プロが意識している重要なテクニックが隠されています。

1. `Timer` 関数の賢い使い方

`Timer` 関数は、1日の経過秒数を浮動小数点数で返します。これを利用することで、「今、何秒経過したか」を極めて軽量に計算できます。`DateDiff` 関数を使うよりも、ループ内での計算コストが低く、精密です。

2. `On Error Resume Next` の「局所化」

ここが最も重要です。多くの初心者は、スクリプトの冒頭に `On Error Resume Next` を書いてしまいます。これは「絶対にやってはいけない」。なぜなら、予期せぬ別のエラー(パスが見つからない、権限がない等)まで全て無視されてしまうからです。

私のコードでは、「ファイル操作の直前」で有効にし、「直後」に `On Error GoTo 0` で無効化しています。エラーの影響範囲を最小限に抑える。これが、バグを特定しやすくするための鉄則です。

3. `WScript.Sleep` による「呼吸」

コンピュータの処理速度は人間には想像もできないほど速いです。リトライの間隔を `0` にしてしまうと、CPUに過剰な負荷をかけ、ファイルが解放される隙を与えずにエラーを連発し続けます。`retryIntervalSec` を設けることで、OSに「呼吸(猶予)」を与えているのです。

陥りやすい罠:ここをチェック!

もし、このコードを動かしてうまくいかない場合は、以下の3点を確認してください。

  • パスの末尾に注意: `sourcePath` がフォルダを指しているのか、ファイルを指しているのか。FSOのメソッド(`MoveFile` vs `MoveFolder`)と一致しているか確認しましょう。
  • 書き込み権限: ファイルがロックされているのではなく、そもそも移動先のフォルダに「書き込み権限」がない場合も、同じエラー 70 が出ることがあります。
  • 無限ループの恐怖: `timeoutSec` を設定し忘れると、ファイルが開きっぱなしの時にスクリプトが永遠に止まり続けます。必ずタイムアウトはセットしましょう。

終わりに

「エラーが出たら止まる」コードは、ただの道具です。
しかし、「エラーを予測し、対処し、報告する」コードは、「信頼できるパートナー」になります。

今回学んだリトライ・ループのパターンは、ファイル操作だけでなく、ネットワーク越しへの接続確認や、データベースへのアクセス待ちなど、あらゆる自動化の現場で応用できる「黄金律」です。

ここをマスターすれば、あなたのVBScriptは一段上のステージへ上がります。自信を持って、次の自動化に挑んでください。応援していますよ。

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