なぜ、君のツールは「読み取り専用」で止まるのか? プロフェッショナルが実践するファイル属性制御の極意
業務自動化の現場において、もっとも「初歩的でありながら、もっとも致命的なバグ」を誘発するのが、ファイル属性の扱いである。
大量の図面をバッチ処理で一括変換するツールを作ったとしよう。順調に数百枚の処理が進んでいた矢先、ある1枚の図面がOSレベルで「読み取り専用(Read-Only)」に設定されていただけで、プログラムは無残にもランタイムエラーを吐き出し、停止する。あるいは、エラーを回避したつもりでも、書き込みができずに空のファイルが生成される。
これは、「AutoCADの操作」と「OSのファイルシステム」の境界線を理解していない設計の典型的な失敗である。
本記事では、単に「読み取り専用を解除するコード」を教えるのではない。なぜ開く前にチェックしなければならないのか、そして、実務に耐えうる「堅牢な設計」とはどうあるべきかを、アーキテクトの視点から伝授する。
1. 「事後対応」ではなく「事前検知」を選べ
初心者が書くコードは、常に「事後対応(Reactive)」である。
`Documents.Open` を実行し、エラーが出たら `On Error Resume Next` で誤魔化す。これは設計として極めて脆弱だ。
プロフェッショナルは「事前検知(Proactive)」を行う。
AutoCADという巨大なアプリケーションに、重たい図面ファイルをロードさせる(リソースを消費させる)前に、そのファイルが「操作可能か」をOSレベルで判定する。これが、パフォーマンスと安定性を両立させる唯一の道だ。
なぜ `GetAttr` を使うのか?
VBAには `GetAttr` という関数が存在する。これは、ファイルを開くことなく、そのファイルの属性(読み取り専用、隠しファイル、システムファイル等)をビットフラグとして取得できる関数だ。これを利用することで、AutoCADのメモリを汚染することなく、超高速にファイルの状態をスキャンできる。
2. 堅牢な設計のための思考プロセス
実務用ツールを設計する際、以下の4ステップを必ずプロセスに組み込め。
1. Existence Check: ファイルが物理的に存在するか?
2. Attribute Check: `GetAttr` を使い、`vbReadOnly` フラグが立っていないか?
3. Permission/Lock Check: (高度な実装)他のプロセスがファイルをつかんでいないか?
4. Resolution Strategy: 読み取り専用だった場合、「自動で解除する」のか「ユーザーに問う」のか、あるいは「スキップする」のか?
この「Resolution Strategy(解決戦略)」を決めずにコードを書くことは、設計図なしでビルを建てるのと同義である。
3. プロダクション・グレードのコード実装例
以下に、実務でそのまま利用できるレベルの、堅牢なファイルオープン・モジュールを示す。単なるコピペではなく、各パーツの役割を理解してほしい。
Option Explicit
' =================================================================
' Module: FileSystemManager
' Description: 図面操作におけるファイル属性の制御および安全なオープン
' =================================================================
''' <summary>
''' 指定された図面ファイルが読み取り専用属性を持っているか判定し、
''' 必要に応じて属性を解除してから図面を開く。
''' </summary>
''' <param name="filePath">対象ファイルのフルパス</param>
''' <param name="autoFix">Trueの場合、読み取り専用属性を自動で解除する</param>
''' <returns>開いた図面オブジェクト (Document)。失敗した場合はNothing</returns>
Public Function SafeOpenDocument(ByVal filePath As String, Optional ByVal autoFix As Boolean = False) As AcadDocument
Dim acadApp As AcadApplication
Dim doc As AcadDocument
Dim fileAttr As Integer
On Error GoTo ErrorHandler
' 1. 存在確認 (File System Check)
If Dir(filePath) = "" Then
Err.Raise vbObjectError + 513, "FileSystemManager", "ファイルが見つかりません: " & filePath
End If
' 2. 属性取得 (Attribute Check)
fileAttr = GetAttr(filePath)
' 3. 読み取り専用判定 (Read-Only Detection)
If (fileAttr And vbReadOnly) <> 0 Then
Debug.Print "警告: 読み取り専用属性を検知しました: " & filePath
If autoFix Then
' 属性の解除 (Attribute Modification)
' vbNormal (0) をベースに、読み取り専用ビット(vbReadOnly)を除去する
' 既存の属性(隠しファイル等)を保持したい場合は、ビット演算を用いる
Dim newAttr As Integer
newAttr = fileAttr And Not vbReadOnly
SetAttr filePath, newAttr
Debug.Print "属性を解除しました。"
Else
' 自動解除しない場合は、エラーとして処理を中断
Err.Raise vbObjectError + 514, "FileSystemManager", "ファイルが読み取り専用です。手動で解除するか、autoFixをTrueにしてください。"
End If
End If
' 4. AutoCADへのオープン要求 (Heavy Operation)
' ここまで到達した時点で、ファイルは書き込み可能な状態である可能性が高い
Set acadApp = ThisDrawing.Application
Set doc = acadApp.Documents.Open(filePath)
Set SafeOpenDocument = doc
Exit Function
ErrorHandler:
' エラーログの出力(実務ではログファイルへの書き出しを推奨)
Debug.Print "Critical Error in SafeOpenDocument: " & Err.Description
Set SafeOpenDocument = Nothing
End Function
''' <summary>
''' メイン処理の実行例
''' </summary>
Public Sub ExecuteBatchProcess()
Dim targetFile As String
Dim targetDoc As AcadDocument
' テスト用のパス(環境に合わせて書き換えること)
targetFile = "C:\Work\Project_A\Sample_Drawing.dwg"
' 堅牢なオープン試行
' autoFixをTrueにすることで、読み取り専用でも強制的に書き込みモードで開く
Set targetDoc = SafeOpenDocument(targetFile, True)
If Not targetDoc Is Nothing Then
Debug.Print "成功: " & targetDoc.Name & " を制御下に置きました。"
' --- ここに図面操作ロジックを記述 ---
' 例: targetDoc.Save
' ----------------------------------
Else
MsgBox "図面のオープンに失敗しました。詳細はイミディエイトウィンドウを確認してください。", vbCritical
End If
End Sub
4. アーキテクトからの警告:見落としがちな「真のロック」
ここで一つ、非常に重要な警告を与えておく。
`GetAttr` で `vbReadOnly` を判定し、`SetAttr` でそれを解除できたとしても、「ファイルが他のプロセスによってロックされている(使用中である)」問題は解決できない。
例えば、ネットワーク上の共有フォルダで、同僚がその図面を開いている場合、OSレベルでの属性は「読み取り専用」ではないが、書き込み権限は拒否される。この場合、`SetAttr` は成功しても、AutoCADの `Documents.Open` は「ファイルが使用中です」というエラーを投げる。
もし君が、極めて高い信頼性が求められるバッチ処理ツールを開発しているのなら、`SetAttr` の後に、「ファイルを排他的に開けるか試行する(Open For Append 試行)」 というステップを追加することを検討すべきだ。
プロのテクニック:ファイルロックの真偽判定
' ファイルが実際に書き込み可能(ロックされていない)かを確認する関数
Private Function IsFileWritable(ByVal filePath As String) As Boolean
Dim fileNum As Integer
On Error Resume Next
fileNum = FreeFile
' 書き込みモード(Append)で開こうと試みる
Open filePath For Append Access Write As #fileNum
If Err.Number = 0 Then
Close #fileNum
IsFileWritable = True
Else
IsFileWritable = False
End If
On Error GoTo 0
End Function
5. まとめ:設計の質が、ツールの寿命を決める
「動けばいい」という考えで書かれたコードは、運用フェーズで必ず牙を剥く。
特にファイル入出力が絡む業務自動化において、以下の3点は魂に刻んでおいてほしい。
1. AutoCADに頼るな、OSをコントロールせよ: `GetAttr` を使い、AutoCADが重い処理を始める前に事態を把握せよ。
2. エラーハンドリングは「例外」ではなく「設計」の一部である: 読み取り専用、存在しない、ロックされている……これらはエラーではなく、想定内の「シナリオ」として扱う。
3. 副作用を最小限に: `SetAttr` で属性を変更する場合、他の属性(隠し属性など)を破壊しないよう、ビット演算を正しく使いこなせ。
これらを遵守することで、君の書くコードは「ただのスクリプト」から、プロフェッショナルが信頼を寄せる「ソフトウェア」へと昇華する。
