AutoCAD VBAの限界を突破する:EvalによるLISPハイブリッド選択エンジンの実装
AutoCAD VBAを極めようとする者が必ず突き当たる壁がある。それは「複雑な図形選択」だ。
VBAの `SelectionSets.Select` メソッドは確かに強力だが、ネストされたブロック内の属性検索や、DXFコードを駆使した複雑な論理フィルタ(AND/OR/XORの組み合わせ)においては、その実装コストと実行速度の非効率さに絶望するはずだ。
一方で、AutoLISPの `ssget` は、30年以上の歴史が生んだ、まさに「選択のための最強のDSL(ドメイン固有言語)」である。我々アーキテクトが目指すべきは、VBAを制御の核としつつ、LISPの深淵な検索能力を `Eval` を通じて召喚する「ハイブリッドアーキテクチャ」だ。
1. なぜ「Eval」なのか?― アーキテクチャの視点
VBAからAutoLISP関数を実行するには `ThisDrawing.SendCommand` が一般的だが、これには大きな欠点がある。同期実行が難しく、コマンドラインへの文字列送り込みは、非同期的な挙動やタイミングの問題でシステムを不安定にさせる。
対して `AcadApplication.Eval` は、LISPの評価式を即座に実行し、その結果をVBAへ直接フィードバックする。これにより、VBA側は「選択の結果(SelectionSetの名前)」だけを受け取り、あとはVBAの強力なオブジェクト操作へ引き渡すという、極めてクリーンなパイプラインが完成する。
2. 実装:LISP選択エンジンをVBAに統合する
以下に、複雑なフィルタリングをLISPに丸投げし、VBAで結果を処理するための実用的なモジュールを示す。
‘ 伝説的なチーフアーキテクトによるハイブリッド選択ルーチン
Public Sub ExecuteHybridSelection()
Dim ssName As String
Dim objSS As AcadSelectionSet
‘ 1. LISPのフィルタを使って選択セットを作成する
‘ 例: 画層”0″にある、長さ50以上の線分だけを選択するフィルタ
‘ ‘((0 . “LINE”) (8 . “0”) (-4 . “>”) (40 . 50.0))
ssName = “MyHybridSet”
‘ 既存の選択セットをクリーンアップ(メモリリーク防止の鉄則)
On Error Resume Next
ThisDrawing.SelectionSets.Item(ssName).Delete
On Error GoTo 0
‘ Evalを使用してLISPの ssget を叩く
‘ 戻り値として選択セット名を返すように構成
Dim lispExpr As String
lispExpr = “(ssget “”X”” ‘((0 . “”LINE””) (8 . “”0″”)))”
‘ 注意: Evalは文字列を返す。LISPの選択セット名(SS:XXXX)をVBA側で捕捉するロジックを組む
‘ ※実務ではSendCommandでSS名を変数に格納し、VBAから取得する手法が最も堅牢である
‘ ここでは簡略化のため、選択されたオブジェクトをVBA側で処理するフローを示す
ProcessSelectionByLispFilter
End Sub
Private Sub ProcessSelectionByLispFilter()
‘ メモリ最適化:オブジェクトの明示的解放
Dim ss As AcadSelectionSet
Set ss = ThisDrawing.SelectionSets.Add(“TempSelection”)
‘ ここで複雑な選択条件をLISP経由で実行させる
‘ フィルタ: ブロック参照かつ特定の属性を持つもの
ThisDrawing.SendCommand “(ssget “”X”” ‘((0 . “”INSERT””) (66 . 1))) ” & vbCr
ss.Select acSelectionSetPrevious
‘ 処理ループ
Dim ent As AcadEntity
For Each ent In ss
‘ ここでVBAの強力なオブジェクト操作を展開
Debug.Print “Found: ” & ent.ObjectName
Next
‘ 終了後の後始末(AutoCADのメモリ管理は極めてセンシティブ)
ss.Delete
Set ss = Nothing
End Sub
3. シニアエンジニアが守るべき3つの鉄則
この手法を本番環境で運用する場合、以下の技術的負債を回避するための規律が必要だ。
① オブジェクトの明示的解放と破棄
VBAの `SelectionSets` は非常にメモリ食い虫である。`Set ss = Nothing` を書くだけで満足してはならない。必ず `ss.Delete` を呼び出し、AutoCADの内部メモリから選択セットを解放すること。これを行わないと、図面を開きっぱなしにするような環境では、数時間で `Out of Memory` エラーを引き起こす。
② SendCommandとEvalの使い分け
- SendCommand: 図面状態を物理的に変更する(コマンドを実行する)際に使用する。非同期性が強いため、直後のコードでオブジェクトに触る際は `DoEvents` や `Sleep` を噛ませて同期を取る必要がある。
- Eval: 戻り値(データ)を取得したい場合に使用する。こちらは計算結果を即座にVBA側に引き渡せるため、データ抽出に向いている。
③ エラーハンドリングの極致
`On Error Resume Next` を多用するのは素人の仕事だ。特定のメソッド呼び出し前後のみに限定し、必ず `On Error GoTo 0` で制御を戻すこと。特にLISP側でエラーが発生した場合、VBA側がフリーズする可能性があるため、LISPの `(vl-catch-all-apply …)` でエラーをラップした状態でVBAに結果を返すのが、最も洗練された防御的プログラミングである。
結論:技術は「使い分け」にある
VBAだけで全てを解決しようとするのは、AutoCADの構造を理解していない初心者の思考だ。AutoLISPの「選択能力」、VBAの「ロジック構築能力」、そして必要に応じて呼び出す「Windows API(メモリ管理やダイアログ制御)」。
これらを適材適所で組み合わせ、一つの有機的なシステムとして昇華させることこそが、我々エンジニアが目指すべき地平である。君たちの書くコードが、明日のAutoCADのパフォーマンスを決定づけるのだ。健闘を祈る。
