AutoCAD VBAを掌握する極限の知見:`GetEntity`の罠と、実務で崩壊しない「ガード節」の設計思想
開発プロジェクトのリーダーである私のもとに、よくこんな泣きついてくるエンジニアがいる。
「図形選択をするマクロを作ったんですが、ユーザーが関係ない線や文字をクリックすると、型不一致エラー(Runtime Error 13)でVBAが強制終了するんです。どうリトライさせればいいですか?」
……甘い。実務の現場において、ユーザーが開発者の「期待通りの操作」をしてくれるなどという性善説は、今すぐゴミ箱に捨てるべきだ。
AutoCAD VBAを用いた業務自動化において、`AcadDocument.Utility.GetEntity` は最も頻繁に使われるメソッドの一つだ。しかし、このメソッドは「何が選択されたか」の型安全性を自動では保証してくれない。返されるのは、抽象化された `Object` 型の器だけだ。
今回は、この `GetEntity` が孕むオブジェクトモデルの闇を暴き、実行時エラーを完全封殺しながら、極限まで保守性の高いプロダクションコードを書くための「ガード節の極意」を伝授しよう。
—
なぜ「あの書き方」は非効率で危険なのか?
多くの初級・中級プログラマブルがやりがちなアンチパターンを見てみよう。
‘ 【悪例】典型的なダメコード
Dim ent As AcadEntity
Dim pt As Variant
ThisDrawing.Utility.GetEntity ent, pt, “図形を選んでください: ”
‘ エラーの予感しかしない直接判定
If ent.ObjectName = “AcDbPolyline” Then
‘ 処理…
End If
このコードの何が致命的か?
1. Null参照の恐怖:ユーザーが図形をクリックせず、何もない空間(ESCキーなど)を押した場合、`GetEntity` はオブジェクトを返さない。そこに `ent.ObjectName` アクセスを試みた瞬間、VBAは容赦なくクラッシュする。
2. 派生クラスの無視:AutoCADのデータベースにおいて、「ポリライン」と一言で言っても、旧式の `AcDb2dPolyline` なのか、現代の軽量ポリライン `AcDb3dPolyline` / `AcDbPolyline` なのか、あるいはブロック参照 (`AcDbBlockReference`) なのか。`ObjectName` の完全一致だけで判定しようとすると、微妙なバージョンの違いでコードが沈没する。
3. エラーハンドリングの欠如:予期せぬオブジェクトが選択された場合のフォールバック(再選択や処理スキップ)が考慮されていない。
プロのエンジニアであれば、「取得した直後にいかに早く、安全に門前払い(ガード)するか」を考えるべきだ。
—
堅牢なガード節設計の3ステップ
実務で通用するマクロを作るには、以下の3層防御を施したガード節を構築する。
1. オブジェクトの存在確認(Null/Nothing判定):そもそも図形が選択されたか。
2. 型(ObjectName)の厳密な検証:期待するCADオブジェクトの種類か。
3. 早期リターン(Guard Clause)によるネストの排除:条件を満たさない場合は即座にプロシージャを抜けるか、ループバックさせる。
これらを実装した、実務でそのまま使えるプロダクションコードを提示しよう。
—
【コピペOK】実務仕様の堅牢な図形選択プロシージャ
以下のコードは、ユーザーに「軽量ポリライン(AcDbPolyline)」または「ブロック参照(AcDbBlockReference)」の選択を促し、期待する種類でなければ優しく警告して再試行させる、実戦的で美しいモジュールだ。
Option Explicit
‘ —————————————————————–
‘ 狙った図形のみを安全に取得するメインプロシージャ
‘ —————————————————————–
Public Sub ExecuteSmartSelection()
Dim targetEntity As AcadEntity
‘ 1. ガード節付きの選択関数を呼び出す(ここでは「軽量ポリライン」を要求する例)
Set targetEntity = GetVerifiedEntity(“AcDbPolyline”, “処理対象の軽量ポリラインを選択してください: “)
‘ 2. 選択がキャンセルされた場合、または型違いで弾かれた場合の処理
If targetEntity Is Nothing Then
MsgBox “図形の選択がキャンセルされました、または有効な図形ではありません。”, vbExclamation, “処理中断”
Exit Sub
End If
‘ — ここから先は「絶対に安全なオブジェクト」として業務ロジックを展開できる —
Dim plineObj As AcadPolyline
Set plineObj = targetEntity
MsgBox “選択成功! 処理対象のハンドル: ” & plineObj.Handle & vbCrLf & _
“頂点数: ” & plineObj.Coordinates, vbInformation, “成功”
‘ TODO: ここに実務の自動化処理(座標の吸出し、データベース連携など)を記述
End Sub
‘ —————————————————————–
‘ 【極限の知見】型安全性を保証するラッパー関数(ガード節の真髄)
‘ —————————————————————–
Private Function GetVerifiedEntity(ByVal expectedObjectName As String, ByVal promptMessage As String) As AcadEntity
Dim selectedObj As AcadEntity
Dim pickPoint As Variant
On Error GoTo ErrorHandler ‘ ユーザーのESCキーなどによるエラーをトラップ
Do
‘ 図形選択の実行
ThisDrawing.Utility.GetEntity selectedObj, pickPoint, promptMessage
‘ 【ガード1】オブジェクトが取得できているか(Nothing判定)
If selectedObj Is Nothing Then
Set GetVerifiedEntity = Nothing
Exit Function
End If
‘ 【ガード2】ObjectNameが期待値と一致するか
If StrComp(selectedObj.ObjectName, expectedObjectName, vbTextCompare) = 0 Then
‘ 条件クリア:検証済みオブジェクトを返却
Set GetVerifiedEntity = selectedObj
Exit Function
End If
‘ 【ガード3】不一致の場合のユーザーフレンドリーなフィードバック
Dim retry As VbMsgBoxResult
retry = MsgBox(“選択された図形の種類は [” & selectedObj.ObjectName & “] です。” & vbCrLf & _
“期待される種類は [” & expectedObjectName & “] です。” & vbCrLf & _
“再選択しますか?”, vbYesNo + vbExclamation, “図形種類の不一致”)
If retry = vbNo Then
Set GetVerifiedEntity = Nothing
Exit Function
End If
Loop While True
ErrorHandler:
‘ ユーザーがESCキーを押した場合や予期せぬエラーのトラップ
‘ Err.Number = -2147352567 (Automation error などのキャンセル時番号)
Set GetVerifiedEntity = Nothing
End Function
—
コードのアーキテクチャ解説
1. `StrComp` による大文字小文字を無視した比較
`selectedObj.ObjectName = expectedObjectName` でも動作はするが、CADのバージョンやオブジェクトの特性により厳密な文字列比較を行うため、`StrComp(…, vbTextCompare)` を採用している。プロのコードには「揺らぎ」を許さない配慮が必要だ。
2. ラッパー関数による関心事の分離 (Separation of Concerns)
「図形を選択して型を検証する」という面倒なプロセスを `GetVerifiedEntity` というプライベート関数にカプセル化している。これにより、メインのビジネスロジック (`ExecuteSmartSelection`) が極めてクリーンになり、保守性が飛躍的に向上する。
3. 無限ループとユーザーへの逃げ道
間違った図形を選んだ際、即座にエラーで落とすのではなく、`MsgBox` でユーザーに「何が間違っていたか(実際のObjectName vs 期待するObjectName)」を伝え、再試行かキャンセルを選ばせるUI/UX設計にしている。これが現場で「使われるツール」と「嫌われるツール」の分かれ道だ。
—
データベース連携・ファイル連携への布石
この「ガード節による厳密な型判定」をクリアしたオブジェクトだけを扱うアーキテクチャにしておくと、将来的な機能拡張が極めてスムーズになる。
- Excel/Access/SQLServer連携:
取得したオブジェクトが確実に `AcDbBlockReference` であると担保されていれば、その属性タグ(Attribute)を安全に抽出し、迷うことなく外部データベースのレコードとマッピングできる。
- 図面間データ移行:
他図面へのエンティティコピー (`CopyObjects`) を行う際、不正なオブジェクト混入によるメモリリークやトランザクションの崩壊を未然に防ぐことができる。
リーダーからのメッセージ
AutoCAD VBAは手軽反面、一歩間違えると不安定な「動くおもちゃ」になりがちだ。
しかし、今回紹介したような「オブジェクトモデルの境界線で厳格なガードを張る」設計思想を取り入れれば、エンプラ水準に耐えうる堅牢な業務自動化基盤へと生まれ変わる。
あなたの書くコードが、現場のエンジニアたちの信頼を勝ち取る最高のものであることを期待している。さあ、今すぐ既存のコードの `GetEntity` を見直し、強固なガード節を実装してほしい。
