【上級】AcadDocument.Database.XRefEdit状態の検知:外部参照のインプレイス編集中の誤操作を防止する
AutoCAD VBAの限界領域に踏み込むエンジニアであれば、一度は「外部参照(Xref)のインプレイス編集(REFEDIT)」という魔物による予期せぬ挙動に悩まされたことがあるはずだ。
インプレイス編集がアクティブな状態(`XRefEdit`セッション中)のドキュメントデータベースは、通常のドキュメント構造とは異なる特殊なトランザクションとロック状態にある。この状態でVBAから無謀な一括処理、ブロック定義の書き換え、あるいは図面間のオブジェクト転送などを実行すれば、待っているのは致命的なデータベースの不整合(Corruption)、最悪の場合は即座の致命的エラー(Fatal Error)によるクラッシュだ。
本稿では、AutoCADのオブジェクトモデルの深層に潜り、`AcadDocument.Database.XRefEdit`状態を完璧に検知し、暴走するVBAマクロからシステムを守り抜く極限の知見を授ける。
—
1. 外部参照インプレイス編集の正体とVBAの盲点
AutoCAD上で外部参照をインプレイス編集している時、内部的には `Nref`(Nested Reference)関連のシステム変数や、データベース内の特殊なプッシュ/ポップ機構が働いている。
VBAの標準オブジェクトモデルにおいて、`ThisDrawing.ActiveDocument` は常に現在のドキュメントを指すが、REFEDITコマンドが実行されている最中であっても、VBAからは通常の図面編集中と全く区別がつかない状態でコードが実行されてしまう。
これが何を意味するか。
例えば、全画層のプロパティを書き換えるバッチ処理や、ブロックテーブルを走査してパージを行うルーチンを組んでいた場合、ユーザーがうっかり外部参照のインプレイス編集を開いたままそのマクロを実行すると、参照元の親図面ではなく、一時的に展開・ロックされた参照アセンブリのデータベースに対して破壊的な操作を行ってしまうのだ。
この惨劇を防ぐためには、VBAからデータベースのロック状態、あるいはそれに準ずるシステム状態を直接監視・検知するメカニズムを構築しなければならない。
—
2. 実装アプローチ:システム変数とデータベースステータスの複合監視
残念ながら、純粋なAcadオブジェクトモデルのプロパティ一発で「現在インプレイス編集中である」と返してくれるスマートなプロパティは存在しない。
そのため、以下の2つのアプローチを組み合わせるのが、シニアエンジニアの採るべき定石である。
1. システム変数 `REFEDITNAME` の監視:現在インプレイス編集セッションがアクティブである場合、この変数に外部参照のブロック名が格納される。
2. データベースのトランザクション/ロック状態の検証:VBAから直接アクセスする `AcadDatabase` の振る舞いを安全サイドに倒す。
さらに、VBAから安全にAutoCADのシステム変数へアクセスするためには、エラーハンドリングを極限まで硬化させ、メモリリークやオブジェクトの解放漏れを防ぐ必要がある。
—
3. 実装コード:堅牢なXRefEdit検知・ガードモジュール
以下に、実務の現場でそのまま組み込める、極めて堅牢なVBAコードを示す。このモジュールをルーの最初に挟むことで、インプレイス編集中の誤操作を完全にブロックする。
Option Explicit
‘ =================================================================================
‘ 模块名: clsXRefGuard
‘ 概要: 外部参照インプレイス編集(REFEDIT)の状態を検知し、誤操作を防止するガードクラス
‘ =================================================================================
‘ 外部参照編集状態のエラー定義
Public Enum XRefEditStatus
xeNormal = 0 ‘ 通常状態(安全)
xeEditingInPlace = 1 ‘ インプレイス編集中(危険)
xeErrorState = -1 ‘ 状態取得失敗
End Enum
”’
”’
”’ 検証対象のAcadDocument
”’
Public Function CheckXRefEditState(ByVal targetDoc As AcadDocument) As XRefEditStatus
On Error GoTo ErrorHandler
Dim refName As String
‘ 1. Drawing.GetVariable を使用してシステム変数 “REFEDITNAME” を取得
‘ インプレイス編集中ではない場合、この変数の取得は通常エラー(または空文字)を返す
refName = “”
refName = targetDoc.GetVariable(“REFEDITNAME”)
‘ 2. REFEDITNAME が空でなければ、現在インプレイス編集がアクティブであると判定
If Len(Trim$(refName)) > 0 Then
CheckXRefEditState = xeEditingInPlace
Exit Function
End If
‘ 3. 追加の安全確認:DBMODやその他のフラグを必要に応じて検証
‘ (※AutoCADのバージョンによってはUSEFLAGS等の併用も有効)
CheckXRefEditState = xeNormal
Exit Function
ErrorHandler:
‘ システム変数が存在しない(初期状態等)のエラー番号はトラップする
‘ Err.Number 77 は変数が見つからない場合の一般的なオートメーションエラー
If Err.Number = -2147352567 Or Err.Number = 77 Then
CheckXRefEditState = xeNormal
Resume Next
Else
‘ 予期せぬエラーの場合は安全側に倒してエラー状態を返す
CheckXRefEditState = xeErrorState
End If
End Function
”’
”’
Public Function AssertSafeForExecution(ByVal targetDoc As AcadDocument, ByVal operationName As String) As Boolean
Dim status As XRefEditStatus
status = CheckXRefEditState(targetDoc)
Select Case status
Case xeNormal
AssertSafeForExecution = True
Case xeEditingInPlace
MsgBox “【致命的な操作の抑止】” & vbCrLf & _
“現在、外部参照のインプレイス編集(REFEDIT)が実行されています。” & vbCrLf & _
“操作 [” & operationName & “] はデータベースの不整合を招くため実行できません。” & vbCrLf & _
“編集セッションを保存または破棄してから再度実行してください。”, _
vbCritical + vbOKOnly, “AutoCAD VBA セーフティガード”
AssertSafeForExecution = False
Case Else
MsgBox “外部参照状態の判定中に不明なエラーが発生しました。安全のため処理を中止します。”, _
vbCritical + vbOKOnly, “AutoCAD VBA セーフティガード”
AssertSafeForExecution = False
End Select
End Function
—
4. 実務での活用パターン:メインルーチンへの組み込み
シニアエンジニアたる者、個別のプロシージャに場当たり的なIf文を散りばめるような無様な設計はしない。すべてのバッチ処理や一括変更処理の入口(Dispatcher)で、先ほどのガード関数をフックさせる。
Sub BatchProcessLayerOptimization()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument
‘ 【極限の知見】処理の実行前に必ずXRefEdit状態をアサートする
If Not AssertSafeForExecution(acadDoc, “画層一括最適化”) Then
‘ 処理を安全にアボート。メモリ解放を行って終了。
GoTo Cleanup
End If
‘ — 以下、安全が保証された領域でのメイン処理 —
‘ acadDoc.Database を使用した高度な処理をここに記述
‘ ————————————————
MsgBox “画層の最適化が正常に完了しました。”, vbInformation, “完了”
Cleanup:
‘ オブジェクト変数の明示的な解放(COMの参照カウンタを適切にデクリメント)
Set acadDoc = Nothing
Set acadApp = Nothing
End Sub
—
5. チーフアーキテクトからの戒め:メモリ管理とCOMのライフサイクル
VBAコードを書く際、`ThisDrawing` というグローバルショートカットに依存し切っている開発者は、大規模なAutoCADアドイン開発の現場では二流とみなされる。
`ThisDrawing` は便利だが、インプレイス編集状態や複数のドキュメント(`Documents` コレクション)をまたぐマルチドキュメント環境(SDI/MDI)において、意図しないコンテキストを掴み続ける原因となる。
明示的に `AcadApplication` から `ActiveDocument` を取得し、処理が終われば速やかに `Set … = Nothing` によってCOMオブジェクトの参照を解放すること。これを怠ると、AutoCADのプロセス内にVBAのCOM参照がゴーストとして残り、図面のクローズ時やCAD自体の終了時にクラッシュを引き起こす遠因となる。
外部参照のインプレイス編集という「歪んだ状態」をコードで正確に捉え、システムを護る。この細部へのこだわりこそが、プロフェッショナルとアマチュアを分かつ境界線である。
