【実務・中級編】【アクセス拒否対策】CreateTextFile・DeleteFileのForceパラメータ仕様と事前権限チェックによるエラー回避策 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【アクセス拒否対策】CreateTextFile・DeleteFileのForceパラメータ仕様と事前権限チェックによるエラー回避策

開発現場でVBScriptを用いたファイル自動化スクリプトを組んでいて、最も遭遇頻度が高く、かつエンジニアの心を折るエラーが何か知っているか?

そう、「書き込みできません(エラー番号: 70 / 53)」だ。

バッチ処理の最中に突如としてスクリプトが停止し、タスクスケジューラがエラーを吐く。原因を調べると、対象のファイルに「読み取り専用(Read-Only)」属性が付いていた、あるいは他のプロセスが握っていた――。こんな無駄なトラブルシューティングに貴重な開発工数を割いていないだろうか。

今回は、FileSystemObject (FSO) におけるファイル操作の暗部、`CreateTextFile` と `DeleteFile` が持つパラメータの真の挙動と、実務の現場で絶対に破綻しない「事前防御型」の堅牢な設計論を授けよう。

—

1. FSOの仕様の罠:なぜ「上書き」や「削除」でエラーが起きるのか

VBScriptでファイルを操作する際、私たちは当たり前のように `FileSystemObject` を生成し、メソッドを叩く。

Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルを作成(または上書き)
Set ts = fso.CreateTextFile(“C:\Logs\app.log”, True)

ここで `CreateTextFile` の第2引数に `True`(overwrite: 上書き許可)を指定していれば、既存ファイルがあっても消して新しく作ってくれる……そう思っていないだろうか?

大間違いだ。

もし対象のファイルにOSレベルで「読み取り専用属性」が付付与されている場合、第2引数に `True` を渡していても、容赦なく 「書き込みできません (Permission denied)」 エラーが発生してスクリプトはクラッシュする。

`DeleteFile` メソッドにおいても同様だ。第2引数に `Force` パラメータ(強制削除フラグ)が存在するが、この挙動を正確に理解していないエンジニアが非常に多い。

—

2. Forceパラメータの正しい理解

各メソッドにおける「強制力」の仕様を整理する。

① `CreateTextFile` には「Force」引数はない

まず前提として、`CreateTextFile(filename, [overwrite], [unicode])` の第2引数はあくまで `overwrite` であり、読み取り専用属性を強制突破する機能はない。 読み取り専用ファイルに対してこれを実行すると、上書き許可(True)であっても即座にアクセス拒否となる。

② `DeleteFile(filespec, [force])` の Force の挙動

一方、`DeleteFile` の第2引数 `force`(真偽値)は、「読み取り専用属性が設定されているファイルであっても、強制的に削除する」 ためのフラグだ。

  • `False` (省略時): 読み取り専用ファイルが含まれているとエラーになる。
  • `True`: 読み取り専用属性を無視して削除を実行する。

しかし、この `Force = True` を過信してはならない。ファイルが別のプロセス(Excelや別プログラム)にロックされている場合、`Force` を指定してい測ってもアクセス拒否エラーは防げないのだ。

—

3. 業務自動化におけるベストプラクティス:事前権限・属性チェック

「エラーが出てから捕捉する(On Error Resume Next)」という場当たり的な実装は、VBScriptのコードを保守不能なスパゲッティにする最悪のアンチパターンだ。

真にプロフェッショナルな設計とは、「実行前に環境を検査し、安全な状態を作ってから操作する」 ことである。

以下の要件を満たす、プロダクションクオリティの関数群を見てほしい。

1. 対象ファイルが存在する場合、読み取り専用属性を動的に解除する。
2. 他者によるロック状態を検知する。
3. エラーハンドリングを適切に行い、ログに原因を残す。

—

4. プロダクションコード:堅牢なテキスト生成&ファイル削除ルーチン

以下のコードは、実際の業務自動化ツール(ログ出力、データ連携バッチ等)でそのまま流用できるモジュールだ。コピペして資産にしてほしい。

Option Explicit

Const ForWriting = 2
Const TristateUseDefault = -2

‘ ==============================================================================
‘ 処理メイン(テスト実行用)
‘ ==============================================================================
Sub Main()
Dim targetPath
targetPath = “C:\Data\output_sample.txt”

‘ 1. 安全なテキストファイルの作成(上書き対応・属性自動解除)
If SafeCreateTextFile(targetPath, “本日のバッチ処理を開始します。”, True) Then
WScript.Echo “ファイルの作成・書き込みに成功しました。”
Else
WScript.Echo “ファイルの作成に失敗しました。ログを確認してください。”
End If

‘ 2. 安全なファイルの削除(強制削除・属性解除・存在チェック付き)
‘ If SafeDeleteFile(targetPath, True) Then
‘ WScript.Echo “ファイルを安全に削除しました。”
‘ End If
End Sub

‘ ==============================================================================
‘ 関数名: SafeCreateTextFile
‘ 概要: 読み取り専用属性や存在を気にせず、安全にテキストファイルを作成・上書きする
‘ 引数:
‘ filePath (String) – 対象ファイルのフルパス
‘ content (String) – 書き込む文字列
‘ overwrite(Boolean)- 既存ファイルがある場合の上書き許可
‘ 戻り値: Boolean (Success: True, Fail: False)
‘ ==============================================================================
Function SafeCreateTextFile(ByVal filePath, ByVal content, ByVal overwrite)
Dim fso, fileObj, ts
Set fso = CreateObject(“Scripting.FileSystemObject”)

SafeCreateTextFile = False

On Error Resume Next

‘ ファイルが既に存在する場合の処理
If fso.FileExists(filePath) Then
If Not overwrite Then
WScript.Echo “[ERROR] ファイルが既に存在します: ” & filePath
Exit Function
End If

‘ 【重要】読み取り専用属性がついている場合は強制解除する
Set fileObj = fso.GetFile(filePath)
If (fileObj.Attributes And 1) = 1 Then ‘ 1 = ReadOnly Attribute
fileObj.Attributes = fileObj.Attributes Xor 1 ‘ 属性をOFFにする
End If
Set fileObj = Nothing
End If

‘ フォルダが存在しない場合は作成する親切設計
Dim parentDir
parentDir = fso.GetParentFolderName(filePath)
If Not fso.FolderExists(parentDir) Then
fso.CreateFolder(parentDir)
End If

‘ テキストファイルの作成と書き込み
‘ CreateTextFileの第2引数(overwrite)にTrueを渡しつつ、事前に属性を剥いでおくことで確実に成功させる
Set ts = fso.CreateTextFile(filePath, True, False)

If Err.Number <> 0 Then
WScript.Echo “[FATAL] ファイル作成失敗 (” & Err.Number & “): ” & Err.Description
Exit Function
End If

ts.Write content
ts.Close

If Err.Number <> 0 Then
WScript.Echo “[FATAL] 書き込み失敗 (” & Err.Number & “): ” & Err.Description
Exit Function
End If

Set ts = Nothing
Set fso = Nothing

SafeCreateTextFile = True
End Function

‘ ==============================================================================
‘ 関数名: SafeDeleteFile
‘ 概要: 読み取り専用属性やファイル不在を考慮した安全な削除関数
‘ 引数:
‘ filePath (String) – 削除対象のフルパス
‘ force (Boolean)- 強制削除フラグ (TrueでReadOnlyも削除)
‘ 戻り値: Boolean
‘ ==============================================================================
Function SafeDeleteFile(ByVal filePath, ByVal force)
Dim fso, fileObj
Set fso = CreateObject(“Scripting.FileSystemObject”)

SafeDeleteFile = False

If Not fso.FileExists(filePath) Then
‘ そもそも存在しない場合はエラーとせずTrueを返す設計(冪等性の担保)
SafeDeleteFile = True
Exit Function
End If

On Error Resume Next

Set fileObj = fso.GetFile(filePath)

‘ Forceが指定されている、または読み取り専用属性がついている場合の事前処理
If force Then
If (fileObj.Attributes And 1) = 1 Then
fileObj.Attributes = fileObj.Attributes Xor 1
End If
End If

Set fileObj = Nothing

‘ 削除実行
fso.DeleteFile filePath, force

If Err.Number <> 0 Then
WScript.Echo “[FATAL] ファイル削除失敗 (” & Err.Number & “): ” & Err.Description
Exit Function
End If

Set fso = Nothing
SafeDeleteFile = True
End Function

‘ 実行トリガー
Call Main()

—

5. チーフアーキテクトからの実践的アドバイス

このコードを見て、「なぜわざわざ属性をビット演算 (`Xor`) で解除しているのか」と疑問に思ったなら、君はコードの美しさとハードウェアレベルの挙動に関心がある優秀なエンジニアだ。

Windowsのファイル属性(`File.Attributes`)はビットフラグで管理されている。

  • ReadOnly: `1`
  • Hidden: `2`
  • System: `4`

単に `fileObj.Attributes = 0` と書き換えてしまうと、そのファイルが本来持っていた「隠しファイル属性」や「システム属性」まで吹き飛んでしまう。
そのため、`Xor` 演算子を用いて、他の属性を汚さずに「読み取り専用ビットのみをピンポイントで剥ぎ取る」というアプローチをとっている。これが、トラブルを未然に防ぐプロの流儀だ。

自動化スクリプトは、動くことだけが目的ではない。「例外的な環境(誰かがファイルを開いている、属性が固定されている)」においても、無言でクラッシュせず、綺麗にハンドリングするか、あるいは確実に環境を整えて完遂することが求められる。

今日の知見を君のシステムに組み込み、二度と「アクセス拒否」の夜間アラートで起こされない堅牢なアーキテクチャを実現してほしい。

タイトルとURLをコピーしました