【属性制御の極意】FSOのAttributesプロパティを完全掌握し、ファイルロックの罠を回避する技術
開発現場で自動化スクリプトを組んでいて、最もフラストレーションが溜まる瞬間の一つが、「ファイル更新時の予期せぬアクセス拒否エラー」ではないか。
特に、基幹システムから出力されたCSV、他のプロセスが生成したログファイル、あるいはバージョン管理の網をくぐり抜けてきたテンプレートファイルなど、実務で扱うファイルの多くには「読み取り専用(ReadOnly)」や「隠しファイル(Hidden)」などの属性が付与されている。
素人が書いたVBScriptは、こうしたファイルを無造作に書き換えようとして `Permission denied (エラー 70)` を吐き、無慈悲に停止する。
プロのエンジニアであれば、FSO(FileSystemObject)の `Attributes` プロパティが持つビット演算の仕組みを完全に理解し、「属性の安全な剥奪 ➔ 処理の実行 ➔ 属性の復元」というライフサイクルをトランザクション的に制御しなければならない。
今回は、VBScriptにおけるファイル属性制御の深淵と、実務の現場で絶対に破綻しないプロダクションコードの書き方を伝授する。
—
なぜ `Attributes` 操作でバグが頻発するのか?
多くの初学者が犯す最大の過ちは、`Attributes` プロパティに直接数値を代入することだ。
‘ 【悪手】絶対にやってはいけない実装
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set file = fso.GetFile(“C:\data\config.ini”)
‘ 読み取り専用を解除しようとして、直接「0」や「Normal(0)」を代入する
file.Attributes = 0
このコードの何が問題か?
`Attributes` は単なる数値ではない。「ビットフラグの集合体」なのだ。
ファイルには「読み取り専用」「隠しファイル」「システムファイル」「アーカイブ」などの状態が、ビット単位で同時に内包されている。もし対象のファイルが「隠しファイルかつ読み取り専用」であった場合、上記のように `0` を代入すると、隠しファイル属性まで吹き飛んでしまい、ファイルの存在自体がOSの視界から消える(あるいは予期せぬ挙動を引き起こす)という致命的なバグに繋がる。
ビット演算という「正しいアプローチ」
属性を安全に変更するためには、VBA/VBScriptにおける論理演算(Bitwise Operations)を用いる必要がある。
- 属性の付与(ONにする): ビット論理和 (`OR`)
- 属性の剥奪(OFFにする): ビット論理積 + ビット反転 (`AND` と `NOT`)
- 属性の判定: ビット論理積 (`AND`)
この基本原則を無視した場当たり的なコードは、エンタープライズ環境の自動化においては「爆弾」でしかない。
—
FSO属性定数の全貌
VBScriptにはVBAのような組み込み定数(`vbReadOnly`など)が標準で用意されていないものがあるため、FSOを扱う際は自分で定数を定義するか、数値の持つ意味を把握しておく必要がある。
| 定数名 | 値 (10進数) | 値 (16進数) | 意味 |
| :— | :— | :— | :— |
| `Normal` | 0 | `&H0` | 通常ファイル(属性なし) |
| `ReadOnly` | 1 | `&H1` | 読み取り専用ファイル |
| `Hidden` | 2 | `&H2` | 隠しファイル |
| `System` | 4 | `&H4` | システムファイル |
| `Archive` | 32 | `&H20` | アーカイブファイル(変更あり) |
| `Alias` | 1024 | `&H400` | ショートカット / リンク |
これらを組み合わせ、ビット演算子を用いて安全に制御する。
—
【実務仕様】堅牢な属性制御・更新・復元テンプレート
ここに示すのは、実際の業務自動化(RPAやバッチ処理)でそのまま組み込める、極めて堅牢なプロダクションコードだ。
「読み取り専用ファイルを一時的に書き込み可能にし、設定ファイルを上書き更新したのち、元の属性(隠しファイルや読み取り専用など)を完璧に復元する」一連の流れを実装している。
Option Explicit
‘ 処理の実行
Call UpdateProtectedFile(“C:\Automation\Settings\app_config.ini”, “Server=api.production.local” & vbCrLf)
Sub UpdateProtectedFile(targetPath, newContent)
Dim fso, file
Dim originalAttributes
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 1. ファイルの存在確認
If Not fso.FileExists(targetPath) Then
WScript.Echo “[ERROR] 対象ファイルが存在しません: ” & targetPath
Exit Sub
End If
Set file = fso.GetFile(targetPath)
‘ 2. 現在の属性を退避(トランザクションの準備)
originalAttributes = file.Attributes
WScript.Echo “[INFO] 現在の属性値: ” & originalAttributes
‘ 3. 読み取り専用(ReadOnly: 1)フラグが立っている場合のみ、安全に解除する
‘ 判定: (現在の属性 AND 読み取り専用フラグ) > 0
If (originalAttributes And 1) = 1 Then
‘ ビット反転とAND演算で、ReadOnlyビットだけをピンポイントでOFFにする
‘ 例: 属性が 3 (ReadOnly + Hidden) の場合 -> 3 AND (NOT 1) = 2 (Hiddenのみ残る)
file.Attributes = originalAttributes And Not 1
WScript.Echo “[INFO] 読み取り専用属性を一時的に解除しました。”
End If
‘ 4. ファイルの更新処理(エラーハンドリング付き)
On Error Resume Next
Dim stream
‘ ForWriting = 2, Create = True
Set stream = fso.OpenTextFile(targetPath, 2, True)
If Err.Number <> 0 Then
WScript.Echo “[CRITICAL] ファイルのオープンに失敗しました: ” & Err.Description
‘ エラー発生時も属性の復元を試みる
Call RestoreAttributes(file, originalAttributes)
Exit Sub
End If
stream.Write newContent
stream.Close
If Err.Number <> 0 Then
WScript.Echo “[CRITICAL] 書き込み中にエラーが発生しました: ” & Err.Description
Else
WScript.Echo “[SUCCESS] ファイルの更新が完了しました。”
End If
On Error GoTo 0
‘ 5. 元の属性へ確実に復元
Call RestoreAttributes(file, originalAttributes)
Set stream = Nothing
Set file = Nothing
Set fso = Nothing
End Sub
‘ 属性復元用ヘルパープロシージャ
Sub RestoreAttributes(targetFile, orgAttributes)
On Error Resume Next
targetFile.Attributes = orgAttributes
If Err.Number = 0 Then
WScript.Echo “[INFO] ファイル属性を元の状態 (” & orgAttributes & “) に復元しました。”
Else
WScript.Echo “[WARNING] 属性の復元に失敗しました: ” & Err.Description
End If
On Error GoTo 0
End Sub
—
現場のエンジニアが押さえておくべき「3つの罠」
上記のコードに至るまでに、数々の現場トラブルを踏んできた。実務でハマりがちなポイントを共有しよう。
1. アンチウイルスソフトや別プロセスによる「ファイルのロック」
属性を `ReadOnly And Not 1` で解除した直後であっても、OSがファイルハンドルの解放を完了する前に `OpenTextFile` を実行すると、タイミングエラー(Permission denied)が発生することがある。
高頻度で処理を行うバッチでは、ファイルアクセス直前に `WScript.Sleep 100` などのウェイトを挟むか、リトライロジックを実装するのがプロの作法だ。
2. ネットワークドライブ(UNCパス)における挙動の差異
ローカル環境(`C:\…`)では完璧に動作するコードも、社内ファイルサーバー(`\\server\share\…`)上のファイルを対象にした途端、権限の伝播やネットワーク遅延により `Attributes` の書き換えが拒絶されるケースがある。
ネットワーク上のファイルを操作する場合は、あらかじめローカルに `CopyFile` で引き離し、ローカルで処理した後に上書きコピーする設計の方が、結果的にトラブルが少ない。
3. オブジェクトのライフサイクルとメモリリーク
VBScriptのCOMコンポーネント(FSOやTextStream)は、スクリプト終了時に自動解放されるが、巨大なループ処理の中でインスタンスを生成・破棄し続けると、メモリリークやファイルハンドルの枯渇を引き起こす。
ループ内でFSOを使う場合は、必ず使い終わりに `Set file = Nothing` と明示的な参照破棄を行うこと。
—
まとめ
たかが「ファイルの属性変更」と侮るなかれ。
システムの自動化において、こうした細部(エッジケース)のハンドリングの有無が、「夜間バッチで平気で止まる脆弱なスクリプト」と「何年もの間、無人環境で稼働し続ける堅牢なツール」の分水嶺となる。
FSOの `Attributes` を扱うときは、常に「ビット演算によるピンポイント制御」と「トランザクション的な属性の退避・復元」を意識してほしい。あなたの書くスクリプトの信頼性は、劇的に向上するはずだ。
