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

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

図面品質を「コード」で統治せよ:BeginSaveフックによる社内標準強制実装の極意

設計業務の自動化において、多くのエンジニアが「便利なツール」を作ることに終始し、「堅牢なシステム」を作ることを忘れている。

例えば、「レイヤー名がルール通りかチェックするマクロ」だ。単にループを回して名前を比較するだけのコードは、ツールとしては及第点だが、業務プロセスを制御する「ゲートキーパー(門番)」としては失格である。なぜなら、ユーザーが「保存」を押した瞬間に、ルール違反を許して図面が書き出されてしまうからだ。

真のプロフェッショナルが目指すべきは、「ルールに適合しない限り、保存そのものを物理的に不可能にする」仕組みの構築である。

本記事では、AutoCAD VBAにおけるイベントハンドリングを駆使し、保存プロセスをインターセプトして社内規定チェックを強制する、プロダクションレベルの設計手法を伝授する。

1. 設計思想:Notification(通知)ではなく Interception(遮断)を狙え

AutoCAD VBAにおいて、`Application.BeginSave` イベントは、保存が「開始される直前」に発行される。しかし、ここで一つ重要な技術的制約を理解しておかなければならない。

VBAの `BeginSave` イベント自体には、戻り値によって保存をキャンセルする機能(Boolean型の戻り値など)は備わっていない。つまり、単にイベント内でエラーを出したとしても、保存処理そのものを止めることはできないのだ。

「保存を阻止する」という目的を達成するためには、以下の2つのアプローチのいずれかを選択する必要がある。

1. CommandWillStart イベントの活用(推奨): `SAVE` または `QSAVE` コマンドが実行される直前をフックし、チェックに失敗した場合は `Cancel` メソッドを叩いてコマンド自体を抹殺する。
2. 保存プロセスのラップ: ユーザーに直接 `SAVE` コマンドを使わせず、自作の「標準チェック付き保存マクロ」をボタンに割り当てて運用する(ただし、これはユーザーの規律に依存するため、堅牢性に欠ける)。

今回は、最も強力で、ユーザーの操作を問わず強制力を持つ 「`CommandWillStart` によるコマンド遮断」 の設計を解説する。

2. アーキテクチャ設計:関心の分離(Separation of Concerns)

場当たり的なコードは、将来のルール変更(例:新しいレイヤー規定の追加)のたびに、メインロジックを破壊する。
堅牢な設計には、以下の3つのレイヤーが必要だ。

1. Event Listener Layer: AutoCADのイベントを監視し、特定のコマンド(SAVE)を検知する。
2. Validation Engine Layer: 図面内のオブジェクト(レイヤー、スタイル等)を検査し、結果を返す。
3. Configuration Layer: チェックすべき基準(許可されたレイヤー名リストなど)を保持する。

3. 実装:プロダクション・クオリティのコード例

以下に、クラスモジュールを用いた、保守性の高い実装例を示す。

3.1. EventClass モジュール(イベント監視役)

まず、イベントを保持するためのクラスモジュールを作成する(名前を `clsAppEventHandler` とする)。

' [Class Module: clsAppEventHandler]
Option Explicit

' AutoCAD Applicationオブジェクトへの参照
Private WithEvents App As AcadApplication

' チェック結果を保持するフラグ
Public IsStandardCompliant As Boolean

' 初期化メソッド
Public Sub Initialize(ByRef targetApp As AcadApplication)
    Set App = targetApp
    IsStandardCompliant = True
End Sub

' コマンド開始直前に発生するイベント
Private Sub App_CommandWillStart(ByVal CommandName As String)
    ' SAVE または QSAVE コマンドをターゲットにする
    If UCase(CommandName) = "SAVE" Or UCase(CommandName) = "QSAVE" Then
        
        ' 1. 検証エンジンを呼び出し
        ' ※ここでは簡易的に、別モジュールの関数を呼び出す
        If Not Validator.CheckDrawingStandards() Then
            
            ' 2. 違反がある場合、ユーザーに警告
            MsgBox "【警告】図面が社内標準に準拠していません。" & vbCrLf & _
                   "修正するまで保存はできません。", vbCritical, "標準チェック違反"
            
            ' 3. コマンドを強制キャンセルする(これがゲートキーパーの役割)
            App.Cancel
            
        End If
    End If
End Sub

3.2. Validator モジュール(検証エンジン)

次に、実際のロジックを記述する標準モジュールを作成する。ここでは計算量を意識し、オブジェクトを一つずつ走査するのではなく、Symbol Tables(レイヤーやスタイルの管理テーブル) を直接参照する。

' [Standard Module: Validator]
Option Explicit

' 図面標準の検証を実行する
Public Function CheckDrawingStandards() As Boolean
    On Error GoTo ErrorHandler
    
    Dim acadDoc As AcadDocument
    Set acadDoc = ThisDrawing
    
    ' --- チェック項目1: レイヤー名の検証 ---
    ' 全てのエンティティをループするのは O(n) で低速。
    ' レイヤーテーブルを走査する方が圧倒的に高速。
    Dim lay As AcadLayer
    For Each lay In acadDoc.Layers
        If Not IsValidLayerName(lay.Name) Then
            Debug.Print "Invalid Layer: " & lay.Name
            CheckDrawingStandards = False
            Exit Function
        End If
    Next lay

    ' --- チェック項目2: 文字スタイルの検証 ---
    Dim txtStyle As AcadTextStyle
    For Each txtStyle In acadDoc.TextStyles
        If Not IsValidTextStyle(txtStyle.Name) Then
            Debug.Print "Invalid Style: " & txtStyle.Name
            CheckDrawingStandards = False
            Exit Function
        End If
    Next txtStyle

    ' 全てのチェックを通過
    CheckDrawingStandards = True
    Exit Function

ErrorHandler:
    MsgBox "検証中に予期せぬエラーが発生しました: " & Err.Description, vbExclamation
    CheckDrawingStandards = False
End Function

' レイヤー名が社内規定(例:接頭辞 'STD_')に合致するか
Private Function IsValidLayerName(ByVal layerName As String) As Boolean
    ' 本来はここで外部DBやテキストファイルからリストを読み込むのがプロの設計
    ' 今回は簡易的に接頭辞チェック
    If Left(layerName, 4) = "STD_" Or layerName = "0" Or layerName = "Defpoints" Then
        IsValidLayerName = True
    Else
        IsValidLayerName = False
    End If
End Function

' 文字スタイル名の検証
Private Function IsValidTextStyle(ByVal styleName As String) As Boolean
    ' 規定のスタイル名リスト
    Select Case UCase(styleName)
        Case "STANDARD", "SHX_TEXT", "TITLE_BLOCK"
            IsValidTextStyle = True
        Case Else
            IsValidTextStyle = False
    End Select
End Function

3.3. Main モジュール(起動用)

' [Standard Module: Main]
Option Explicit

Private EventHandler As clsAppEventHandler

' アドイン起動時に実行する
Public Sub AutoStart()
    ' 既存のインスタンスがあれば破棄して再生成(二重起動防止)
    Set EventHandler = New clsAppEventHandler
    EventHandler.Initialize AutoCAD.Application
    
    MsgBox "図面標準チェック・ゲートキーパーが起動しました。", vbInformation
End Sub

' 終了時にイベントを解除
Public Sub AutoStop()
    Set EventHandler = Nothing
    MsgBox "ゲートキーパーを停止しました。", vbInformation
End Sub

4. エンジニアが陥る「罠」と回避策

パフォーマンスの罠:$O(n)$ の恐怖

図面内のすべての「線」や「円」をループして、そのレイヤーをチェックするようなコードを書いてはいけない。数万個のオブジェクトがある図面では、保存のたびに数秒〜数十秒のフリーズが発生し、ユーザーから「重い」とクレームが入る。
「オブジェクト(Entity)」ではなく「定義(Symbol Table)」をループせよ。 これが鉄則だ。

データベース連携の重要性

社内規定は変わる。コード内にレイヤー名をハードコードするのは、保守性の観点から最悪の選択だ。
プロフェッショナルは、「チェックルールを外部ファイル(CSV, JSON, もしくは SQL Server)に切り出す」。これにより、コードを書き換えることなく、管理部門がルールを変更できる柔軟性を確保できる。

例外処理の徹底

`CommandWillStart` 内でランタイムエラーが発生すると、AutoCAD自体が不安定になるか、最悪の場合クラッシュする。
検証ロジック内には必ず `On Error GoTo` を配置し、エラーが発生した場合は「安全側に倒して(=保存を禁止して)」終了させる設計にせよ。

5. 結論

AutoCAD VBAを用いた業務自動化は、単なる「作業の短縮」ではない。それは「品質の強制」である。

`CommandWillStart` を利用したインターセプト手法は、ユーザーの利便性を損なうリスクもあるが、それ以上に「間違ったデータが社内システムに流出するコスト」を劇的に低減させる。

設計の際は、常に「このコードは変更に耐えられるか?」「このコードは図面の規模に依存しないか?」を自問自答してほしい。それが、伝説的なアーキテクトへの第一歩である。

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