AutoCAD VBAを掌握する極限の知見
【実務中級】AcadDocument.StartUndoMarkとEndUndoMarkの適切な配置:複雑な一括処理を「Ctrl+Z」一回で元に戻せるようにする親切設計
大規模な図面データに対し、数千・数万単位のエンティティを一括生成・改変するマクロを構築したとする。処理速度を極限まで高め、完璧な演算結果を出力したとしても、実行後に「Ctrl+Z」を押したとき、Undoスタックが細切れのトランザクションで埋め尽くされ、ユーザーが元の状態に戻すために何十回も「元に戻す」を連打させられる――そんなツールを、君は「実用的な業務システム」と呼ぶだろうか。
プロフェッショナルが構築するAutoCAD VBAアプリケーションにおいて、トランザクションの制御は単なる「お作法」ではない。それはUI/UXの優劣を決定づけ、メモリの肥大化を防ぎ、CADデータベースの整合性を担保する生命線である。
今回は、`AcadDocument.StartUndoMark` と `EndUndoMark` を駆使し、複雑怪奇な一括処理を完璧に単一のトランザクションとしてカプセル化する極意を授ける。
—
1. AutoCADのUndo機構とCOMラッパーの暗黒面
AutoCADの内部データベース(DBX)は、C++ベースの強固なトランザクション管理システムの上に構築されている。VBAから操作する場合、我々はCOM(Component Object Model)という薄い皮膜を介してこの巨大な要塞と対話している。
VBA側で明示的なUndoマークを付与しない限り、AutoCADはCOM経由で行われた個々のメソッド呼び出し(例:`AddLine`, `Update`, 座標の書き換えなど)を、それぞれ独立したUndoイベントとしてスタックに積み上げる。これが何を意味するか。
数千個の図形をループで処理するマクロ走らせた瞬間、Undoスタックは数千の履歴で飽和し、メモリを圧迫し、場合によっては致命的なパフォーマンス低下やクラッシュを引き起こす。
これを防ぐ唯一の手段が、「ここからここまでを1つの不可分の操作(Logical Transaction)として扱え」とAutoCADエンジンに強制的に命令することである。それが `StartUndoMark` と `EndUndoMark` の役割だ。
—
2. 実装パターン:堅牢なUndoグループ化の構文
実務において、Undoマークの配置には厳格なルールが存在する。最大の鉄則は「例外発生時におけるUndoマークの開放(デッドロックの回避)」である。エラーハンドリングを怠ると、Undoマークが閉じられないままセッションが宙ぶらりんになり、図面ファイル全体のUndo機構が破壊される。
以下のコードは、数千個の円を一括生成する処理を、完璧なトランザクション管理下で実行する実務レベルのテンプレートである。
Option Explicit
Public Sub BatchCreateCirclesWithUndo()
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing.Application.ActiveDocument
‘ 1. エラーハンドリングの準備(Undoのロストを防ぐため必須)
On Error GoTo ErrorHandler
‘ 2. 画面描画の凍結(パフォーマンスの極限最適化)
acadDoc.Application.ZoomExtents
‘ 3. 【最重要】Undoマークの開始
‘ acStartUndoMarkDoc (通常はこれを使用)
acadDoc.StartUndoMark acStartUndoMarkDoc
Dim i As Long
Dim centerPoint(0 to 2) As Double
Dim radius As Double
Dim circObj As AcadCircle
radius = 50.0
‘ 4. 大規模な一括処理のシミュレーション(例:5000個のオブジェクト生成)
For i = 1 To 5000
centerPoint(0) = (i Mod 100) 120.0
centerPoint(1) = (i \ 100) 120.0
centerPoint(2) = 0.0
‘ データベースへの書き込み
Set circObj = acadDoc.ModelSpace.AddCircle(centerPoint, radius)
‘ オブジェクト変数の明示的な解放(VBAのCOM参照リーク対策)
Set circObj = Nothing
Next i
‘ 5. 【最重要】Undoマークの終了(正常系)
acadDoc.EndUndoMark
‘ 画面更新の再開と通知
MsgBox “5000個のオブジェクト生成が完了しました。「Ctrl+Z」1回で元に戻せます。”, vbInformation, “チーフアーキテクトからの通知”
Exit Sub
ErrorHandler:
‘ 6. 異常系におけるリカバリ処理
‘ 万が一エラーで処理が中断した場合でも、Undoマークを必ず閉じる
If Err.Number <> 0 Then
‘ エラー伝播時にUndoスタックの整合性を保つためEndUndoMarkを強制実行
On Error Resume Next
acadDoc.EndUndoMark
On Error GoTo 0
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
End If
End Sub
—
3. チーフアーキテクトが教える、現場で直面する罠と回避策
罠その1:入れ子構造(Nested Undo Marks)の恐怖
`StartUndoMark` は入れ子にすることが理論上可能だが、実務でこれを安易に行うと、どの `EndUndoMark` がどの開始に対応しているのか把握できなくなり、Undoスタックが破損する。
原則:1つのプロシージャ、あるいは1つの明確なトランザクションブロックにつき、`StartUndoMark` と `EndUndoMark` は「1対1」で完全にペアリングさせよ。
罠その2:イベント(AcadDocument Events)との干渉
もし君のシステムが `AcadDocument` のイベント(例:`ObjModified` や `LayoutSwitched` など)を監視している場合、マクロ実行中のオブジェクト生成がイベントを誘発し、Undoの挙動に予期せぬ干渉を引き起こすことがある。
大規模な一括処理を行う前には、一時的にイベントの反応を抑制する設計(フラグ制御など)を組み合わせるのが、真のシニアエンジニアの処世術である。
罠その3:メモリのCOM参照リーク
先ほどのコード例でも示した通り、VBAにおける `Set xxx = Nothing` は単なる気休めではない。AutoCADのCOMオブジェクトは裏でC++のポインタを握っている。数千回のループ内でオブジェクト変数を解放し忘れると、VBAのガベージコレクションが追いつかず、メモリリークを引き起こす。Undoマークと組み合わせることで、データベースの整合性だけでなく、メモリ空間の美しさも保たれるのだ。
—
4. まとめ:コードの品質は「後始末」の美しさに宿る
動くだけのコードを書くのはアマチュアの仕事だ。「ユーザーがどう感じるか」「異常終了したときにシステムがどう振る舞うか」「CADの内部リソースにどのような負荷をかけるか」。そこまで深く思考を巡らせたコードだけが、企業の資産として何年もの間、現場を支え続けることができる。
`StartUndoMark` と `EndUndoMark`。たった2行のメソッド呼び出しだが、ここにエンジニアとしての誇りと、設計の美学が凝縮されている。
次にマクロを組むときは、必ず「Ctrl+Z一回で消せるか?」を自問自答した上で、コードをビルドしたまえ。
