はじめに:なぜ「読み取り専用」の判定漏れは現場の地獄を生むのか
自動化スクリプトを組むとき、開発者が最も陥りがちな罠が「ファイルは常に自分のものである」という性善説に基づいたコーディングだ。
例えば、AutoCADのバッチ処理や図面の一括処理マクロを作ったとする。
- ほかの設計者がすでにサーバー上で図面を開いている(排他制御)。
- 権限のないフォルダの図面をプログラムが掴んでしまった。
- 参照モードで開かれた図面に対して、無慈悲に `SaveAs` やオブジェクトの追加を実行する。
この状況で何が起きるか?
VBAの実行時エラー(「実行時エラー ‘-2145386446 (80200032)’: 図面は読み取り専用です」等)が突如としてポップアップし、夜間に無人稼働させるはずだった何百枚もの図面処理バッチが最初の数ファイルで完全にフリーズ・停止する。翌朝、出社したエンジニアが目にするのは、進捗ゼロの画面と絶望感だけだ。
プロの自動化エンジニアであれば、コードを書く前にこう自問すべきだ。
「その図面、本当に書き込めるのか?」と。
今回は、`AcadDocument.ReadOnly` プロパティを完全に手なずけ、実務の荒波の中でも絶対にクラッシュしない、堅牢なドキュメント制御の極意を伝授する。
—
AcadDocument.ReadOnly の正体とライフサイクル
AutoCADのオブジェクトモデルにおいて、現在アクティブな図面は `ThisDrawing`(型としては `AcadDocument`)として表現される。
この `AcadDocument` には、図面の状態を示す `ReadOnly` というブール値(Boolean)のプロパティが存在する。
- `True`: 図面は読み取り専用で開かれている(書き込み不可)。
- `False`: 図面は書き込み可能状態で開かれている。
一見すると「if文で判定するだけ簡単じゃん」と思うかもしれない。しかし、ここにAutoCAD特有のライフサイクルの罠がある。
罠:ドキュメントが開かれた「後」のステータス
`ReadOnly` プロパティは、あくまで「その瞬間、そのドキュメントインスタンスがどのような権限でロードされているか」を示すものに過ぎない。
例えば、プログラム側で `Documents.Open` メソッドを使って図面を開く際、第三者がすでに排他ロックをかけているファイルを無理やり開こうとすると、AutoCADは自動的に読み取り専用モードでファイルを開くか、エラーを吐く。
だからこそ、「ファイルを開いた直後、あるいは処理を実行する直前のミリ秒単位のタイミングで、必ず `ReadOnly` を検知し、処理を分岐・回避するガード節(Guard Clause)」を張る必要があるのだ。
—
【実践】プロダクションコード:堅牢な読み取り専用ガードの実装
それでは、実務の現場でそのまま使える、堅牢なVBAコードを提示しよう。
このコードは、単に判定するだけでなく、ログを残し、処理を優雅にスキップ(または読み取り専用としての別処理へ誘導)する設計になっている。
Option Explicit
”’
”’
Public Sub SafeProcessCurrentDrawing()
‘ 1. ドキュメントが存在するか(AutoCADが起動しているか)の基本チェック
If Not IsDrawingOpen() Then
MsgBox “処理対象の図面が開かれていません。”, vbCritical, “致命的エラー”
Exit Sub
End If
Dim targetDoc As AcadDocument
Set targetDoc = ThisDrawing
‘ 2. 【最重要】ガード節:読み取り専用かどうかの判定
If targetDoc.ReadOnly Then
‘ ログ出力やユーザーへの警告(バッチ処理ならログファイルへの書き込みを推奨)
Call WriteLog(“【スキップ】図面は読み取り専用です: ” & targetDoc.FullName)
MsgBox “指定された図面は現在「読み取り専用」で開かれているため、” & vbCrLf & _
“書き込みを伴う処理をスキップします。” & vbCrLf & _
“対象ファイル: ” & targetDoc.Name, _
vbExclamation, “処理中断”
‘ ここで処理を安全に抜ける(Exit)
Exit Sub
End If
‘ — 以下、書き込み権限が保証された安全な領域 —
On Error GoTo ErrorHandler
‘ 例:レイヤを追加する処理(書き込み権限が必須)
Call ExecuteMainProcess(targetDoc)
Exit Sub
ErrorHandler:
‘ 予期せぬエラーの捕捉
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
‘ 必要に応じてエラーログを残す
End Sub
”’
”’
Private Function IsDrawingOpen() As Boolean
On Error Resume Next
Dim docCount As Long
docCount = Application.Documents.Count
If Err.Number <> 0 Or docCount = 0 Then
IsDrawingOpen = False
Else
IsDrawingOpen = True
End If
On Error GoTo 0
End Function
”’
”’
Private Sub ExecuteMainProcess(ByRef doc As AcadDocument)
‘ トランザクションやオブジェクト追加のロジック
doc.Utility.Prompt “図面への書き込み処理を実行中…” & vbCrLf
‘ (ここに実際の自動化コードを記述)
MsgBox “処理が正常に完了しました。”, vbInformation, “完了”
End Sub
”’
”’
Private Sub WriteLog(ByVal message As String)
Debug.Print Format(Now, “yyyy/mm/dd hh:nn:ss”) & ” : ” & message
‘ 実務ではここでテキストファイルやDBへの書き込みを行うと強靭になります
End Sub
—
チーフアーキテクトからの設計アドバイス:バグを生まないための3箇条
実務で数千枚の図面を扱う自動化ツールを構築する場合、上記のコードに加えて以下のアーキテクチャ上の配慮が不可欠となる。
1. `Documents.Open` 時の第2引数(ReadOnly)を制御せよ
もしVBA側から他の図面をコードで開いて処理する場合(Batch Processing)、`Documents.Open` メソッドの引数を意識しろ。
‘ 構文: expression.Open(Name [, ReadOnly] [, Password])
Dim targetDoc As AcadDocument
Set targetDoc = Application.Documents.Open(“C:\Drawing\Sample.dwg”, True) ‘ 強制的に読み取り専用で開く
意図的に「読み取り専用」として開くことで、サーバー上の排他ロック(ファイルを占有してしまう事故)を回避し、他の設計者の邪魔をせずに図面内の情報(画層名やブロック定義など)を「読み取る」だけの安全な処理が可能になる。
2. エラーハンドリングに頼った設計をするな
「エラーが起きたら `Err.Number` で拾えばいいや」という甘い考えは、VBAプログラミングにおいて悪手中の悪手だ。
ファイルがロックされていることや読み取り専用であることは、プログラムを実行する「前」に予測・検知できる既知の状態(Known State)である。
予測可能な状態異常は、例外(Exception)で処理するのではなく、あらかじめ `If targetDoc.ReadOnly Then` のような条件分岐(ガード節)で弾くのが、美しく保守性の高いコードの絶対条件である。
3. マルチドキュメント環境(SDI/MDI)を意識せよ
近年のAutoCADはマルチドキュメントインターフェイス(MDI)を採用しており、複数の図面が同時にメモリ上にロードされている。
`ThisDrawing` は「現在ユーザーがアクティブにしている図面」を指すため、ユーザーが勝手に別の図面にフォーカスを切り替えた瞬間に、スクリプトのターゲットが変わってしまうリスクがある。
一括処理を行うロジックでは、`ThisDrawing` を漫然と使い回すのではなく、開いた際に取得した `AcadDocument` オブジェクトを変数に確実にバインドし、その変数に対して操作を完結させるスコープ設計を徹底してほしい。
—
おわりに
「たかが ReadOnly の判定」と侮るなかれ。
現場のインフラ環境(共有サーバー、NAS、PDMシステム、権限管理)は、開発者のローカル環境ほど優しくはない。ネットワークの遅延や他者とのファイルの奪い合いの中で、自動化ツールが何のエラーも出さずに静かに、しかし確実に安全装置を働かせてスキップする――これこそが、信頼される「プロダクション品質のVBAコード」である。
あなたの書くコードに強靭なガード節を組み込み、明日からの図面バッチ処理を完全無人化・ノーエラーへと導いてほしい。
