AutoCAD VBAを掌握する極限の知見
【実務中級】AcadDocument.Utility.GetEntityでの「選択対象のフィルタリング」:円やポリライン以外が選ばれた場合に再選択を促す堅牢なUI
AutoCAD VBAの実務開発において、最もユーザーフレンドリーかつ堅牢性が求められるのは「図形の対話的選択(GetEntity / GetSelection)」のプロセスである。
アマチュアプログラマブルなコードは、`GetEntity` でユーザーが適当な図形をクリックした瞬間、想定外のオブジェクト(例えば文字やハッチング)が選択されてエラー落ちするか、あるいは型ミスマッチ(Type Mismatch)を引き起こしてマクロが沈黙する。
シニアエンジニアが構築すべきは、不確実なユーザー入力を包み込み、目的の図形(例えば「LWPolyline」や「Circle」など)が捕捉されるまで静かに、しかし確実にループを維持する「自己防衛型のUI構造」である。
今回は、VBAのランタイムエラー、COMのオブジェクトライフサイクル、そしてAutoCAD内部のデータベース構造の特性を踏まえた、極限まで最適化された選択フィルタリングの実装手法を解き明かす。
—
1. `GetEntity` の罠とCOMオブジェクトの現実
`AcadUtility.GetEntity` メソッドは、画面上のオブジェクトをオブジェクト参照(`Object` 型)と選択点(`Variant` 型)として取得する極めて便利な関数だ。しかし、このメソッドには2つの重大な設計上の罠がある。
1. 予期せぬオブジェクトの返却:
フィルタ条件をネイティブで指定できない(`GetSelection` のようなSelectionSetフィルタが使えない)ため、ユーザーが何をクリックしようと、一度はオブジェクトへのポインタが返されてしまう。
2. VBAにおけるCOMラッパーのメモリ肥大化:
`GetEntity` が返すオブジェクトは、AutoCADのC++コア(ARX)が生成したCOMラッパーである。これを適切に解放(Release)しないままループを回すと、VBAのVBE環境およびAutoCADプロセス内のCOMメモリがリークし、長時間のバッチ処理や常時稼働システムにおいて致命的なパフォーマンス低下を引き起こす。
この課題を克服するには、「厳密な型判定(TypeName または Is)」と「明示的なオブジェクト参照の破棄(`Nothing`代入)」をループのワンサイクルごとに完遂させる必要がある。
—
2. 堅牢な再選択ループの実装コード
以下のコードは、ユーザーに「軽量ポリライン(AcDbPolyline)」または「円(AcDbCircle)」の選択を強制し、それ以外のオブジェクト(線分や文字など)が選択された場合には警告を発して再選択を促す、実務仕様のプロシージャである。
Option Explicit
Public Sub SelectTargetEntityExample()
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing.Application.ActiveDocument
Dim targetObj As AcadObject
Dim pickPoint As Variant
Dim isSelected As Boolean
isSelected = False
‘ 画面描画の最適化(必要に応じて有効化)
‘ acadDoc.Utility.Prompt “ターゲット図形の選択を開始します。”
Do While Not isSelected
On Error Resume Next
‘ ユーザーに対話型選択を要求
acadDoc.Utility.GetEntity targetObj, pickPoint, vbCrLf & “【指定】円または軽量ポリラインを選択してください: ”
‘ エラー番号 503 は「ユーザーがESCキーでキャンセルした」ことを示す
If Err.Number = -2147352567 Or Err.Number = 503 Then
Err.Clear
MsgBox “選択がキャンセルされました。”, vbExclamation, “処理中断”
Exit Sub
End If
‘ その他の予期せぬエラーの捕捉
If Err.Number <> 0 Then
Err.Clear
MsgBox “選択処理でエラーが発生しました。再度選択してください。”, vbCritical, “エラー”
GoTo ContinueLoop
End If
On Error GoTo 0
‘ オブジェクトが正しく取得できたかの判定
If Not targetObj Is Nothing Then
‘ TypeName関数またはTypeOf演算子による厳密な型判定
‘ AutoCAD VBAにおける内部クラス名を正確に評価する
Select Case TypeName(targetObj)
Case “IAcadLWPolyline”, “IAcadCircle”
‘ 目的のオブジェクトである場合、フラグを立ててループを抜ける
isSelected = True
MsgBox “有効な図形が選択されました: ” & TypeName(targetObj), vbInformation, “成功”
Case Else
‘ 目的外の図形(Line, Text, Hatchなど)が選ばれた場合
MsgBox “【警告】選択されたオブジェクトは [” & TypeName(targetObj) & “] です。” & vbCrLf & _
“円または軽量ポリラインを指定してください。”, vbExclamation, “再選択”
‘ 【極限の知見】COMメモリリークを防ぐため、不要になった参照を即座に破棄
Set targetObj = Nothing
End Select
Else
‘ 何も選択されずにエンターが押された場合など
MsgBox “図形が選択されませんでした。再度選択してください。”, vbExclamation, “警告”
End If
ContinueLoop:
‘ ループの各イテレーションで確実にエラーステートをクリア
Err.Clear
Loop
‘ — ここ以降に目的の図形に対するビジネスロジックを記述 —
‘ 例: targetObj.Color = acRed
‘ 最終的なオブジェクトの解放
Set targetObj = Nothing
Set acadDoc = ThisDrawing.Application.ActiveDocument ‘ 解放時の安全性確保
End Sub
—
3. チーフアーキテクトが解説するコードの急所
① 厳密な型評価(`TypeName` の活用)
AutoCAD VBAにおいて、オブジェクトの型を判定する際、`TypeOf … Is` 構文も有効だが、COMインターフェースの多重継承構造やレイイトバインディングの特性上、`TypeName()` 関数によって返される文字列(`IAcadLWPolyline`, `IAcadCircle` 等)を直接評価する方が、予期せぬ型キャストエラーを回避できる。特に、旧来の `2Dポリライン(AcDb2dPolyline)` と現代の `軽量ポリライン(AcDbPolyline)` は内部構造が異なるため、厳密に区別する必要がある。
② ESCキー(UserCancel)のハンドリング
ユーザーが選択プロンプト中に `ESC` キーを押した場合、AutoCAD VBAは実行時エラー(通常は自動化エラー、または特定のHRESULT)を発生させる。これを捕捉しないと、デバッグモードに突入するか、不意にマクロが強制終了する。
エラー番号のトラップ(`Err.Number = -2147352567` 等)を適切に実装し、静かにプロシージャを抜け出すハンドリングが、プロフェッショナルなUIの必須条件である。
③ COMメモリ管理の徹底(`Set targetObj = Nothing`)
VBAのガベージコレクタは、プロシージャが終了するまでローカル変数のCOMオブジェクトをメモリ上に保持し続ける傾向がある。今回のコードのように、ユーザーが間違った図形を選び直す「ループ構造」を持つ場合、ループ内で `targetObj` を上書きし続けると、過去に指していたAutoCAD上のオブジェクトへのポインタがメモリ空間に宙ぶらりんになり、メモリフットプリントが肥大化する。
`Case Else` のブロック内で意図的に `Set targetObj = Nothing` を実行し、参照カウントを即座にデクリメントさせる配慮が、大規模図面を扱う現場でのクラッシュを防ぐ。
—
総括
AutoCAD VBAにおけるUIの優劣は、こうした「例外系(ユーザーの誤操作)」をいかにエレガントに吸収するかで決まる。
メッセージボックスで叱りつけるだけの粗雑なコードではなく、ユーザーの操作ミスを優しく包み込み、システムのリソースをもクリーンに保つ――このレベルのコード品質を担保してこそ、真に信頼されるCAD自動化システムと言える。
