AutoCAD VBAを掌握する極限の知見
【実務中級】AcadDocument.Utility.GetEntityで選択された図形が「期待する種類」か即座に判定するガード節の設計
AutoCAD VBAの現場において、`Utility.GetEntity`メソッドはユーザーインタラクションの要である。しかし、このメソッドを素朴に実装するだけでは、実務に耐えうる堅牢なマクロとは言えない。ユーザーが意図しない図形をクリックした瞬間、ランタイムエラーが爆発するか、あるいは予期せぬ型変換エラーによってコードが沈黙する。
本稿では、選択されたオブジェクトが「真に期待する種類」であるかを`ObjectName`プロパティを用いてミリ秒単位で検証し、不一致を優雅に、かつ厳格に弾き返す「ガード節(Guard Clauses)」の極限実装を解説する。
レガシーなVBA環境であっても、メモリリークやCOMラッパーの解放漏れを意識したプロフェッショナルな作法を貫くことで、システム全体の信頼性は劇的に向上する。
—
1. なぜ `GetEntity` 単体では実務で使えないのか
`AcadDocument.Utility.GetEntity` は、画面上の図形とピック点を同時に取得するための強力なAPIである。しかし、このメソッドが返すのは汎用的な `Object` 型(COMの `IDispatch`)であり、内部の具象型が何であるかは実際に触れてみるまで保証されない。
‘ 【アンチパターン】型安全性を無視した素朴な実装
Dim returnObj As AcadObject
Dim basePnt As Variant
ThisDrawing.Utility.GetEntity returnObj, basePnt, “図形を選択してください: ”
‘ いきなり特定メソッドを叩くと、型違いで即座にクラッシュする
returnObj.Layer = “0”
実務システムにおいて、ユーザーが線分(`AcadLine`)を選ぶべきところでブロック参照(`AcadBlockReference`)を選択してしまうことは日常茶飯事である。ここで型チェックを怠ると、後続の処理で `Type mismatch`(実行時エラー 13)が発生し、業務が中断する。
—
2. ObjectName プロパティと COM オブジェクトのライフサイクル
図形の型を判定する最も確実かつ高速なアプローチが、`AcadObject.ObjectName` プロパティの利用である。これには `TypeName関数` よりも優位な点がある。`TypeName` が VBAラッパーとしての型名を返すのに対し、`ObjectName` は AutoCADの内部実体名(ObjectARXのクラス名、例: `”AcDbLine”` や `”AcDbBlockReference”`)を直接返すため、CADデータベースの構造と完全に一致する。
しかし、ここでシニアエンジニアが意識しなければならないのが COM オブジェクトの参照カウンタとメモリ管理 である。
VBAでは、取得したCOMオブジェクトは明示的に変数から解放(`Nothing`を代入)しないと、AutoCADのプロセス内にCOMラッパーの参照が残り続け、図面を閉じた際や長時間のバッチ処理でメモリリーク(Heap Fragmentation)を引き起こす。
—
3. 【実践】ガード節による堅牢な図形判定プロシージャ
以下に、指定した型(今回はポリライン)以外を完全に排除し、かつメモリ解放のベストプラクティスを網羅した実務レベルのコードを示す。
Option Explicit
Public Sub ExecuteWithPolylineGuard()
Dim targetObj As AcadObject
Dim pickPoint As Variant
Dim errNumber As Long
On Error GoTo ErrorHandler
‘ 1. ユーザーに図形の選択を要求
‘ ※ユーザーがESCキーを押した場合のエラー(Err.Number = -2147352567 等)をトラップするため、
‘ 事前にエラーハンドリングの準備が必要。
ThisDrawing.Utility.GetEntity targetObj, pickPoint, vbCrLf & “対象のポリラインを選択してください: ”
‘ 2. ガード節:オブジェクトが正常に取得できたか
If targetObj Is Nothing Then Exit Sub
‘ 3. ガード節:期待する図形の種類(ObjectName)か即座に判定
‘ ※LWPolyline (“AcDbPolyline”) と 従来型Polyline (“AcDb3dPolyline”) の両方を許容する場合の例
If Not IsValidPolyline(targetObj) Then
MsgBox “選択された図形はポリラインではありません。” & vbCrLf & _
“現在の図形タイプ: ” & targetObj.ObjectName, vbExclamation, “型不一致エラー”
GoTo CleanUp
End If
‘ ==========================================
‘ ここから先は「確実にPolylineである」ことが保証された安全な領域
‘ ==========================================
ProcessPolyline targetObj
CleanUp:
‘ 4. 厳格なメモリ解放(COMオブジェクトの参照破棄)
‘ ローカル変数であっても、明示的にNothingを代入しガベージコレクションを促す。
Set targetObj = Nothing
Exit Sub
ErrorHandler:
‘ ユーザーのESC中断(Err 2147352567 / Automation Error等)は正常終了として扱う
If Err.Number = -2147352567 Or Err.Number = -2147467259 Then
Resume CleanUp
Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End If
End Sub
‘ — ヘルパー関数:ポリライン判定のガードロジック —
Private Function IsValidPolyline(ByVal obj As AcadObject) As Boolean
Dim objName As String
objName = obj.ObjectName
‘ ObjectARXのクラス名で厳密に評価
Select Case objName
Case “AcDbPolyline”, “AcDb2dPolyline”, “AcDb3dPolyline”
IsValidPolyline = True
Case Else
IsValidPolyline = False
End Select
End Function
‘ — 実処理のスタブ —
Private Sub ProcessPolyline(ByVal poly As AcadObject)
‘ 既に型が保証されているため、安全にキャストして処理可能
Dim pl As AcadLWPolyline
Set pl = poly
MsgBox “有効なポリラインを検出しまし。頂点数: ” & UBound(pl.Coordinates) + 1, vbInformation
Set pl = Nothing
End Sub
—
4. チーフアーキテクトからの実務的助言
1. エラー番号のハードコーディング回避について
AutoCAD VBAにおける `GetEntity` のESCキャンセル時のエラー番号は、バージョンや環境(ビット数)によって挙動が微小に異なる場合がある。厳密には `Err.Number` のみならず、`Err.Description` の文字列部分(”User abort” 等)を併せて評価するか、あるいは `On Error Resume Next` を一時的に併用する設計も視野に入れること。
2. パフォーマンスへの配慮
`ObjectName` プロパティの呼び出しは、内部でCOMのBSTRを marshalling するため極わずかにオーバーヘッドがある。しかし、図形選択のようなユーザー操作を伴うイベントにおいては人間の知覚速度を遥かに超えて高速であり、ボトルネックになることはない。迷わず `ObjectName` による文字列比較を採用せよ。
3. システム間連携への拡張性
このガード節の思想(「入り口で型を検証し、不正なデータは即座に排除する」)は、AutoCAD内部のVBA完結型マクロだけでなく、Excel VBAからCOM経由でAutoCADを操作する外部連携システムにおいても、そのまま強力な武器となる。
アーキテクチャの美しさは、例外処理の美しさと同義である。型不一致の恐怖から解放された、堅牢で拡張性の高いコードベースを構築してほしい。
