【一時ファイル】GetSpecialFolderとGetTempNameを使った安全なテンポラリファイル生成と自動消去
レガシーシステムの裏側で、今もなお静かに、そして強烈な負荷を支え続けているVBScript(Visual Basic Scripting Edition)。
現代のモダンな言語から見れば「前時代的な遺物」と嘲笑されることもあるが、OSの根幹にダイレクトにアクセスし、インフラストラクチャの一部として完結するその極限の軽快さは、未だに他の追随を許さない。
特に、バッチ処理や外部API連携、巨大なCSV/XMLのストリーム処理において避けて通れないのが「一時ファイル(テンポラリファイル)のライフサイクル管理」である。
「適当なパスに `temp.txt` なんて名前でファイルを作って、処理が終わったら消せばいいや」
――もし、本番環境でそのような実装をしているエンジニアがいるならば、今すぐその手を止めてほしい。複数プロセスが同時実行されるエンタープライズ環境において、ハードコーディングされた一時ファイル名は、ファイル競合(Sharing Violation)、データ破損、そして致命的なセキュリティホール(TOCTOU脆弱性)の温床となる。
今回は、FSO(FileSystemObject)が提供する `GetSpecialFolder` と `GetTempName` を極限まで使い倒し、プロセスの異常終了すら想定した「絶対にゴミを残さない堅牢なワークファイル管理アーキテクチャ」を紐解く。
—
1. FSOによるテンポラリ生成のメカニズムと限界
VBScriptでファイルシステムを操作する場合、`Scripting.FileSystemObject` をインスタンス化するのが定石だ。
一時ファイルを安全に生成するためには、以下の2つのメソッドを組み合わせる必要がある。
1. `GetSpecialFolder(2)`: システムがテンポラリ領域として予約しているフォルダ(例: `C:\Users\
2. `GetTempName`: ランダムな文字列を含む、他のファイルと絶対に重複しないファイル名を動的に生成する。
なぜ `GetTempName` が必要なのか?
`GetTempName` が生成するファイル名は `rad
.tmp` (#はランダムな16進数)のような形式をとる。これは単にランダムな文字列を生成しているだけでなく、生成された瞬間にその名前のファイルが物理的に存在しないことをファイルシステムレベルで保証するための第一歩となる(厳密な排他制御にはAPIの組合せが必要だが、VBScript単体ではこのプレフィックスとランダム性の組み合わせが最善の防衛策となる)。
—
2. 【実装】堅牢なテンポラリファイル管理スクリプト
以下に、実務の現場でそのまま使用できる、エラーハンドリングとリソースの明示的解放(メモリ最適化)を網羅したプロダクションコードを示す。
Option Explicit
‘ メイン処理の実行
Call Main()
Sub Main()
Dim fso, ts
Dim tempFolder, tempFileName, tempFilePath
‘ 1. FileSystemObjectの生成
Set fso = CreateObject(“Scripting.FileSystemObject”)
On Error Resume Next ‘ 予期せぬエラーによるゾンビファイル化を防ぐためのトラップ
‘ 2. システムのテンポラリフォルダを取得 (2 = TemporaryFolder)
Set tempFolder = fso.GetSpecialFolder(2)
‘ 3. 衝突しない一意な一時ファイル名を生成
tempFileName = fso.GetTempName()
tempFilePath = fso.BuildPath(tempFolder.Path, tempFileName)
WScript.Echo “生成された一時ファイルパス: ” & tempFilePath
‘ 4. 一時ファイルへの書き込み (ForWriting = 2, Create = True)
Set ts = fso.OpenTextFile(tempFilePath, 2, True)
If Err.Number <> 0 Then
WScript.Echo “CRITICAL: 一時ファイルの生成に失敗しました. Error: ” & Err.Description
Call Cleanup(fso, tempFilePath, ts)
Exit Sub
End If
‘ 業務データの書き込みシミュレーション
ts.WriteLine “ID,Name,Timestamp”
ts.WriteLine “1001,Enterprise Architecture,” & Now()
ts.WriteLine “1002,VBScript Core Engine,” & Now()
‘ ストリームを一旦閉じる(排他ロックの解除)
ts.Close
Set ts = Nothing
‘ — ここで一時ファイルを利用した重い処理(外部API送信や別プロセス実行など)を行う —
WScript.Echo “一時ファイルを用いた処理を実行中…”
‘ WScript.Sleep 3000 ‘ 処理の擬似ウェイト
‘ 5. 正常終了時の確実なクリーンアップ
Call Cleanup(fso, tempFilePath, Nothing)
On Error GoTo 0
If Err.Number <> 0 Then
WScript.Echo “非致命的なエラーが発生しましたが処理を継続しました。”
End If
Set fso = Nothing
WScript.Echo “すべての処理が正常に完了しました。”
End Sub
‘==============================================================================
‘ 冗長性と確実性を担保するクリーンアップ関数
‘==============================================================================
Sub Cleanup(ByRef fso, ByVal filePath, ByRef stream)
‘ ストリームが開いていれば強制クローズ
On Error Resume Next
If Not stream Is Nothing Then
stream.Close
Set stream = Nothing
End If
‘ ファイルが存在していれば強制削除(ゾンビファイルの排除)
If Not fso Is Nothing Then
If fso.FileExists(filePath) Then
fso.DeleteFile filePath, True ‘ Force = True (読み取り属性等も無視して削除)
WScript.Echo “クリーンアップ完了: テンポラリファイルを削除しました -> ” & filePath
End If
End If
On Error GoTo 0
End Sub
—
3. チーフアーキテクトが解説する「死角のない」設計思想
上記のコードには、レガシー環境で生き抜いてきたエンジニアの知見が幾重にも組み込まれている。
① オブジェクトの明示的解放とメモリ管理
VBScriptのCOMコンポーネント(特に `FileSystemObject` や `TextStream`)は、スクリプト終了時に自動解放されるとは言え、巨大なループ処理やタスクスケジューラから常駐的に叩かれるバッチにおいて、変数を `Nothing` に明示しない設計はメモリリークの温床となる。
特に `TextStream` はファイルハンドルを握り続けるため、処理が終わったら即座に `.Close` し、インスタンスを破棄することが鉄則だ。
② `On Error Resume Next` とクリーンアップの分離
VBScriptにはモダンな言語にある `try-finally` 構文が存在しない。そのため、処理途中でネットワーク切断やディスク容量不足などの致命的エラー(Err)が発生した場合、スクリプトが異常終了し、開いたままのファイルハンドルやゾンビファイルがOS上に放置される。
これを防ぐため、エラー発生時であっても必ず `Cleanup` サブルーチンを通過させ、`fso.DeleteFile filePath, True` によって物理ファイルを強制消去する担保構造を作っている。
③ 権限とファイル属性の罠 (`Force = True`)
Windows環境において、何らかの原因で一時ファイルに「読み取り専用」属性が付与されたり、別のプロセスがわずかにハンドルを掴んでいたりする場合、通常の削除命令は弾かれる。
`fso.DeleteFile` の第2引数に `True`(Forceフラグ)を指定することで、属性の壁をブチ破り、確実にゴミを残さない強靭さを実現している。
—
4. さらなる高みへ:Windows APIとの連携(高度な排他制御)
もし、金融系や基幹システムのログ処理など、「ミリ秒単位の競合すら許されない極限の環境」であるならば、FSO単体の機能だけでは不十分な場合がある。
その場合は、VBScriptからCOM経由でWMIやシェルオブジェクト、あるいはVBA/VBSの枠を超えたAPIアプローチを検討すべきだが、純粋なVBScriptの範疇であれば、「生成したテンポラリファイルのアクセス権(ACL)の継承」に気を配る必要がある。
`GetSpecialFolder(2)` が返すフォルダは通常、実行ユーザーのプロファイル配下であるためセキュリティ的には安全だが、IIS(Webサーバー)経由やSYSTEM権限で動くタスクから呼び出す場合、テンポラリフォルダのパスが意図せぬ場所(C:\Windows\Tempなど)に化けることがある。
そのため、スクリプトの冒頭で環境変数 `%TEMP%` や `%TMP%` を `WshShell.ExpandEnvironmentStrings` で明示的に取得し、FSOの `GetSpecialFolder` と二重で検証するアプローチをとるのが、真のプロフェッショナルの実装スタイルと言える。
—
総括
VBScriptは古い。しかし、古いからこそ「OSの仕様とハードウェアの挙動を理解している者」と「表面的なコードのコピペで凌いできた者」の差が、如実にコードの美しさと堅牢性に現れる。
今回解説した `GetSpecialFolder` と `GetTempName` による一時ファイル管理は、単なるファイル操作のテクニックではない。「システムに爪痕を残さない、クリーンでプロフェッショナルなエンジニアリングの精神」そのものである。
あなたの書くレガシーコードが、明日も止まることなく、インフラストラクチャの裏側で完璧に稼働し続けることを願う。
