イベント駆動型アーキテクチャの極致:SolidWorksイベントによるプロアクティブな図面制御
マクロを「ボタンを押した時に動く便利ツール」と考えているうちは、まだエンジニアの端くれに過ぎない。真の自動化エンジニア、あるいはシステムアーキテクトを目指すならば、ソフトウェアを「ユーザーの操作を待つ受動的な存在」から「システムの状態変化を検知して自律的に動く能動的な存在」へと昇華させなければならない。
本稿では、SolidWorks VBAにおけるイベントハンドリング、特に`SldWorks_FileSaveNotify`等を利用した、図面管理プロセスの高度な自動化手法について詳説する。単なるコードの書き方ではない。SolidWorksという巨大なモノリス(単一プロセス)の中で、いかにしてシステムの安定性を損なわず、外部システムとの同期を実現するか。その設計思想を伝授する。
1. 限界への挑戦:なぜ「マクロ」では足りないのか
一般的な図面処理の自動化は、ユーザーがボタンをクリックし、VBAが起動し、処理が終わるまでユーザーが待機するという「同期実行」のモデルだ。しかし、大規模な設計現場において、これは致命的な欠陥となる。
- ヒューマンエラーの温床: 保存忘れ、命名規則違反、バックアップ漏れ。これらはすべて「ユーザーの記憶」に依存している。
- コンテキストスイッチの損失: 図面を保存した直後、設計者は次の設計に移りたい。しかし、重い自動処理が走っていれば、その思考のフローは断絶される。
- データの不整合: 外部PDM(Product Data Management)への登録や、サーバーへのバックアップが「手動」である限り、設計データと管理データの乖離は避けられない。
これらを解決するのが、イベント駆動型(Event-Driven)設計である。SolidWorksが「ファイルが保存された」というイベントを発行した瞬間に、背後でシステムがそれをキャッチし、処理を開始する。ユーザーの操作を妨げず、かつ確実に業務フローを完遂させる。これが、我々が目指すべき領域だ。
2. イベントフックの設計思想:非同期処理の鉄則
SolidWorksのイベントハンドラ(`SldWorksEvents`)を実装する際、最も初心者が陥りやすい、そして最も破壊的なミスは、「イベントハンドラ内で重い処理を行うこと」である。
SolidWorksは基本的にシングルスレッドに近い挙動を示す。VBAのイベントハンドラ内で、ネットワーク越しにファイルをコピーしたり、データベースにSQLを発行したり、外部APIを叩いたりすれば、その処理が終わるまでSolidWorksのUIは完全にフリーズする。これはユーザー体験を著しく損なうだけでなく、最悪の場合、SolidWorks自体のクラッシュを招く。
黄金律:Dispatcher Pattern(ディスパッチャ・パターン)
プロフェッショナルな設計では、イベントハンドラには「ディスパッチャ(振り分け役)」としての役割のみを担わせる。
1. Event Capture: VBAがイベントを検知する(極めて軽量)。
2. Dispatch: 検知した情報を引数として、外部プロセス(PowerShell, Python, もしくは専用の.NET Service)を非同期で起動する。
3. Execution: 実作業は、SolidWorksのプロセスとは独立した「別プロセス」で実行する。
これにより、SolidWorksはイベント検知直後に制御をユーザーに戻し、ユーザーはフリーズを感じることなく作業を継続できる。
3. 実装:堅牢なイベントリスナーの構築
以下に、クラスモジュールを用いたイベントハンドリングの実装例を示す。ここでは、`SldWorksEvents` インターフェースを実装し、ファイルの保存をトリガーに外部プロセスをキックする構造を記述する。
Class Module: `clsEventWatcher`
このクラスは、SolidWorksのイベントを監視し、特定の条件(図面ファイルの保存など)を満たした際に外部プロセスを呼び出す役割を持つ。
' Class Module: clsEventWatcher
Option Explicit
' SolidWorksイベントインターフェースを保持するための変数
Private WithEvents swEvents As SldWorksEvents
Private swApp As SldWorks.SldWorks
' 外部プロセス起動用の定数(例:PowerShell)
Private Const EXTERNAL_EXECUTABLE As String = "powershell.exe"
' 初期化メソッド
Public Sub Initialize(ByRef app As SldWorks.SldWorks)
Set swApp = app
' SolidWorksEventsオブジェクトの生成
' 注意: 環境によりCreateObjectの挙動が異なる場合があるため、
' 本来はアドインとして実装するのが理想的だが、VBAでは以下のように扱う
On Error Resume Next
Set swEvents = swApp.GetEventData ' 実際にはアドイン経由での取得が一般的
If swEvents Is Nothing Then
' 擬似的な実装例:実際にはAdd-in経由でイベントシンクを登録する必要がある
MsgBox "Event Sink initialization failed. Ensure Add-in is registered.", vbCritical
End If
On Error GoTo 0
End Sub
' ファイル保存イベントのハンドラ
' SldWorks_FileSaveNotify が発生した際に自動的に呼ばれる
Private Sub swEvents_FileSaveNotify(ByVal FileName As String)
On Error GoTo ErrorHandler
' 1. フィルタリング:図面ファイル(.drw)のみを対象とする
If LCase(Right(FileName, 5)) <> ".drw" Then Exit Sub
' 2. 非同期処理のキック(Dispatcherとしての役割)
' ここで重い処理(DB登録など)を直接書いてはいけない。
' Windows Shellを使用して、別プロセスとして外部スクリプトを呼び出す。
Call DispatchToExternalProcess(FileName)
Exit Sub
ErrorHandler:
' イベント内でのエラーは、SolidWorks全体の安定性に直結するため、
' ログ出力のみに留め、決してユーザーにダイアログを出してはいけない。
Debug.Print "Event Error: " & Err.Description & " [File: " & FileName & "]"
End Sub
' 外部プロセスへのディスパッチ処理
Private Sub DispatchToExternalProcess(ByVal targetFile As String)
Dim shell As Object
Dim command As String
' PowerShellスクリプトに引数としてファイルパスを渡す
' スクリプト側でバックアップやPDM連携を完結させる
command = String(1, Chr(34)) & "C:\Scripts\DrawingSync.ps1" & String(1, Chr(34)) & _
" -FilePath " & String(1, Chr(34)) & targetFile & String(1, Chr(34))
Set shell = CreateObject("WScript.Shell")
' 第2引数に 0 を指定することで、ウィンドウを非表示で実行(バックグラウンド実行)
' 第3引数に False を指定することで、プロセスの終了を待たずに制御を戻す(非同期)
shell.Run command, 0, False
Set shell = Nothing
End Sub
' オブジェクトの明示的な解放(メモリリーク防止)
Public Sub Terminate()
Set swEvents = Nothing
Set swApp = Nothing
End Sub
Standard Module: `modMain`
' Standard Module: modMain
Option Explicit
Private Watcher As clsEventWatcher
' 自動実行(SolidWorks起動時やマクロ開始時)
Sub StartAutomationSystem()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
' クラスのインスタンス化
Set Watcher = New clsEventWatcher
Watcher.Initialize swApp
Debug.Print "Event-Driven System Started: Monitoring Drawing Files..."
End Sub
' システムの停止
Sub StopAutomationSystem()
If Not Watcher Is Nothing Then
Watcher.Terminate
Set Watcher = Nothing
Debug.Print "Event-Driven System Stopped."
End If
End Sub
4. 極限の知見:メモリ管理とレガシー環境の保守
このレベルのシステムを運用する場合、以下の3点については「常識」として叩き込んでおく必要がある。
① オブジェクトのライフサイクル管理
VBAにおいて、`Set obj = Nothing` による明示的な解放は、単なる「行儀の良さ」ではない。SolidWorksのような巨大なアプリケーションにおいて、イベントリスナーが参照を保持し続けることは、プロセス内に「ゾンビ・オブジェクト」を生み出すことを意味する。これは、SolidWorksの再起動時におけるメモリリークや、アドインのアンロード失敗の主因となる。必ず `Terminate` メソッドを設計し、参照を完全に断つこと。
② Windows APIによるプロセスの監視
もし、外部プロセス(PowerShell等)が異常終了した場合、VBA側からはそれを検知しにくい。より高度なシステムでは、`kernel32.dll` の `OpenProcess` や `WaitForSingleObject` を呼び出し、外部プロセスの生存確認を行うロジックを組み込む。ただし、これはVBAの範疇を超え、C++やVB.NETによる「監視エージェント」の領域となる。
③ レガシー環境への配慮
社内システムには、依然として古いWindows OSや、古いバージョンのSolidWorksが混在している。`WScript.Shell` の挙動や、PowerShellの実行ポリシー(ExecutionPolicy)の違いによって、自動化が突然機能しなくなるリスクがある。設計段階で「環境の差異」を抽象化し、スクリプトのパスや実行コマンドを外部の設定ファイル(INIやJSON)から読み込む設計にしておくべきだ。
結言
SolidWorks VBAによる自動化は、単なる「作業の代行」であってはならない。それは「設計プロセスのインフラ」であるべきだ。
イベントをフックし、非同期に処理を逃がし、SolidWorksのメインプロセスを聖域として守る。このアーキテクチャを理解し、実装できる者こそが、現場の混乱を鎮め、真に価値のある自動化システムを構築できるエンジニアである。
技術の真髄は、コードの短さではなく、その設計の堅牢さと、システム全体への影響力の制御にあることを忘れるな。
