AutoCAD VBAを掌握する極限の知見
【実務中級】AcadDocument.StartUndoMarkとEndUndoMarkの適切な配置:複雑な一括処理を「Ctrl+Z」一回で元に戻せるようにする親切設計
開発プロジェクトのリーダーである私たちが、社内の設計者に向けて業務効率化ツールを配布したとき、最も恐ろしいクレームは何か知っているか?
それはエラーによるクラッシュではない。「マクロを実行したら、図面中が改変されてしまい、元に戻すために『Ctrl+Z』を50回も連打させられた。ふざけるな」という現場からの怒りの声だ。
数千個のブロックを一括置換したり、レイヤを一斉に整理したりするマクロを組んだ際、Undo(元に戻す)の制御を怠ると、AutoCADの内部トランザクションスタックはバラバラの粒度で記録される。結果として、ユーザーは「1つの業務操作」を行ったはずが、CADの履歴上では「2,000回の個別操作」として扱われ、Ctrl+Z地獄に突き落とされることになる。
プロのエンジニアが作るツールと、素人のマクロの決定的な違いは、「ユーザーの操作体験(UX)に対する配慮」、特にUndoスタックの支配にある。
今回は、`AcadDocument.StartUndoMark` と `EndUndoMark` を完全に手なづけ、複雑な一括処理を美しく「Ctrl+Z一発」で全アボート・全ロールバック可能にするための極限の知見を伝授する。
—
なぜ「何もしない」コードはゴミなのか?(非効率な実装の弊害)
AutoCAD VBAで図面オブジェクトを走査し、色やレイヤを変更するコードを書いてみてほしい。デフォルトのままでマクロを実行すると、何が起きるか?
‘ 【反面教師となる愚かな実装】
Sub BadExample_UpdateEntities()
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
‘ 何らかの大量処理
ent.Color = acRed
Next ent
End Sub
このコードを実行すると、`ModelSpace` 内の全エンティティの数だけUndo情報がメモリ(およびトランザクションログ)に積み上げられる。これの何が問題か?
1. パフォーマンスの致命的な劣化: すべての変更が個別のUndoステップとして記録されるため、VBAの実行速度そのものが低下する。
2. メモリの無駄遣い: 巨大な図面であれば、Undoバッファが溢れ、最悪の場合メモリ不足でAutoCAD自体が沈黙する。
3. ユーザビリティの崩壊: 冒頭で述べた「Ctrl+Z連打地獄」の完成だ。
これを解決するのが、Undoグループ化(カスタム・トランザクション境界の設定)である。
—
StartUndoMark と EndUndoMark の正しい作法
AutoCADのオブジェクトモデルにおいて、Undoマークの制御は `AcadDocument`(または `AcadApplication`)のメソッドとして提供されている。
ThisDrawing.StartUndoMark [Smart]
‘ — ここに重い一括処理を書く —
ThisDrawing.EndUndoMark
ここで重要なのは、`StartUndoMark` にはオプション引数として `acUndoMarkDocActivation` などの定数(※バージョンや仕様により挙動が異なるが、基本はデフォルト引数なし、または `acStartUndoMark` 系のフラグ)を指定できる点だが、実務上は「ペアを絶対に崩さないこと」が鉄則となる。
鉄則1:エラー時でも必ず EndUndoMark を呼び出す(例外処理の義務)
VBAには .NET のような `Using` 構文がない。そのため、処理途中でエラー(ランタイムエラー)が発生した場合、`EndUndoMark` が呼ばれぬままプロシージャが抜けてしまうことがある。
これが発生すると、AutoCADのUndoスタックが破壊され、「その図面ウィンドウを開いている間、一切のUndoが効かなくなる、または致命的な予期せぬ挙動を引き起こす」という最悪のバグ(Undoズレ)に繋がる。
必ず `On Error GoTo` を用いて、エラーハンドリングの経路でも `EndUndoMark` が確実に実行される構造にしなければならない。
—
【プロダクションコード】実務で使える堅牢な一括処理テンプレート
以下のコードは、数千〜数万のオブジェクトを操作する実務ツールを想定した、極めて堅牢なテンプレートだ。
エラーハンドリング、画面描画の凍結(パフォーマンス最適化)、そしてUndoマークの厳格な管理をすべて実装している。
Option Explicit
”’
”’
Sub ProductionReady_BatchProcess()
Dim doc As AcadDocument
Set doc = ThisDrawing
Dim undoMarkStarted As Boolean
undoMarkStarted = False
‘ 1. パフォーマンス最適化と画面描画の抑制
‘ (画面のチラつきを防ぎ、処理速度を劇的に向上させる)
doc.Application.ScreenUpdating = False
On Error GoTo ErrorHandler
‘ ==========================================
‘ 2. Undoグループの開始(ここからが1つの操作)
‘ ==========================================
doc.StartUndoMark
undoMarkStarted = True
‘ — 【ここから実務の重い処理】 —
Dim ent As AcadEntity
Dim counter As Long
counter = 0
For Each ent In doc.ModelSpace
‘ 例:特定のレイヤにある文字を赤色にし、高さを変更する模擬処理
If ent.ObjectName = “AcDbText” Then
ent.Color = acRed
‘ ent.TextHeight = 5.0 ‘ 必要に応じて変更
counter = counter + 1
End If
Next ent
‘ — 【ここまで実務の重い処理】 —
‘ 正常終了時の処理
doc.EndUndoMark
undoMarkStarted = False
‘ 画面描画の復元
doc.Application.ScreenUpdating = True
MsgBox “処理が完了しました。” & vbCrLf & _
“対象オブジェクト数: ” & counter & “件” & vbCrLf & _
“※「Ctrl+Z」一回でこの処理をすべて取り消せます。”, vbInformation, “完了”
Exit Sub
ErrorHandler:
‘ 異常終了時のセーフティネット
‘ Undoマークが開始されたまま放置されるのを防ぐため、必ず閉じる
If undoMarkStarted Then
doc.EndUndoMark
End If
‘ 画面描画を確実に復元
doc.Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的なエラー”
End Sub
—
チーフアーキテクトからの実践的アドバイス:ネスト(入れ子)の罠
大規模なシステムを開発していると、「共通関数(モジュールA)の中でUndoマークを貼り、それを呼び出す上位のメイン処理(モジュールB)でもUndoマークを貼る」という構造になりがちだ。
AutoCADのUndoマークは、実はネスト(入れ子構造)をサポートしている。`StartUndoMark` を呼ぶたびに内部カウンターがインクリメントされ、同数の `EndUndoMark` が呼ばれるまでひとまとまりのブロックとして維持される仕様になっている。
しかし、このネストをVBAで安易に多用すると、「どの階層でエラーが起きてUndoが閉じられなくなったか」がデバッグ時に極めて追いにくくなる。
結論:
- Undoマークの責任範囲は、マクロの「エントリーポイント(最上位プロシージャ)」に限定せよ。
- 下位の共通関数(ヘルパー関数)の内部では `StartUndoMark` や `EndUndoMark` を呼ばず、純粋なデータ処理・オブジェクト操作に徹させること。
- 処理の境界線(トランザクションの境界)を明確にアーキテクチャ設計に組み込むことが、保守性の高いVBA開発の絶対条件である。
現場の設計者が気持ちよく働き、あなたの作ったツールを心から信頼して活用してくれるために。今日からすべてのマクロに `StartUndoMark` の美学を実装せよ。
