【テクニカル・上級編】【上級プロフェッショナル向け】図面保存イベント「BeginSave」をフックした「社内規定チェック」と保存の強制キャンセル – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

秩序の強制:AutoCAD `BeginSave` イベントを制御する、エンジニアリング・ガバナンスの極致

エンジニアリングの現場において、「規程を守る」という精神論は無価値である。
数千枚の図面が流通する大規模プロジェクトにおいて、レイヤー名の命名規則、テキストスタイルの不一致、あるいは非標準的なブロック定義といった「微細なノイズ」は、後続のデータ連携や自動化処理において致命的な計算エラー、あるいはシステムダウンを引き起こす爆弾となる。

我々アーキテクトの使命は、設計者の善意に頼ることではない。「規程に準拠しない図面は、物理的に保存できない」という数学的な制約を、CADの実行プロセスそのものに組み込むことにある。

本稿では、AutoCAD VBAを用い、保存プロセスが開始される直前の `BeginSave` イベントをフックし、社内標準への準拠を強制的に検査、違反時に保存を中断(Abort)させる、極限のガバナンス・アドインの実装手法を詳述する。

1. 介入のアーキテクチャ:なぜ `BeginSave` なのか

AutoCADのイベントモデルにおいて、保存プロセスは複数のフェーズを持つ。`Save` コマンドが発行されてから、実際にファイルシステムへバイナリが書き込まれるまでには、メモリ上のデータベース(Database)と、ディスク上のファイル(DWG)の間で複雑なトランザクションが発生する。

我々がターゲットにするのは `DocumentEvents` の `BeginSave` である。このイベントは、データベースの変更が確定し、ファイルへの書き出しが開始される「直前」に発火する。このタイミングでの介入は、以下の特性を持つ。

1. 非破壊的検査: データベースの状態を読み取り専用(Read-only)に近い状態でスキャンできる。
2. 完全な制御権: 検査結果に基づき、実行中の `Save` プロセスに対して例外(Exception)を投げ、コマンドチェーンを強制的に遮断できる。

2. 実装:規程遵守エンジン(Compliance Engine)

単なる `If…Then` の羅列は、保守性を著しく低下させる。プロフェッショナルな実装では、検査ロジックを「ポリシー(規程)」と「エンジン(実行器)」に分離すべきである。

以下に、Windows APIを活用してユーザーへ警告を通知し、保存を阻止する堅牢なクラスモジュールの構造を示す。

クラスモジュール: `clsComplianceGuard`

'---------------------------------------------------------------------------------------
' Class: clsComplianceGuard
' Purpose: AutoCADの保存イベントを監視し、社内規程への準拠を強制する
'---------------------------------------------------------------------------------------
Option Explicit

' Windows API: プロフェッショナルなUIを提供するためのメッセージボックス
Private Declare PtrSafe Function MessageBox Lib "user32" Alias "MessageBoxA" ( _
    ByVal hwnd As LongPtr, _
    ByVal lpText As String, _
    ByVal lpCaption As String, _
    ByVal uType As Long) As Long

Private Const MB_ICONERROR As Long = &H10
Private Const MB_OK As Long = &H0

Private WithEvents objApp As AcadApplication
Private objDoc As AcadDocument

' 初期化メソッド
Public Sub Initialize(ByRef app As AcadApplication)
    Set objApp = app
End Key
End Sub

' ドキュメントイベントの捕捉
Private Sub objApp_DocumentEvents(ByVal Doc As AcadDocument)
    ' 新しく開かれた、あるいは作成されたドキュメントに対してイベントをバインド
    ' 注: 実際の実装では、Applicationレベルのイベントから各Documentへのハンドリングが必要
    Set objDoc = Doc
End Sub

' 【核心部】BeginSaveイベントのフック
Private Sub objDoc_BeginSave(ByVal SaveMode As Integer)
    Dim violationFound As Boolean
    Dim errorMsg As String
    
    violationFound = False
    errorMsg = ""

    ' 1. レイヤー名の検査 (O(n) 複写の回避)
    If Not CheckLayerCompliance(errorMsg) Then
        violationFound = True
    End If

    ' 2. テキストスタイルの検査
    If Not CheckTextStyleCompliance(errorMsg) Then
        violationFound = True
    End If

    ' 違反がある場合、保存を強制中断する
    If violationFound Then
        ' Windows APIを使用して、CADの標準ダイアログとは一線を画す「システム警告」を表示
        MessageBox 0, "【規程違反】以下の項目を修正してください:" & vbCrLf & errorMsg, _
                   "Compliance Violation - Save Aborted", MB_ICONERROR
        
        ' 重要: VBAにおいて保存プロセスを中断する唯一の確実な方法は、
        ' 実行コンテキストに対してランタイムエラーを発生させることである。
        ' これにより、AutoCADの保存コマンドチェーンが破壊(Abort)される。
        Err.Raise vbObjectError + 513, "clsComplianceGuard", "Standard Compliance Check Failed."
    End If
End Sub

' --- 内部検査ロジック ---

Private Function CheckLayerCompliance(ByRef msg As String) As Boolean
    Dim lay As AcadLayer
    Dim standardPrefix As String
    standardPrefix = "STD_" ' 社内標準レイヤーの接頭辞
    
    CheckLayerCompliance = True
    
    ' レイヤーコレクションの反復処理
    ' パフォーマンス最適化のため、可能な限りオブジェクトへのアクセスを最小限にする
    For Each lay In objDoc.Layers
        ' ゼロレイヤー(0)やDefpointsは規程の対象外とする等の例外処理
        If lay.Name <> "0" And lay.Name <> "Defpoints" Then
            If Left(lay.Name, Len(standardPrefix)) <> standardPrefix Then
                msg = msg & "- 非標準レイヤー名: " & lay.Name & vbCrLf
                CheckLayerCompliance = False
            End If
        End If
    Next lay
End Function

Private Function CheckTextStyleCompliance(ByRef msg As String) As Boolean
    Dim sty As AcadTextStyle
    Dim allowedStyles As Object ' Dictionaryを使用し、O(1)での検索を実現
    Set allowedStyles = CreateObject("Scripting.Dictionary")
    
    ' 許可されたスタイルリストの定義
    allowedStyles.Add "Standard", True
    allowedStyles.Add "CAD_TEXT_MAIN", True
    
    CheckTextStyleCompliance = True
    
    For Each sty In objDoc.TextStyles
        If Not allowedStyles.Exists(sty.Name) Then
            msg = msg & "- 非標準スタイル: " & sty.Name & vbCrLf
            CheckTextStyleCompliance = False
        End If
    Next sty
End Function

3. 極限の知見:メモリ管理とパフォーマンスの重み

シニアエンジニアがこのコードを実装する際、単に「動く」こと以上の配慮をしなければならない。

オブジェクトのライフサイクルとメモリリーク

AutoCAD VBAはCOM(Component Object Model)のラッパーである。`For Each` ループ内で大量の `AcadLayer` オブジェクトを操作する場合、これらのオブジェクトはメモリ上に残り続ける可能性がある。
大規模な図面(数万のレイヤーやエンティティを持つもの)では、検査が終わるたびに明示的に `Set obj = Nothing` を行うか、あるいは検査ロジック内で参照を最小限に抑える設計が不可欠である。

計算量の制御(Complexity Control)

本実装では、テキストスタイルの照合に `Scripting.Dictionary` を使用している。これは、リストが増大した際に `For Each` で許可リストを回す($O(n)$)のではなく、ハッシュテーブルによる定数時間($O(1)$)での検索を実現するためである。
「規程が増える」ことは、システム運用において避けられない。その際、検査時間が線形に増大しない設計にすることが、プロフェッショナルの証である。

エラーハンドリングの戦略

`Err.Raise` を用いて保存を中断する手法は、強力であるが諸刃の剣である。
不適切なエラーハンドリングは、ユーザーに「アドインがクラッシュした」という誤解を与え、CAD自体の不安定化を招く。本実装では、Windows APIの `MessageBox` を先行させることで、「エラーによって止まったのではなく、規程によって阻止された」という文脈をユーザーに明確に提示している。

4. 結論

`BeginSave` をフックした強制的なコンプライアンスチェックは、単なる自動化ではない。それは、エンタープライズにおける「品質の自動防衛ライン」の構築である。

レガシーなVBA環境であっても、Windows API、適切なデータ構造、そしてオブジェクトのライフサイクルに対する深い理解があれば、現代的なガバナンス・システムを構築することは十分に可能である。技術的な妥協は、将来のシステムコストの増大に直結することを忘れてはならない。

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