執筆:ファイル作成の「瞬間」を捉え、「完了」を確信する —— 堅牢なファイル監視ロジックの極意
「ファイルが作成されたら、すぐに中身を読み取って処理する」
一見、シンプルで完璧なロジックに見えますよね。でも、いざ現場で動かしてみると、どうでしょう。
「実行時エラー ‘800A0046’: 書き込み禁止です」
……そんな冷たいエラーメッセージに、肩を落としたことはありませんか?
実はこれ、プログラミング初学者が必ずと言っていいほど踏み込む「落とし穴」なんです。今日は、このエラーの正体を解き明かし、プロフェッショナルが使う「WMI(Windows Management Instrumentation)」と「FileSystemObject」を組み合わせた、極限まで堅牢な監視ロジックを伝授します。
ここをクリアすれば、あなたのVBScriptは「動けばラッキーなスクリプト」から「業務を支える信頼のツール」へと進化しますよ。
1. なぜ「ファイル作成直後」はエラーになるのか?
まず、敵を知ることから始めましょう。なぜエラーが起きるのか。
それは、「OSがファイルを作った瞬間」と「ファイルへの書き込みが完了した瞬間」には、わずかなタイムラグがあるからです。
1. OS: 「よし、新しいファイルを作るぞ!(作成イベント発生!)」
2. あなたのスクリプト: 「おっ、ファイルができた! すぐに中身を読み込むぞ!」
3. OS: 「ちょっと待って! まだデータを書き込んでる最中だから、他のソフト(スクリプト)は触らせないよ!(ロック状態)」
4. あなたのスクリプト: 「エラー発生……(絶望)」
この「書き込み中ロック」を回避するには、「ファイルが作られたこと」を検知したあとに、「書き込みが止まったこと」を確認するという、二段構えの設計が必要なのです。
2. 武器を揃える:WMI と FileSystemObject の役割分担
この問題を解決するために、私たちは2つの強力な武器を使い分けます。
| 武器 | 正体 | 役割(今回のミッション) |
|---|---|---|
| WMI | Windowsの管理システム | 「センサー」。フォルダ内で「ファイルが作られた!」というイベントをリアルタイムで検知する。 |
| FileSystemObject (FSO) | ファイル操作の標準機能 | 「計測器」。検知したファイルのサイズを監視し、「書き込みが止まったか」を判定する。 |
「センサーで異常(作成)を察知し、計測器で状態(サイズ変化の停止)を確認する」。この連携こそが、プロの設計です。
3. 【実践】堅牢なファイル監視スクリプト
さあ、実際に使えるコードを見てみましょう。このコードは、指定したフォルダにファイルが現れた瞬間を捉え、そのサイズが変化しなくなるまで待機してから後続処理へ進みます。
' ==========================================================================
' 【極限の知見】堅牢なファイル監視・完了検知スクリプト
' 概要: WMIでファイル作成を検知し、FSOで書き込み完了(サイズ安定)を確認する
' ==========================================================================
Option Explicit
' --- 設定領域 ---
Dim targetFolder
targetFolder = "C:\Temp\MonitorTarget" ' 監視対象のフォルダ(必ず実在させてください)
' --- オブジェクト宣言 ---
Dim objFSO, objWMI, colMonitoredItems, objEvent, strFileName, strFilePath
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
' フォルダの存在チェック(基本中の基本!)
If Not objFSO.FolderExists(targetFolder) Then
WScript.Echo "Error: 監視対象のフォルダが存在しません: " & targetFolder
WScript.Quit
End If
WScript.Echo "監視を開始しました... (Ctrl+C で終了)"
' --- STEP 1: WMIによるイベント監視の設定 ---
' __InstanceCreationEvent を使い、指定フォルダ内にファイルが作成された瞬間を監視します
Set colMonitoredItems = objWMI.ExecNotificationQuery _
("SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE " & _
"TargetInstance ISA 'CIM_DataFile' AND " & _
"TargetInstance.Drive = '" & Left(targetFolder, 2) & "' AND " & _
"TargetInstance.Path = '" & Replace(targetFolder, "\", "\\") & "\'")
' --- メインループ ---
Do
' イベントが発生するまでここで待機(CPU負荷を抑える賢い設計)
Set objEvent = colMonitoredItems.NextEvent
' 作成されたファイルの情報を取得
strFilePath = objEvent.TargetInstance.Name ' ファイル名(フルパスではない場合があるため注意)
' WMIのPathプロパティは特殊な形式のため、結合してフルパスを作成
strFilePath = targetFolder & "\" & objEvent.TargetInstance.Name
WScript.Echo "--- 検知: 新しいファイルが見つかりました ---"
WScript.Echo "Path: " & strFilePath
' --- STEP 2: 書き込み完了の判定(サイズ変化の監視) ---
If WaitForFileReady(strFilePath, objFSO) Then
WScript.Echo "Status: 書き込み完了を確認しました。"
' ここに「ファイルが完成した後にやりたい処理」を記述します
Call ProcessFile(strFilePath, objFSO)
Else
WScript.Echo "Status: ファイルの準備に失敗しました(タイムアウト)。"
End If
WScript.Echo "------------------------------------------"
Loop
' ==========================================================================
' 関数: WaitForFileReady
' 目的: ファイルサイズが変化しなくなるまで待機し、書き込み完了を判定する
' ==========================================================================
Function WaitForFileReady(strPath, objFSO)
Dim lastSize, currentSize, retryCount, maxRetries
lastSize = -1
currentSize = 0
retryCount = 0
maxRetries = 30 ' 最大30回試行(約30秒間)
WaitForFileReady = False
Do While retryCount < maxRetries
' ファイルが存在するか再確認
If objFSO.FileExists(strPath) Then
On Error Resume Next ' ロックエラー対策
currentSize = objFSO.GetFile(strPath).Size
' エラーがなければ(=ファイルが開けたら)、サイズを比較
If Err.Number = 0 Then
' 前回のサイズと同じ、かつ、サイズが0より大きければ「書き込み完了」とみなす
If currentSize > 0 And currentSize = lastSize Then
WaitForFileReady = True
Exit Do
End If
lastSize = currentSize
End If
On Error GoTo 0
Err.Clear
End If
' 次のチェックまで少し待機(CPUに優しいインターバル)
WScript.Sleep 1000
retryCount = retryCount + 1
Loop
End Function
' ==========================================================================
' 関数: ProcessFile
' 目的: ファイルが完成した後の後続処理(例として中身を表示)
' ==========================================================================
Sub ProcessFile(strPath, objFSO)
Dim objFile, strContent
Set objFile = objFSO.OpenTextFile(strPath, 1) ' 1 = ForReading
strContent = objFile.ReadAll
objFile.Close
WScript.Echo "Success: ファイルの内容を読み込みました。"
WScript.Echo "Content Preview: " & Left(strContent, 50) & "..."
End Sub
4. アーキテクトの視点:コードの「肝」を解説
このコードには、現場でトラブルを防ぐための「設計思想」が3つ組み込まれています。
① WMIの `WITHIN 1`(ポーリング間隔の最適化)
`SELECT * FROM __InstanceCreationEvent WITHIN 1` の `WITHIN 1` は、「1秒ごとにイベントがないか確認してね」という指示です。ここを `0.1` にすると反応は速くなりますが、CPU使用率が跳ね上がります。逆に `10` にすると、ファイルができてから検知まで最大10秒のラグが出ます。「1秒」は、業務自動化においてバランスの取れた黄金比です。
② `WaitForFileReady` による「サイズ安定」ロジック
単に `WScript.Sleep 5000`(5秒待つ)とするのは、素人のやり方です。
大きなファイル(例:1GBのログファイル)をコピーしている場合、5秒では足りません。
私のコードでは、「前回のサイズと今回のサイズが一致したか?」をチェックしています。これにより、ファイルが小さければ待ち続け、書き込みが終わった瞬間に次へ進む、極めて効率的な動きを実現しています。
③ `On Error Resume Next` の戦略的配置
ファイルが書き込み中のとき、`objFSO.GetFile(strPath).Size` を実行した瞬間に、再び「アクセス拒否」エラーが出る可能性があります。
これを防ぐために、サイズ取得の前後で一時的にエラーを無視する(`On Error Resume Next`)ことで、スクリプトを強制終了させずに「あ、まだロックされてるな。じゃあもう一回待とう」という柔軟な挙動を実現しています。
最後に:ここをクリアすれば、あなたはもう初級者ではない
いかがでしたか?
「ファイルができた」というイベントだけを見るのではなく、その「状態(Size)」と「連続性(Stability)」に目を向ける。これが、業務を止めない自動化エンジニアへの第一歩です。
VBScriptは古い言語と言われることもありますが、Windows環境においてこれほど手軽に、かつ強力にOSの深部にアクセスできる言語は他にありません。仕組みを正しく理解すれば、これほど頼もしい相棒はいませんよ。
さあ、このコードを自分の環境に合わせて調整し、自信を持って実務に投入してみてください。応援していますよ!
