データの聖域を守る:FSOにおける「上書き拒絶」のメカニズムと、Err.Number 58を制御する極限の設計思想
エンタープライズの自動化領域において、レガシーシステムは単なる「古いコード」ではない。それは、数十億のトランザクションを支え続けてきた、動的なインフラストラクチャそのものである。
我々エンジニアが直面する最も忌むべき事態は、バグによるシステムダウンではない。「意図しないデータの消失」だ。
VBScript (Visual Basic Scripting Edition) を用いたファイル操作において、その最前線に立つのが `FileSystemObject` (FSO) である。中でも `CreateTextFile` メソッドの `Overwrite` 引数は、設計者の意志を直接ファイルシステムに叩き込む重要なスイッチだ。
本稿では、`Overwrite` 引数を `False` に設定した際に発生する `Err.Number = 58` (File already exists) を、単なる「エラー」としてではなく、「データの整合性を守るためのシグナル」としていかにエレガントに、かつ堅牢にハンドリングすべきか。その極致を解説する。
1. FSO `CreateTextFile` の内部構造と設計意図
まず、我々は抽象化されたメソッドの背後にある挙動を理解しなければならない。
Set ts = fso.CreateTextFile(strFilePath, bOverwrite)
この `bOverwrite` 引数は、真偽値 (Boolean) を取る。
- `True` を指定した場合:既存ファイルは無慈悲に抹消され、新しいストリームが書き込まれる。これは「破壊的変更」である。
- `False` を指定した場合:ファイルが存在すれば、FSOは即座に実行を中断し、エラーをスローする。
シニアエンジニアであれば、この `False` 指定を「上書き防止の防壁」として利用する。しかし、ここで多くの開発者が陥る罠がある。それは、「エラーが発生した際に、例外処理を適切に行わず、スクリプト全体をクラッシュさせてしまう」という初歩的なミスだ。
2. 脆弱な実装 vs 堅牢な実装
【アンチパターン】例外を無視、あるいは握りつぶす実装
多くの初心者は、エラーを回避するために `On Error Resume Next` を乱用する。これは、システム全体の予期せぬ挙動を招く「静かなる時限爆弾」だ。
' --- 悪手:エラーを無視する設計 ---
On Error Resume Next
Set ts = fso.CreateTextFile("C:\Data\CriticalLog.txt", False)
' もしファイルが存在しても、エラーは無視され、tsはNothingのまま進む。
' その後の ts.WriteLine は、Null Referenceエラー(Err.Number = 0x800A000A)を引き起こす。
On Error GoTo 0
【プロフェッショナル】Err.Number 58 を特定的に捕捉する設計
プロフェッショナルは、エラーを「拒絶」として受け入れ、それに基づいた分岐ロジックを構築する。
' --- 良手:特定のシグナル(Err.58)を制御する設計 ---
Dim fso, ts, strPath
strPath = "C:\Data\CriticalLog.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
On Error Resume Next ' エラーハンドリングを開始
Set ts = fso.CreateTextFile(strPath, False)
If Err.Number <> 0 Then
If Err.Number = 58 Then
' Err.Number 58: ファイルが既に存在する場合の戦略的ハンドリング
WScript.Echo "【警告】既存ファイルが存在するため、上書きを回避しました: " & strPath
' ここで、ファイル名をタイムスタンプ付きに変更する、あるいは処理を中断する等のロジックを走らせる
Else
' 58以外の致命的なエラー(権限不足、パス不正等)
WScript.Echo "【致命的エラー】予期せぬエラーが発生しました: " & Err.Description
Err.Clear
End If
On Error GoTo 0
WScript.Quit ' 安全のため、処理を中断
Else
' 正常に作成された場合
ts.WriteLine "Log Entry: " & Now
ts.Close
On Error GoTo 0
WScript.Echo "成功: ファイルが作成されました。"
End If
' オブジェクトの明示的解放(メモリリーク防止)
Set ts = Nothing
Set fso = Nothing
3. アーキテクトの視点:TOCTOU問題とWindows APIの境界
ここで、一歩踏み込んだ知見を共有しよう。
上記のコードは、`CreateTextFile(path, False)` を直接叩くことで、「存在確認」と「作成」をアトミック(不可分)に近い形で実行しようとしている。
ベテランが避けるべきパターンは、以下のような「二段構え」のチェックである。
1. `If fso.FileExists(path) Then …` でチェックする。
2. `If Not exists Then fso.CreateTextFile(path, False)` で作成する。
これは、TOCTOU (Time-of-Check to Time-of-Use) と呼ばれる競合状態(Race Condition)を生む。ステップ1とステップ2の極めて短い間に、別のプロセスがファイルを作成した場合、ステップ2でエラーが発生する。
Windows APIのレベルで言えば、`CreateFile` 関数の `dwCreationDisposition` 引数に `CREATE_NEW` を指定することに相当する。FSOの `Overwrite = False` は、まさにこの低レイヤーの挙動をエミュレートしている。したがって、「事前に FileExists で確認するのではなく、CreateTextFile のエラーハンドリングによって存在を検知する」ことこそが、最も確実な実装である。
4. メモリ管理とリソースのライフサイクル
VBScriptはCOM(Component Object Model)に基づいている。`FileSystemObject` や `TextStream` オブジェクトは、参照カウントによって管理されている。
大規模なバッチ処理や、ループ内で大量のファイルを操作するスクリプトを書く場合、`Set ts = Nothing` を怠ることは、メモリリークを意味する。特に、ファイルハンドルが開いたまま(`ts.Close` を忘れた状態)オブジェクトを解放しようとすると、ファイルがロックされ、後続のプロセスがそのファイルを操作できなくなるという、システム間連携における「死の連鎖」を引き起こす。
鉄則:
1. `ts.Close` でストリームを閉じる。
2. `Set ts = Nothing` で参照を切り離す。
3. `Set fso = Nothing` でリソースを解放する。
5. 結論
`Err.Number = 58` は、プログラムの失敗ではない。それは、システムが「データの整合性を守るために、設計者の意図通りに動作した証」である。
シニアエンジニアとして、我々はエラーを排除するのではなく、エラーを「制御可能な変数」として扱うべきだ。適切なエラーハンドリング、アトミックな設計、そして厳格なリソース管理。これらこそが、レガシーな環境においても揺るぎない信頼性を生み出す唯一の道である。
*Author: Chief Architect of Automation Systems*
