図面品質を「コード」で統治せよ: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` を利用したインターセプト手法は、ユーザーの利便性を損なうリスクもあるが、それ以上に「間違ったデータが社内システムに流出するコスト」を劇的に低減させる。
設計の際は、常に「このコードは変更に耐えられるか?」「このコードは図面の規模に依存しないか?」を自問自答してほしい。それが、伝説的なアーキテクトへの第一歩である。
