【上級】AutoCAD VBAを掌握する:AcadApplication.Evalで解き放つ「ssget」の魔力
AutoCADの自動化において、VBAの `SelectionSets.Add` は極めて非力だ。
「画層と色と線種が一致し、かつ属性値が特定の条件を満たす図形を抽出したい」といった、実務で頻出する複雑なフィルタリングに対し、VBAの標準機能だけで挑むのは、鈍器で精密機械を修理しようとするようなものだ。ループを回して `Entity` を一つずつチェックする? そんな非効率なコードは今すぐ捨てろ。
我々が求めるのは、AutoLISPが誇る最強の選択フィルタエンジン「ssget」のパワーを、VBAの堅牢な構造の中に統合するハイブリッドな設計だ。
なぜ「VBAのSelectionSets」では限界があるのか
VBAの `SelectionSets` は、本質的に「IDのリスト管理」に過ぎない。複雑な条件を付与するには、全オブジェクトを走査して `If` 文の迷宮に潜る必要がある。これはパフォーマンスを著しく低下させ、図面規模が大きくなるほど処理時間が線形的に増大する。
一方、AutoLISPの `ssget` はC++レベルで最適化された内部インデックスを利用しており、フィルタ条件を一瞬で解決する。この「選別能力」をVBAから呼び出し、抽出されたオブジェクトをVBA側で料理する。これが、大規模開発における唯一の正解だ。
—
【極限実装】AcadApplication.Evalによるハイブリッド・ブリッジ
`AcadApplication.Eval` を介してLISPを実行し、その結果をVBAで受け取る。ここでの鍵は、「LISP側で選択セットを直接返すことはできない」という制限を、いかにエレガントに回避するかである。
プロダクションコード:高度なSSGET連携モジュール
このコードは、特定の条件(例:画層 “DIM” かつ 色 1)を満たす図形を一括で取得する設計だ。
‘ @brief LISPのssgetを利用してVBAで選択セットを構築する
‘ @param filterList LISP形式のフィルタリスト
‘ @return AcadSelectionSet
Public Function GetSelectionByLisp(ByVal filterStr As String) As AcadSelectionSet
Dim acadApp As Object: Set acadApp = ThisDrawing.Application
Dim ssName As String: ssName = “TEMP_SS_” & Format(Now, “hhmmss”)
‘ 1. LISPで選択セットを作成し、名前を付けて保持させる
‘ ssgetの結果を特定の変数に保存するラッパーLISPをEvalで実行
Dim lispCmd As String
lispCmd = “(ssget “”X”” ‘” & filterStr & “)”
‘ LISP側で選択セットを生成し、AutoCADの内部メモリにスタックさせる
‘ 注意: ssgetの結果は直接VBAで受け取れないため、LISP内で変数に格納する工夫が必要
acadApp.Eval “(setq ” & ssName & ” ” & lispCmd & “)”
‘ 2. VBA側で同名の選択セットを作成し、LISP側の結果をマッピングする
Dim ss As AcadSelectionSet
On Error Resume Next
Set ss = ThisDrawing.SelectionSets.Add(ssName)
If Err.Number <> 0 Then
Set ss = ThisDrawing.SelectionSets.Item(ssName)
ss.Clear
End If
On Error GoTo 0
‘ 3. LISPの選択セットからVBAオブジェクトへ変換(内部的にはエンティティの追加)
‘ ここが最大のポイント:ssgetで取得したリストをVBA側で認識させる
Dim count As Long
count = acadApp.Eval(“(if ” & ssName & ” (sslength ” & ssName & “) 0)”)
Dim i As Long
For i = 0 To count – 1
Dim ent As AcadEntity
Dim entName As String
entName = acadApp.Eval(“(vlax-ename->vla-object (ssname ” & ssName & ” ” & i & “))”)
‘ 本来は直接オブジェクトを渡せないため、ハンドル等を活用して取得する
‘ ここでは簡略化のためハンドルによる再取得を推奨
Dim entHandle As String
entHandle = acadApp.Eval(“(cdr (assoc 5 (entget (ssname ” & ssName & ” ” & i & “))))”)
ss.AddItems ThisDrawing.HandleToObject(entHandle)
Next i
Set GetSelectionByLisp = ss
End Function
—
堅牢な設計のための3つの鉄則
1. 選択セットのクリーンアップは必須
AutoCADの `SelectionSets` は一度作成すると、明示的に `Delete` しない限り残り続ける。`ss.Delete` を忘れることは、メモリリークの最大の要因だ。必ず `On Error` ブロックまたは終了処理で解放すること。
2. Evalの文字列構築に細心の注意を
`Eval` に渡す文字列は、クォーテーションのネストでカオスになりやすい。定数化するか、動的な生成ロジックを独立した関数に切り離せ。デバッグ時は `Debug.Print` で生成されたLISP文字列をコンソールに出力し、AutoCADのコマンドラインで直接実行してテストすること。
3. データベース連携の罠
抽出した図形をデータベースに反映する際、`ssget` で取得した直後に図形が修正される可能性がある。`SelectionSets` はあくまで「抽出時」の静的スナップショットであることを忘れてはならない。トランザクションの整合性を保つには、処理前に図面がロックされていないかを確認するロジックを挟むべきだ。
最後に:なぜこの手法が「伝説」なのか
多くのエンジニアは、VBAのAPIリファレンスをなぞるだけで満足する。だが、AutoCADの真のパワーは、LISP・VBA・.NETという「複数の言語レイヤー」を統合的に操る者が引き出すものだ。
この `ssget` 連携は、単なるコードテクニックではない。「既存の強力なツールを、言語の壁を超えて再利用する」という設計思想そのものである。この手法を使いこなせば、あなたが書くマクロは、単なる「自動化ツール」から、エンジニアリングの生産性を劇的に変える「CADエンジン」へと進化するだろう。
さあ、その手でAutoCADの制約を打ち破れ。
