「アクセス拒否」の壁を突破せよ。FSOのAttributes(属性)をビット演算で掌握する極意
こんにちは。自動化の荒波を渡るエンジニアの皆さん。
マクロの記録や、ネットで見つけたコードを繋ぎ合わせて「動いた!」と喜ぶフェーズは、もう卒業ですね。
これから皆さんが目指すのは、「予期せぬエラーに屈せず、システムが意図した通りに、エレガントに振る舞うコード」を書くプロフェッショナルです。
今日、皆さんに伝授するのは、ファイル操作の自動化において避けては通れない、しかし多くの初学者が「アクセス拒否(Permission Denied)」という名の壁に突き当たる問題の解決策です。
テーマは、FileSystemObject (FSO) の `Attributes` プロパティを使い、隠しフォルダやシステムフォルダを「ビット演算」でスマートに回避する手法です。
ここをクリアすれば、VBScriptの基礎はバッチリですよ。準備はいいですか?
1. なぜ、あなたのコードは「エラー」で止まるのか?
フォルダの中身を順番に見ていく(走査する)プログラムを書いたとき、こんな経験はありませんか?
> 「フォルダを一つずつ辿っている最中に、突然『実行時エラー 70: 書き込み禁止、またはアクセスが拒否されました』が出て止まってしまう……」
これは、あなたのコードが悪いのではありません。「触れてはいけない聖域」に足を踏み入れてしまっただけなのです。
Windowsの世界には、ユーザーが普段は見ることができない「隠しフォルダ(Hidden)」や、OSの動作に不可欠な「システムフォルダ(System)」が存在します。これらを無理に開こうとすると、OSは「ここは君が触れる場所じゃないよ」とエラーを返します。
これを回避するには、フォルダの中に入る「前」に、そのフォルダがどのような性質(属性)を持っているかを確認しなければなりません。
2. `Attributes` プロパティ:フォルダの「正体」を見抜く
FSOの `Folder` オブジェクトには、`Attributes` というプロパティがあります。ここには、そのフォルダが「隠し属性を持っているか」「読み取り専用か」といった情報が詰まっています。
しかし、ここで一つ、プロの視点として重要な事実をお伝えします。
「属性は、単一の数値としてではなく、『フラグの集合体』として格納されている」
ここが、初心者が最も陥りやすい罠です。
罠の例:単純な比較
例えば、「隠し属性(値は 2)」かどうかを判定しようとして、以下のようなコードを書いてしまうことがあります。
' ❌ 危険な書き方
If objFolder.Attributes = 2 Then
' 隠しフォルダなら処理する...
End If
これでは不十分です。なぜなら、あるフォルダが「隠し属性(2)」かつ「システム属性(4)」を持っていた場合、その `Attributes` の値は `2 + 4 = 6` になるからです。`6 = 2` は成立しないため、このコードは「隠しフォルダ」を見逃してしまいます。
3. 解決策:ビット演算 `And` による「フラグの抽出」
では、どうすればいいのか? そこで登場するのがビット演算(Bitwise Operation)です。
ビット演算の `And` を使うと、数値の中に含まれる特定の「フラグ」だけをピンポイントで抜き出すことができます。
魔法の数式
「隠し属性(2)が含まれているか?」を判定する正しい書き方はこうです。
' ✅ 正しい書き方(ビット演算)
If (objFolder.Attributes And 2) Then
' 隠し属性が含まれていれば、この条件が True になる
End If
図解的なイメージ:
数値はコンピュータ内部では「0と1の並び」で管理されています。
- 隠し属性 (2) : `0010` (2進数)
- システム属性 (4) : `0100` (2進数)
- 両方の属性を持つフォルダ (6) : `0110` (2進数)
ここで `6 And 2` を計算すると:
0110 (6)
& 0010 (2)
----------
0010 (結果は 2 = True扱い)
このように、`And` を使うことで、他の属性がどんなに混ざっていようとも、「隠し属性のビットが立っているか」だけを正確に判定できるのです。
4. 実践:安全なフォルダ走査スクリプト
それでは、学んだことを使って、実際に使えるコードを書いてみましょう。
このスクリプトは、指定したフォルダ以下のすべてのフォルダを探索しますが、「隠しフォルダ」と「システムフォルダ」は安全にスキップします。
Option Explicit
' --- 定数の定義 (マジックナンバーを避けるためのプロの作法) ---
Const ATTRIBUTE_HIDDEN = 2
Const ATTRIBUTE_SYSTEM = 4
Dim fso, targetPath
Set fso = CreateObject("Scripting.FileSystemObject")
' 探索を開始するパスを指定してください
targetPath = "C:\Your\Target\Path"
If fso.FolderExists(targetPath) Then
WScript.Echo "探索を開始します..."
RecursiveScan fso.GetFolder(targetPath)
WScript.Echo "探索が完了しました。"
Else
WScript.Echo "指定されたパスが存在しません。"
End If
' --- 再帰的にフォルダを走査するメイン関数 ---
Sub RecursiveScan(objFolder)
Dim subFolder, attr
' フォルダの属性を取得
attr = objFolder.Attributes
' 【重要】ビット演算で「隠し」または「システム」属性をチェック
' (attr And 2) が 0 以外なら隠し、(attr And 4) が 0 以外ならシステム
If (attr And ATTRIBUTE_HIDDEN) Or (attr And ATTRIBUTE_SYSTEM) Then
' 属性に該当する場合は、中に入らずにスキップする
' WScript.Echo "スキップ中: " & objFolder.Path ' デバッグ用
Exit Sub
End If
' ここに、フォルダが見つかった時の処理を書く
WScript.Echo "発見: " & objFolder.Path
' サブフォルダをループで処理
On Error Resume Next ' アクセス権限エラー対策の保険
For Each subFolder In objFolder.SubFolders
If Err.Number = 0 Then
' 再帰呼び出し:自分自身を呼び出して、さらに奥のフォルダへ
RecursiveScan subFolder
Else
' エラーが発生した場合は、そのフォルダを飛ばす
' WScript.Echo "エラー回避: " & subFolder.Path
Err.Clear
End If
Next
On Error GoTo 0 ' エラーハンドリングを元に戻す
End Sub
コードの解説ポイント
1. 定数の定義: `2` や `4` と直接書かず、`ATTRIBUTE_HIDDEN` と名付けることで、コードの読みやすさ(可読性)を劇的に向上させています。
2. ビット演算の活用: `(attr And ATTRIBUTE_HIDDEN) Or (attr And ATTRIBUTE_SYSTEM)` によって、どちらかの属性でも持っていれば即座に `Exit Sub`(関数を抜ける)させています。
3. 再帰(Recursion): `RecursiveScan` の中で自分自身を呼び出すことで、何階層あるフォルダでも自動的に掘り進んでいく構造にしています。
4. エラーハンドリング: `On Error Resume Next` を最小限の範囲で使用し、万が一のアクセス拒否時にもスクリプトが強制終了しないよう、堅牢性を高めています。
5. まとめ:エンジニアとしての第一歩
いかがでしたか?
単に「フォルダをループで回す」だけなら、誰でもできます。しかし、「フォルダが持つ属性の構造を理解し、ビット演算を用いて、エラーの芽を事前に摘み取る」。このひと手間が、プロとアマチュアを分ける境界線です。
今日学んだことは、VBScriptだけでなく、C#やC++、Pythonといった他の言語でも応用できる、コンピュータサイエンスの普遍的な知見です。
「なぜ動かないのか?」に直面したときは、ぜひオブジェクトの「中身(属性)」を疑ってみてください。
ここをクリアすれば、VBScriptの基本はバッチリですよ。
次は、もっと複雑な自動化の世界へ一緒に行きましょう。応援しています!
