【テクニカル・上級編】【上級】AcadApplication.EvalでAutoLISPの「(ssget “_X” … )」を呼び出し、VBAのフィルタ制限を突破する – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:`AcadApplication.Eval`でAutoLISPの`ssget`を完全統合し、VBAの限界を突破する

AutoCAD VBAは、そのアクセスの容易さと迅速なプロトタイピング能力において今なお強力なツールである。しかし、シニアエンジニアが避けて通れない「性能の壁」が存在する。その最たるものが、`AcadSelectionSets`を通じたオブジェクトのフィルタリング処理だ。

標準の`Add`メソッドによるSelectionSetフィルター(DXFグループコード)は、単一条件や単純なAND条件であれば機能するが、「特定の画層に属し、かつ特定の属性タグを持つ、非表示ではないブロック」といった複雑な複合条件、あるいは空間インデックスを駆使した高度な絞り込みを行おうとした途端、コードは肥大化し、実行速度は劇的に低下する。VBAのCOMレイヤーと内部のC++コアの間で発生するマーシャリングのオーバーヘッドが、処理を鈍化させるのだ。

この限界を粉砕するのが、AutoLISPの圧倒的な選択エンジン、`ssget` である。
今回は、`AcadApplication.Eval`メソッドを極限まで活用し、VBAからAutoLISPの強力な選択セット生成能力を直接呼び出す「ハイブリッド・アーキテクチャ」の真髄を伝授する。

—

1. なぜ `AcadApplication.Eval` なのか?

AutoLISPは、AutoCADのコアエンジンとダイレクトに結合されたLispインタプリタ上で動作する。その中でも `ssget` 関数の `”_X”`(図面全体スキャン)や、座標・条件を組み合わせたフィルタリング能力は、COM APIの比ではない高速性と柔軟性を誇る。

通常、VBAからAutoLISPの関数を叩く場合、`SendCommand`を用いる手法が知られているが、これはコマンドラインのエコーに依存し、画面描画のちらつき(Flicker)やタイミング同期の不安定さという致命的な欠点を抱える。

一方、`AcadApplication.Eval` は、LISP式を直接評価し、結果をバッファ経由で即座にVBA側に返す(あるいは内部変数に格納して連携する)ことが可能な隠し持った強力な武器だ。これを利用すれば、VBAの記述力・UI制御力と、AutoLISPの超高速スキャン能力のいいとこ取りができる。

—

2. アーキテクチャの設計思想:メモリとCOMのライフサイクル

`Eval` を用いてAutoLISPの選択セット(`SelectionSet`、LISP内ではピックセット=整数)を扱う場合、最大の懸念事項はメモリリークと図面データベースの整合性である。

AutoLISP側で生成された選択セットは、明示的に解放しない限りAutoCADのセッションメモリ内に残り続ける。VBAのオブジェクト変数がスコープを抜けても、LISP側のシンボルテーブルや選択セットバッファが汚染されるリスクがある。
したがって、以下の鉄則を遵守する。

1. 一意のシンボル名または無名変数での処理
2. 処理完了後の確実なLISP側メモリ解放(`ssdel` や空の選択セットによる上書き)
3. VBA側での `AcadSelectionSet` への安全なブリッジング

—

3. 実装コード:VBAからLISPの `ssget` を召喚する

以下のコードは、指定した「画層名」と「ブロック名」、さらに「特定の属性値」を持つブロック参照を一瞬で抽出し、VBAのSelectionSetとしてラップする実用的なプロシージャである。

Option Explicit

” =================================================================
” módulo: modLispBridge
” 概要: AcadApplication.Eval を用いた AutoLISP ssget ハイブリッドフィルタ
” =================================================================

Public Sub SelectBlocksWithLisp()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim ssetName As String
Dim lispExpr As String
Dim resultVar As Variant

‘ 1. アプリケーションとアクティブドキュメントの取得
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument

‘ 一意の選択セット名を動的生成(既存の競合を防ぐ)
ssetName = “LISP_BRIDGE_SS_” & Format(Now, “hhmmss”)

On Error GoTo ErrorHandler

‘ 2. 検索条件の定義(例: 画層=”0″, ブロック名=”VALVE_A”)
‘ AutoCAD LISPの ssget “_X” を用いて、条件に合致するエンティティ名(ENAME)のリストを取得する
‘ ここではDXFグループ: 0=INSERT(ブロック), 8=画層名, 2=ブロック名
Dim targetLayer As String
Dim targetBlock As String
targetLayer = “0”
targetBlock = “VALVE_A”

‘ AutoLISP式の構築
‘ (ssget “X” ‘((0 . “INSERT”) (2 . “VALVE_A”) (8 . “0”)))
‘ ※ 複雑な属性フィルタやAND/OR条件もこのLISP式の中に埋め込み可能
lispExpr = “(progn ” & _
” (setq ss (ssget \”X\” ‘((0 . \”INSERT\”) (2 . \”” & targetBlock & “\”) (8 . \”” & targetLayer & “\”)))) ” & _
” (if ss ” & _
” (vl-remove-if ‘null (mapcar ‘entget (vl-remove-if ‘listp (mapcar ‘cadr (ssnamex ss))))) ” & _
” nil” & _
” )” & _
“)”

‘ 3. AcadApplication.Eval による実行
‘ ※ 注意: Evalの戻値の型バリエーションに備えてVariantで受ける
‘ (複雑なリスト構造をEvalで直接VBAのVariant配列に完璧にマッピングするには限界があるため、
‘ 実務ではセレクトセット名をVBAに返し、VBA側でAcadSelectionSetに変換するアプローチが最も堅牢)

‘ — より実用的なアプローチ: LISP側で選択セット名(例: “S1″)を作成し、VBAから参照する —
Dim robustLisp As String
robustLisp = “(if (ssget \”X\” ‘((0 . \”INSERT\”) (2 . \”” & targetBlock & “\”) (8 . \”” & targetLayer & “\”))) ” & _
” (setq global-lisp-ss (ssget \”X\” ‘((0 . \”INSERT\”) (2 . \”” & targetBlock & “\”) (8 . \”” & targetLayer & “\”)))) ” & _
” (setq global-lisp-ss nil)” & _
“)”

acadApp.Eval robustLisp

‘ 4. VBA側のSelectionSetへの取り込み
Dim acadSSet As AcadSelectionSet

‘ 既存の同名選択セットがあれば削除
On Error Resume Next
acadDoc.SelectionSets(ssetName).Delete
On Error GoTo ErrorHandler

Set acadSSet = acadDoc.SelectionSets.Add(ssetName)

‘ LISPで保持した選択セット(global-lisp-ss)をVBAのSelectionSetオブジェクトに追加する方法はないため、
‘ ここはLISP側でハンドル(Handle)のリストを返すか、あるいはVBAの AddItems を使うためのエンティティ配列を構築する。

CleanUp:
‘ 5. メモリ解放(LISP側のグローバル変数をクリア)
acadApp.Eval “(setq global-lisp-ss nil)”
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “Eval Bridge Error”
Resume CleanUp
End Sub

—

4. 極限の最適化:ハンドル配列による超高速ブリッジ

上記のコードの課題は、LISPの選択セット(`SelectionSet`)をVBAのCOMオブジェクトモデルにどう美しく流し込むかだ。最もパフォーマンスが高く、メモリを圧迫しない手法は、LISP側で「エンティティのハンドル(Handle)」の文字列リストを返させ、VBA側で `AcadDocument.HandleToObject` を用いて一括バインドすることである。

このアプローチにより、COMの重いフィルタリング処理を完全にバイパスし、ダイレクトにオブジェクトへアクセスできる。

Public Sub GetHandlesViaEval()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim lispExpr As String
Dim handleListStr As String

Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument

‘ LISP側で条件に合う図形の「ハンドル」をカンマ区切りの文字列として結合して返す極限のワンライナー
‘ (vl-string-right-trim “,” (apply ‘strcat (mapcar ‘(lambda (x) (strcat (cdr (assoc 5 (entget x))) “,”)) …)))

lispExpr = “(let ((ss (ssget \”X\” ‘((0 . \”INSERT\”) (8 . \”0\”)))) (res \”\”))” & _
” (if ss” & _
” (progn” & _
” (setq i 0)” & _
” (while (< i (sslength ss))" & _ " (setq res (strcat res (cdr (assoc 5 (entget (ssname ss i)))) \",\"))" & _ " (setq i (1+ i))" & _ " )" & _ " )" & _ " )" & _ " res" & _ ")" ' Evalの実行(文字列としてハンドル群が返ってくる) Dim retVal As Variant retVal = acadApp.Eval(lispExpr) If VarType(retVal) = vbString And Len(CStr(retVal)) > 0 Then
Dim handles() As String
handles = Split(CStr(retVal), “,”)

Dim i As Long
Dim ent As AcadEntity

‘ トランザクション的な一括処理の開始
For i = LBound(handles) To UBound(handles)
If Len(handles(i)) > 0 Then
‘ HandleToObject は内部インデックスを引くため極めて高速
Set ent = acadDoc.HandleToObject(handles(i))

‘ — ここで取得したエンティティに対する業務処理を行う —
‘ 例: ent.Layer = “NEW_LAYER”
End If
Next i

MsgBox UBound(handles) & ” 個のオブジェクトをLISP-VBAハイブリッド処理しました。”, vbInformation
Else
MsgBox “条件に合致するオブジェクトはありません。”, vbExclamation
End If
End Sub

—

5. チーフアーキテクトからの警句:実運用における注意点

1. エラーハンドリングの厳格化
`Eval` の中でLISPの構文エラーが発生した場合、AutoCAD自体はクラッシュしないものの、VBA側にトラップしにくい不確定な戻り値が返ることがある。LISP式は必ず単体でAutoCADのコマンドライン(またはVisual LISP IDE)でデバッグし、完璧に動作するものだけをVBAに埋め込むこと。
2. 文字コードとセローション依存
図面内の文字列比較や画層名に日本語(マルチバイト文字)が含まれる場合、LISPの文字エンコード(特に古いバージョンやUnicode対応の過渡期)において予期せぬ挙動をすることがある。極力、画層名やブロック名は半角英数で設計された環境、または `vl-string` 系関数で安全に処理されるようLISP側をチューニングすること。
3. 過剰なエンジニアリングへの戒め
数個の図形を扱うだけの処理にこの手法を導入してはならない。数万オブジェクトが入り交じる大規模プラント図面や、建築BIMの統合図面など、「VBAの標準機能ではタイムアウトまたはメモリ不足になる」というボトルネックが明確なシーンにおいてのみ、このパンドラの箱を開くべきである。

システムはシンプルであるべきだが、限界に直面したとき、異なるパラダイム(この場合はAutoLISP)の力をシームレスに借用するブリッジ技術こそが、真に堅牢で拡張性の高いエンタープライズCAD自動化を支える。この知見をあなたの武器庫に加え、レガシーの制約を軽々と超越してほしい。

タイトルとURLをコピーしました