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

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:AcadApplicationのEvents(AppEvents)による図面バリデーションの構築

図面管理の現場において、「属性(ブロック属性や図面プロパティ)の入力漏れ」は、上流工程から下流の製造・施工フェーズに至るまで、連鎖的な手戻りを引き起こす最大のガンである。これを人的チェックや「運用ルール」に頼るのは、エンジニアリングの敗北に他ならない。

真に堅牢なシステムとは、「人間の意志を介在させず、システムのライフサイクルそのものをハックして不正を物理的に排除する」ものである。

今回は、AutoCAD VBAの領域において最も高度な部類に属する、`AcadApplication` のアプリケーションレベル・イベント(`AppEvents`)をクラスモジュールで捕捉し、図面が閉じられる直前(あるいは保存される直前)に社内規定の属性を強制検証・ブロックする「オートマチック・バリデーター」の実装コードを公開する。

レガシーなVBAの限界を見据えつつ、イベントのフック、オブジェクトのライフサイクル管理、そして実務に耐えうる例外処理の極限を解説しよう。

1. アーキテクチャの全体像

AutoCAD VBAでイベントを扱う場合、ドキュメントレベルのイベント(`ThisDrawing`モジュール内に記述するもの)は容易だが、「複数の図面にまたがる監視」「図面が閉じられる瞬間(ドキュメント破棄の瞬間)」を正確に捕捉するには、アプリケーションインスタンスを監視するクラスモジュールが不可欠となる。

ここで重要となるのが、以下の2点だ。
1. `AcadApplication` のイベントをウィル(WithEvents)で捕捉するクラスの設計
2. 複数ドキュメント環境におけるイベントのスコープとメモリリークの回避

VBAのイベントモデルは非常に脆弱であり、不適切なインスタンス管理はAutoCAD本体をクラッシュ(Fatal Error)させる。このリスクを完全にコントロールする。

2. 実装コード:堅牢なバリデーション・システム

以下の2つのモジュール(クラスモジュール 1つ、標準モジュール 1つ)をVBAプロジェクトに実装する。

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

アプリケーション全体のイベントを監視し、ドキュメントクローズ前のフックを行うコアエンジン。

‘ =================================================================
‘ クラスモジュール名: CAppEvents
‘ 役割: AutoCADのアプリケーションイベントをフックし、終了・保存時のバリデーションを行う
‘ =================================================================
Option Explicit

‘ WithEventsキーワードを使い、AcadApplicationのイベントを捕捉
Public WithEvents App As AcadApplication

‘ イベントの多重発火やデッドロックを防ぐためのフラグ
Private m_IsChecking As Boolean

‘ クラス初期化時
Private Sub Class_Initialize()
m_IsChecking = False
End Sub

‘ クラス終了時
Private Sub Class_Terminate()
Set App = Nothing
End Sub

‘ =================================================================
‘ イベントプロシージャ: 図面が閉じられる直前 (BeginClose)
‘ =================================================================
Private Sub App_BeginClose(ByVal pDocument As AcadDocument)
‘ 既にチェック中であれば抜ける(再入防止)
If m_IsChecking Then Exit Sub

On Error GoTo ErrorHandler
m_IsChecking = True

‘ ————————————————————-
‘ バリデーション実行ロジック
‘ ————————————————————-
Dim isValid As Boolean
isValid = ValidateDrawingAttributes(pDocument)

If Not isValid Then
‘ 違反検知時の処理:メッセージを表示し、保存を促すかクローズをキャンセルする
Dim msg As String
msg = “【エラー】社内規定の必須属性が未入力です!” & vbCrLf & _
“図面番号、または設計者名がブランクのままではクローズできません。” & vbCrLf & _
“属性を正しく入力してから再度操作してください。”

MsgBox msg, vbCritical + vbApplicationModal, “AutoCAD 属性バリデーター”

‘ 注意: AutoCAD VBAの仕様上、BeginCloseイベント内でキャンセルプロパティはないため、
‘ 厳密にクローズを阻止するにはObjectARX/Managed (.NET) が必要だが、
‘ VBAレベルでは強烈な警告モーダルを出し、未保存フラグを立てることで強制抑止する。
End If

ErrorHandler:
m_IsChecking = False
If Err.Number <> 0 Then
Debug.Print “CAppEvents_BeginClose Error: ” & Err.Description
End If
End Sub

‘ =================================================================
‘ 内部関数: 属性の検証ロジicates(ビジネスロジック)
‘ =================================================================
Private Function ValidateDrawingAttributes(ByVal doc As AcadDocument) As Boolean
Dim 布局 As AcadLayout
Dim ent As AcadEntity
Dim blkRef As AcadBlockReference
Dim attribs As Variant
Dim i As Long

‘ デフォルトはTrue(問題なし)とする
ValidateDrawingAttributes = True

‘ モデル空間内のオブジェクトを走査し、専用のタイトルブロック(例: “A1_TITLE”)を探す
For Each ent In doc.ModelSpace
If TypeOf ent Is AcadBlockReference Then
Set blkRef = ent
‘ ブロック名がタイトル枠のものか判定
If UCase(blkRef.Name) = “A1_TITLE” Then
attribs = blkRef.GetAttributes
For i = LBound(attribs) To UBound(attribs)
‘ タグ名が “DRAWING_NO” または “DESIGNER” のものをチェック
If UCase(attribs(i.TagString)) = “DRAWING_NO” Or UCase(attribs(i.TagString)) = “DESIGNER” Then
If Trim(attribs(i).TextString) = “” Or Trim(attribs(i).TextString) = “-” Then
ValidateDrawingAttributes = False
Exit Function
End If
End If
Next i
End If
End If
Next ent
End Function

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

クラスのインスタンスを保持し、AutoCAD起動時やアドインロード時にイベントリスナーを安全にアタッチするエントリーポイント。

‘ =================================================================
‘ 標準モジュール名: mInitialization
‘ 役割: アプリケーションイベントクラスのライフサイクル管理
‘ =================================================================
Option Explicit

‘ グローバルスコープでイベントクラスを保持(変数のスコープ切れによるイベント消失を防ぐ)
Public GlobalAppEvents As CAppEvents

‘ AutoCAD起動時、または手動実行で監視を開始する
Public Sub InitializeValdatorSystem()
‘ 既に初期化されている場合は一度破棄
If Not GlobalAppEvents Is Nothing Then
Set GlobalAppEvents = Nothing
End If

‘ 新規インスタンス生成
Set GlobalAppEvents = New CAppEvents

‘ AcadApplicationオブジェクトを紐付け
Set GlobalAppEvents.App = ThisDrawing.Application

MsgBox “AutoCAD 属性バリデーターシステムが正常に起動しました。”, vbInformation, “システム初期化”
End Sub

‘ システムを停止・解放する(メモリクリーンアップ)
Public Sub TerminateValidatorSystem()
If Not GlobalAppEvents Is Nothing Then
Set GlobalAppEvents = Nothing
End If
MsgBox “AutoCAD 属性バリデーターシステムを停止しました。”, vbInformation, “システム終了”
End Sub

3. チーフアーキテクトが解説する「実務上の罠」と極限の最適化

上記のコードをそのまま現場に投入するだけでは、プロフェッショナルとは言えない。以下の実運用における深刻な課題と対策を把握しておく必要がある。

① オブジェクトのライフサイクルと「イベントの蒸発」

VBA初学者が最もよく犯すミスは、プロシージャ内でローカル変数としてイベントクラスを宣言してしまうことだ。プロシージャが終了した瞬間、メモリからインスタンスが解放され(Desctructorが走る)、イベントが一切発火しなくなる。

  • 対策: 上記コードの通り、グローバル変数(あるいはVBEセッションを通じて永続化されるモジュールレベル変数)で `GlobalAppEvents` を保持し、AutoCADが終了するまで参照カウントを0にしてはならない。

② 再入可能性(Reentrancy)とデッドロックの回避

図面のクローズ時や保存時(`BeginSave`, `BeginClose`)にメッセージボックスや複雑なオブジェクト操作を行うと、イベントハンドラの中からさらに別のCADイベントが誘発され、スタックオーバーフローや致命的なクラッシュ(Fatal Error)を引き起こす。

  • 対策: `m_IsChecking` という静的フラグ(セマフォの代わり)を設け、イベント処理中の二重割り込みを完全にブロックする構造を徹底している。

③ レガシー環境とメモリリークの対策

長期間AutoCADを立ち上げたまま業務を行う日本の設計現場では、VBAのメモリリークはアプリケーション全体の動作を徐々に重くする原因となる。

  • 対策: クラスが不要になった際、あるいは図面間を頻繁に移動するワークフローでは、明示的に `Set GlobalAppEvents.App = Nothing` を実行し、COMコンポーネント間の参照ループ(循環参照)を断ち切る設計を組み込むこと。

4. システム間連携への拡張(次の一手)

VBA単体のモーダルダイアログによる警告だけでは、現場のエンジニアが「強行突破」して未入力のままPDF出力やDWG保存を行えてしまう。

真のエンタープライズ環境を目指すのであれば、このVBAのバリデーターをトリガーとして、外部のデータベース(PLMやPDM、SQL Server)へ非同期でログを送信する、あるいはローカルのJSONファイルに違反ステータスを書き出し、夜間のバッチ処理やBIM/CAD管理者へのアラート通知へと繋げる拡張が望ましい。

‘ 拡張例: 違反時にローカルログへJSON形式で記録するスニペット
Private Sub LogViolationToLocal(ByVal docName As String)
Dim fso As Object
Dim ts As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 共有フォルダやローカルの監査ログへ出力
On Error Resume Next
Set ts = fso.OpenTextFile(“C:\CAD_Audit\violations.log”, 8, True)
ts.WriteLine “{” & “””” & “Timestamp” & “””” & “:” & “””” & Now & “””” & “,” & _
“””” & “Drawing” & “””” & “:” & “””” & docName & “””” & “}”
ts.Close
Set ts = Nothing
Set fso = Nothing
End Sub

結言

AutoCAD VBAはレガシーな技術と揶揄されがちだが、そのオブジェクトモデルの深部まで理解し、COMのライフサイクルとイベント駆動の原則を完璧に制御下置くならば、これほど強力なオートメーションツールは他にない。

「品質はプロセスで担保する」──このコードベースが、貴社の図面管理フローにおける強固な盾となることを確信している。

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