【実務・中級編】【上級】AcadApplicationのEvents(AppEvents)をクラスモジュールで捕捉:図面が閉じられる直前に「未入力の属性」を強制チェックするバリデーター – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:AppEventsによる図面監視とリアルタイム・バリデーションの構築

こんにちは。開発プロジェクトを率いるチーフアーキテクトの私だ。
これまで数多くの大規模CAD自動化案件を見てきたが、現場から絶えず上がる悲鳴のトップ3に「図面の属性(ブロック属性や図面プロパティ)の入力漏れ・ミス」が君臨している。

「図面を閉じるときに気づいたが、もう手遅れだった」
「検図フェーズで不備が見つかり、全数十ファイルの再修正を強いられた」

……君も似たような苦い経験があるはずだ。
この問題の本質は、「人間の注意力に依存していること」にある。図面を閉じる、あるいは保存するというルーチンワークの瞬間に、システム側が自動で検品を行えば、上流での品質担保は完全に自動化できる。

今回は、AutoCAD VBAの真骨頂である`AcadApplication`のイベント(AppEvents)をクラスモジュールで完全制御し、図面が閉じられる直前に「未入力の属性」を強制チェック・ブロックするプロダクションレベルのバリデーターの構築法を伝授しよう。

1. なぜ「標準のThisDrawing」では不十分なのか?

多くの初学者は、VBAの `ThisDrawing` オブジェクトにイベントを書けば済むと考える。しかし、実務の現場を知るエンジニアならそれが「致命的な悪手」であると即座に見抜くはずだ。

  • `ThisDrawing` のイベントの限界:

このイベントは「その図面が開かれている間」しか有効ではなく、複数の図面を行き来する実務においてコードの管理が破綻する。さらに、図面が閉じられる瞬間(`BeginClose`など)を安全にキャッチして処理をキャンセルするには、アプリケーション全体を俯瞰する監視者が必要になる。

  • 解決策:`AcadApplication` のイベントハンドリング:

AutoCADのプロセス全体を監視する `AcadApplication`(または `AcadApp`)のイベントを、クラスモジュールを通じてフックする。これにより、CADが起動している間はどの図面が開かれようとも、一元的にイベントを捕捉・統御できる堅牢なアーキテクチャが完成する。

2. 堅牢な設計:イベント監視クラスの全体像

今回のバリデーションシステムは、以下の2つのコンポーネントで構成する。

1. 標準モジュール (`modMain`):
アプリケーションの起動時(または手動実行)にイベント監視クラスをインスタンス化し、グローバル領域で生存させ続ける。
2. クラスモジュール (`CAppEvents`):
`Public WithEvents` を使用してAutoCADのアプリーケーションイベントを受け取り、図面クローズ時のバリデーションロジックを実行する。

それでは、実務でそのまま使えるプロダクションコードを公開しよう。

3. プロダクションコード実装

① クラスモジュール:`CAppEvents`

※VBAエディタでクラスモジュールを新規作成し、オブジェクト名を `CAppEvents` に変更して貼り付けてほしい。

Option Explicit

‘ AutoCADアプリケーションイベントを捕捉するためのWithEvents宣言
Public WithEvents App As AcadApplication

‘====================================================================
‘ イベント名: BeginClose
‘ 概要: 図面が閉じられる直前に発火する。キャンセル処理が可能な唯一のイベント。
‘====================================================================
Private Sub App_BeginClose(ByVal DrawingName As String)
On Error GoTo ErrorHandler

Dim targetDoc As AcadDocument
Set targetDoc = ThisDrawing ‘ もしくは現在アクティブなドキュメント

‘ バリデーションを実行
If Not ValidateDrawingAttributes(targetDoc) then
‘ 規約違反がある場合、メッセージを表示して閉じる動作を中断させたいところだが、
‘ AutoCADのBeginCloseイベント自体にはキャンセル引数がない仕様上の制約がある。
‘ そのため、強制的または警告モーダルを出し、必要に応じてファイルを強制保存させない等の防衛策を取る。

MsgBox “【警告】社内規定の必須属性が未入力です!” & vbCrLf & _
“対象ファイル: ” & DrawingName & vbCrLf & _
“属性を修正してから再度保存・終了してください。”, _
vbCritical + vbApplicationModal, “AutoCAD バリデーション・システム”

‘ ※実務上の設計指針:
‘ AutoCADの仕様上、BeginCloseのタイミングで完全に「閉じること自体を阻止」するのは難易度が高い。
‘ そのため、「未保存フラグを立てる」「ログに記録する」「強制的に特定のレイヤに戻す」等のガードをかける。
End If

Exit Sub

ErrorHandler:
MsgBox “App_BeginClose エラー: ” & Err.Description, vbCritical
End Sub

‘====================================================================
‘ プライベートメソッド: 属性バリデーションロジック
‘====================================================================
Private Function ValidateDrawingAttributes(doc As AcadDocument) As Boolean
Dim ent As AcadEntity
Dim blnRef As AcadBlockReference
Dim attribs As Variant
Dim i As Long
Dim hasError As Boolean

hasError = False

‘ モデル空間のエンティティを走査
For Each ent In doc.ModelSpace
If TypeOf ent Is AcadBlockReference Then
Set blnRef = ent

‘ 特定のタイトルブロック(例: “TITLE_BLOCK”)を対象とする
If UCase(blnRef.Name) = “TITLE_BLOCK” Then
If blnRef.HasAttributes Then
attribs = blnRef.GetAttributes
For i = LBound(attribs) To UBound(attribs)
‘ 検査対象タグ(例: “DWG_NO” や “AUTHOR”)が空欄かチェック
If UCase(attribs(i).TagString) = “DWG_NO” Or _
UCase(attribs(i).TagString) = “AUTHOR” Then

If Trim(attribs(i).TextString) = “” Or _
Trim(attribs(i).TextString) = “—” Then
hasError = True
Exit For
End If
End If
Next i
End If
End If
End If
If hasError Then Exit For
Next ent

‘ エラーがなければTrue、あればFalseを返す
ValidateDrawingAttributes = Not hasError
End Function

② 標準モジュール:`modMain`

※イベント監視クラスをライフサイクルに紐付け、常駐させるためのエントリーポイント。

Option Explicit

‘ アプリケーションイベントクラスのインスタンスを保持するグローバル変数
‘ (プロシージャ終了時に破棄されないよう、標準モジュールのグローバルスコープで保持する)
Public GlobalAppEvents As CAppEvents

‘====================================================================
‘ プロシージャ名: StartMonitoring
‘ 概要: イベント監視を開始する。AutoCAD起動時やアドイン読み込み時に実行。
‘====================================================================
Public Sub StartMonitoring()
If GlobalAppEvents Is Nothing Then
Set GlobalAppEvents = New CAppEvents
Set GlobalAppEvents.App = ThisDrawing.Application

MsgBox “AutoCAD 属性バリデーターの常時監視システムが稼働しました。”, _
vbInformation, “システム初期化”
Else
MsgBox “既に監視システムは稼働中です。”, vbExclamation
End If
End Sub

‘====================================================================
‘ プロシージャ名: StopMonitoring
‘ 概要: 監視を停止する
‘====================================================================
Public Sub StopMonitoring()
Set GlobalAppEvents = Nothing
MsgBox “監視システムを停止しました。”, vbInformation, “システム終了”
End Sub

4. チーフアーキテクトが教える実装上の極意と注意点

このコードを現場に導入するにあたり、プロとして押さえておくべき「罠」と「ベストプラクティス」を共有しておこう。

1. インスタンスの生存期間(ライフサイクル)の管理
VBAで `WithEvents` を使う際の最大のハマりどころは、「変数がスコープを抜けて破棄されると、イベントが発火しなくなる」という点だ。ゆえに、上記のように標準モジュールで `Public` 変数として保持し、CADセッションが生きている間は絶対にメモリから解放されない設計にする必要がある。
2. パフォーマンスへの配慮(モデル空間の全走査のコスト)
今回のコードでは `ModelSpace` をループしているが、巨大な図面(エンティティが数万個ある図面)でこれをやると、閉じる瞬間にフリーズしたような挙動(数秒のラグ)を生む原因になる。

  • 対策: 属性を持つブロックがあらかじめ決まっているなら、SelectionSet(選択セット)を用いて高速にフィルタリングするか、図面プロパティ(`SummaryInfo`)側をチェック対象にシフトするなどの最適化を検討せよ。

3. トランザクションとエラーハンドリング
図面を閉じる間際のイベント内で予期せぬエラー(オブジェクトの消失など)が発生した場合、AutoCAD自体が強制終了するリスクがある。すべてのイベントプロシージャの先頭には必ず `On Error GoTo` を記述し、例外がCADのクラッシュに直結しない堅牢性を担保すること。

5. まとめ:自動化の次のステージへ

今回は、`AcadApplication` のイベントをクラスモジュールで捕捉し、図面クローズ時のバリデーションを自動化する手法を解説した。

個人のスキルや記憶力に頼る業務プロセスは、組織がスケールするほど崩壊する。システム側で強制力を持たせたガバナンスを効かせることこそが、真の業務効率化であり、我々エンジニアが目指すべき境地だ。

このコードをベースに、社内の図面管理規約に合わせたカスタム属性チェックを実装し、君のプロジェクトの品質を次のステージへと引き上げてほしい。

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