「ファイルが開けない!」を防ぐ。VBScriptにおけるTextStream解放の極意
「プログラムは動いたけれど、次に実行しようとしたら『ファイルが他のプロセスで使用されているため…』と怒られてしまった」
もしあなたがそんな経験をしたことがあるなら、おめでとうございます。あなたは今、プログラミングにおけるもっとも重要な概念の一つである「リソースの管理」という壁に直面しています。
マクロの記録や、ネットで見つけたコードをコピーして動かす段階から一歩踏み出し、現場で「本当に信頼されるエンジニア」になるために。今日は、VBScriptのファイル操作における最大の落とし穴、TextStreamのClose(クローズ)とリソース解放について、その本質を深く、かつ分かりやすく紐解いていきましょう。
ここをクリアすれば、VBScriptの基本はバッチリですよ。
1. なぜ「Close」を忘れると致命的なのか?
VBScriptでファイルの中身を読み書きするとき、私たちは `FileSystemObject` (FSO) を使い、その中の `TextStream` というオブジェクトを通じてファイルにアクセスします。
ここで重要なイメージを持ってください。ファイル操作とは、OS(Windows)という巨大な図書館から「本(ファイル)」を借りてくる行為です。
1. Open: 図書館の受付で本を借りる。
2. Read/Write: 借りた本を読み、書き込む。
3. Close: 本を返却する。
もし、あなたが本を借りたまま、途中で「あ、別の用事を思い出した!」と言って(`Exit Sub` などで)立ち去ってしまったらどうなるでしょうか?
図書館のルールでは、「返却されていない本は、他の誰も手に取ることができない」ことになっています。これが、VBScriptで発生する「ファイルロック」の正体です。
陥りやすい「失敗パターン」
以下のコードを見てください。一見、正しく動いているように見えますが、非常に危険です。
' ❌ 危険なコードの例
Sub BadProcess()
Dim fso, ts
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile("C:\temp\data.txt", 1) ' ファイルを開く
' 何らかの条件判定
If Not FileExists(ts) Then
' ここで処理を抜けてしまうと...
Exit Sub ' ⚠️ 致命的!ts.Close が呼ばれず、ファイルがロックされたままになる!
End If
' 本来の処理
MsgBox ts.ReadLine
ts.Close ' ここまで到達すればOKだが、途中で抜けると実行されない
Set ts = Nothing
Set fso = Nothing
End Sub
このコードでは、条件によって `Exit Sub` が実行されると、`ts.Close` を通り過ぎてしまいます。すると、そのプログラムが終了しても、Windowsは「まだ誰かがこのファイルを使っている」と誤認し続け、ファイルがロックされてしまいます。
2. プロが実践する「安全な解放」の黄金パターン
では、どうすればよいのでしょうか?
答えはシンプルです。「どんな理由で処理を抜けるときでも、必ず必ずCloseを通るように設計する」。これだけです。
これを実現するための、もっとも堅牢なパターンを紹介します。
✅ 黄金のテンプレート:エラーハンドリングとクリーンアップ
VBScriptには、現代的な言語にある `try…finally`(エラーが起きても必ず最後に実行する仕組み)がありません。そのため、「エラーが発生しても、最後の一箇所に集まってから去る」という設計思想が必要になります。
' ✨ プロフェッショナルな実装例
Sub SafeFileProcess()
Dim fso, ts
Dim filePath : filePath = "C:\temp\data.txt"
' 1. オブジェクトの初期化
On Error Resume Next ' エラーが起きても止まらず、次の行へ進む(エラー制御の開始)
Set fso = CreateObject("Scripting.FileSystemObject")
' ファイルが存在するかチェック
If Not fso.FileExists(filePath) Then
MsgBox "ファイルが見つかりません。"
GoTo Cleanup ' ❌ Exit Sub ではなく、後述の「Cleanup」へジャンプさせる
End If
' 2. ファイルを開く
Set ts = fso.OpenTextFile(filePath, 1) ' 1 = ForReading
If Err.Number <> 0 Then
MsgBox "ファイルを開けませんでした: " & Err.Description
GoTo Cleanup
End If
' --- ここからメインの処理 ---
MsgBox "ファイルの内容: " & ts.ReadLine
' --------------------------
' 3. 正常終了のための準備
' (特に追加の処理がなければ、そのままCleanupへ流れる)
' =========================================================
' 4. 【最重要】クリーンアップ・セクション
' ここは「Exit Sub」の代わりに、必ずここを通るように設計する
' =========================================================
Cleanup:
' エラー情報のクリア
Err.Clear
' TextStreamの解放
If Not ts Is Nothing Then
ts.Close ' ファイルを閉じる
Set ts = Nothing ' メモリからオブジェクトを削除
End If
' FSOの解放
If Not fso Is Nothing Then
Set fso = Nothing
End If
' エラーが発生していた場合に通知したいならここで判定
If Err.Number <> 0 Then
MsgBox "予期せぬエラーが発生しました。"
End If
End Sub
' ※注: VBScriptでは本来 GoTo は推奨されませんが、
' 構造化されていないVBScriptにおいて、リソース解放を確実に実行するためには
' この「一箇所に集約する」手法が極めて実用的です。
3. コードの深層:なぜ `Set obj = Nothing` まで必要なのか?
「`ts.Close` をすれば十分じゃないのか?」と思ったあなた。鋭いですね。
しかし、真のプロフェッショナルは `Set ts = Nothing` までセットで行います。これには明確な理由があります。
オブジェクトのライフサイクル
`ts.Close` は、「ファイル(本)を閉じる」行為です。
`Set ts = Nothing` は、「その本を手に持っている自分(オブジェクト)を消す」行為です。
VBScriptは「参照カウンタ」という仕組みでメモリを管理しています。オブジェクトが「まだ使われている」と判断されている限り、そのメモリは解放されません。
`Close` だけでは、オブジェクト自体はメモリ上に残り続けます。大量のファイルを扱うループ処理の中でこれを怠ると、メモリ使用量がじわじわと増えていく「メモリリーク」を引き起こし、最終的にシステムが不安定になる原因となります。
「Closeで窓を閉め、Nothingで部屋の電気を消す」。
このイメージを持っておけば、リソース管理で迷うことはありません。
まとめ:今日から使えるチェックリスト
新しいスクリプトを書くときは、心の中でこのチェックリストを唱えてください。
1. [ ] `Exit Sub` を安易に使っていないか?(使うなら、その前に必ず `Close` を呼んでいるか?)
2. [ ] `On Error Resume Next` を使ったら、適切にエラーをハンドリングしているか?
3. [ ] 処理の最後に、必ず `Close` と `Set … = Nothing` を書く「出口」を作っているか?
これらを意識するだけで、あなたの書くコードの品質は、初心者レベルから「プロのエンジニア」の領域へと劇的に進化します。
一歩ずつ、着実に。
リソースを制御できる者こそが、自動化の世界を支配するのです。応援していますよ!
