SolidWorks VBAを掌握する極限の知見
【上級プロ】SldWorksEventsを利用したアセンブリ内のコンポーネント追加・削除イベントのリアルタイム非同期フック処理
SolidWorks VBAの領域において、マクロの自動化は「ボタンを押して図面や部品を一括生成する」という静的な一方向の処理から、「CADの挙動と外部システムが常時同期する」という動的なイベント駆動型アーキテクチャへシフトしなければならないフェーズにきている。
アセンブリ(Assembly)の構築において、ユーザーが手動あるいはプログラムでコンポーネント(Component)を追加・削除した瞬間を捕捉し、ERPやPLM、あるいは外部データベースの在庫数やツリー構造の整合性をリアルタイムに保つ。この極限の要件を満たすためには、単なるVBAの標準機能の枠を超え、SolidWorksのイベントAPIのライフサイクル、COMオブジェクトのメモリ管理、そしてVBAのシングルスレッド制約を突破する知見が不可欠だ。
本稿では、`SldWorksEvents`を用いたコンポーネント増減のリアルタイムフックと、その実務的な実装コードを詳解する。
—
1. SolidWorksイベントハンドリングの構造的罠と常識
VBAでSolidWorksのイベントを捕捉する際、最大の障壁となるのは「どのオブジェクトがイベントを配信しているか」の正確な把握である。
アセンブリレベルでのコンポーネント追加・削除を監視する場合、`SldWorks.SldWorks`(グローバル)のイベントではなく、現在アクティブなドキュメント、すなわち `SldWorks.ModelDoc2` から派生する `AssemblyDoc`、さらには専用のイベントモジュール群を正確に結びつける必要がある。
シニアエンジニアが知るべき致命的な罠
- イベントシンクの喪失: VBAのクラスモジュール内でイベントをフックする際、参照が適切に保持されていないと、ガベージコレクタ(GC)によってインスタンスが即座に消滅し、イベントが全く発火しなくなる。
- COMオブジェクトのメモリリーク: イベントハンドラ内で生成・取得したComponent2やModelDoc2の参照(`Set`)を解放し忘れると、SolidWorksのプロセス内にゾンビオブジェクトが残留し、アセンブリの閉じ込み時にクラッシュを引き起こす。
—
2. アーキテクチャ設計:イベントフックの実装パターン
これを実現するためには、以下の3つの要素を完璧に組み合わせる必要がある。
1. 主幹クラス (`clsAssemblyEventListener`): イベントを実際に受信するクラスモジュール。
2. 監視制御用標準モジュール (`modMain`): マクロの起動、クラスのインスタンス保持、SolidWorksへのアタッチ。
3. 外部連携・非同期処理モジュール: データベースや外部JSONへの書き出し処理(VBAのブロックを防ぐための工夫)。
—
3. 実装コード:極限まで最適化されたVBAコード
以下のコードは、実務の現場でそのまま稼働できる堅牢性を持たせた実装である。
① クラスモジュール: `clsAssemblyEventListener`
Option Explicit
‘ WithEventsキーワードを用いてAssemblyDocのイベントをフック
Public WithEvents AsmDoc As SldWorks.AssemblyDoc
Private swApp As SldWorks.SldWorks
‘ 初期化時にアプリケーションとドキュメントをバインド
Public Sub Initialize(ByVal app As SldWorks.SldWorks, ByVal doc As SldWorks.ModelDoc2)
Set swApp = app
If Not doc Is Nothing AND doc.GetType = swDocASSEMBLY Then
Set AsmDoc = doc
End If
End Sub
‘ ————————————————–
‘ イベント①: コンポーネント追加時
‘ ————————————————–
Private Function AsmDoc_ComponentAdditionNotify(ByVal ComponentPathName As String) As Long
On Error GoTo ErrorHandler
‘ 外部データベースやERPへの非同期連携を想定した処理
Debug.Print “[EVENT] コンポーネント追加検知: ” & ComponentPathName
‘ ここに在庫数減算APIの呼び出しや、ツリー構造の検証ロジックを記述
Call SyncDatabase(“ADD”, ComponentPathName)
AsmDoc_ComponentAdditionNotify = 0 ‘ 正常終了時は0を返す
Exit Function
ErrorHandler:
Debug.Print “[ERROR] ComponentAdditionNotify: ” & Err.Description
AsmDoc_ComponentAdditionNotify = 0
End Function
‘ ————————————————–
‘ イベント②: コンポーネント削除時
‘ ————————————————–
Private Function AsmDoc_ComponentDeletionNotify(ByVal ComponentPathName As String) As Long
On Error GoTo ErrorHandler
Debug.Print “[EVENT] コンポーネント削除検知: ” & ComponentPathName
‘ 削除時の在庫復元やツリー整合性更新処理
Call SyncDatabase(“DELETE”, ComponentPathName)
AsmDoc_ComponentDeletionNotify = 0
Exit Function
ErrorHandler:
Debug.Print “[ERROR] ComponentDeletionNotify: ” & Err.Description
AsmDoc_ComponentDeletionNotify = 0
End Function
‘ ダミーの外部連携プロシージャ
Private Sub SyncDatabase(ByVal action As String, ByVal filePath As String)
‘ 実務ではここでWeb API (HTTPrequests) や外部DBへの書き込みを行う
‘ ※VBAで重い処理を同期実行するとSolidWorksのUIがフリーズするため、
WScriptのShellや外部COMコンポーネント経由での非同期化を推奨
End Sub
Private Class_Terminate()
‘ 参照の明示的解放(メモリリーク防止の極意)
Set AsmDoc = Nothing
Set swApp = Nothing
End Class
② 標準モジュール: `modMain`
Option Explicit
‘ クラスのインスタンスをスコープアウトさせないためにグローバル(またはプロジェクト全体)で保持
Public GlobalEventListener As clsAssemblyEventListener
Public swAppObj As SldWorks.SldWorks
Sub StartAssemblyMonitoring()
Set swAppObj = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Set swModel = swAppObj.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical
Exit Sub
End If
If swModel.GetType() <> swDocASSEMBLY Then
MsgBox “このマクロはアセンブリ文書でのみ実行可能です。”, vbExclamation
Exit Sub
End If
‘ イベントリスナーのインスタンス生成とアタッチ
Set GlobalEventListener = New clsAssemblyEventListener
GlobalEventListener.Initialize swAppObj, swModel
MsgBox “SolidWorks アセンブリ監視システムが稼働を開始しました。”, vbInformation
End Sub
Sub StopAssemblyMonitoring()
‘ 明示的なインスタンス破棄によりイベントをデタッチ
Set GlobalEventListener = Nothing
Set swAppObj = Nothing
MsgBox “アセンブリ監視を停止しました。”, vbInformation
End Sub
—
4. チーフアーキテクトが教えるパフォーマンスと保守の極意
1. メモリ管理と「ゾンビプロセス」の根絶
VBAで`WithEvents`を使用する場合、マクロの実行が終了してもクラスインスタンスがメモリ上に残る設計にする必要がある。上記のコードでは `GlobalEventListener` を標準モジュールのグローバル変数として保持することでこれを実現している。
しかし、アセンブリを閉じる際や別の文書を開いた際、古いドキュメントポインタを保持し続けるとメモリリークの温床となる。厳密なシステム構築を行う場合は、`SldWorks.SldWorks_FileCloseNotify2` や `DocumentLoadNotify2` などのアプリケーションレベルのイベントも併用し、ドキュメントのライフサイクルに合わせてリスナーを動的に再生成・破棄するアーキテクチャが求められる。
2. VBAのシングルスレッド制約と非同期の壁
VBAは本質的にシングルスレッドであり、イベントハンドラ(`ComponentAdditionNotify` 等)の内部で重たいデータベース通信やファイルI/Oを実行すると、SolidWorksのビューポートレンダリングがブロックされ、ユーザーの操作性が著しく低下する(「フリーズしている」と誤認される)。
極限のパフォーマンスを求める現場では、VBA内で直接重い処理を行わず、ファイル書き出しやWindowsレジストリ、あるいはローカルのNamed Pipe(名前付きパイプ)を介して、常駐型の外部プロセス(C#製コンソールアプリやPythonスクリプト)に処理を押し付ける「オフロード設計」を採用するのが、シニアエンジニアの常道である。
総括
SolidWorks VBAは単なる「定型作業の代替ツール」ではない。APIの深層を理解し、イベントのライフサイクルとメモリ構造を掌中に収めれば、CADの内部挙動と企業の基幹システムをリアルタイムに結合する強力な基盤へと変貌する。
本稿で示した設計思想とコードは、その極限領域へ到達するための確かな礎となるはずだ。
