【実務・中級編】【上級プロ】SldWorksEventsを利用したアセンブリ内のコンポーネント追加・削除イベントのリアルタイム非同期フック処理 – SolidWorks VBA解析バイブル

スポンサーリンク

【SolidWorks VBA極限活用】SldWorksEventsによるアセンブリ変更のリアルタイム非同期フック

開発現場でよくある要求だ。「アセンブリに部品が追加・削除された瞬間を捉え、外部DBの在庫を自動増減させたい」「社内BOMシステムとリアルタイムに同期させたい」。

これを愚直に、タイマー処理(定期的なポーリング)や、ユーザーに保存ボタンを押させるアプローチで実装しているとしたら、今すぐその設計を捨ててほしい。それはバグの温床であり、エンジニアとしての怠慢だ。

SolidWorks VBAにおいて、真にエレガントで堅牢なシステムを構築するには、`SldWorksEvents`(正確にはアセンブリレベルのイベントハンドラ)を完全に掌握し、コンポーネントのライフサイクルをイベント駆動でフックしなければならない。

今回は、実務の現場でそのまま稼働できる、極限まで最適化されたプロダクションコードと、VBA特有の罠を回避するアーキテクチャを伝授する。

—

1. なぜ通常のVBAコードではアセンブリ変更を捉えきれないのか?

多くの初学者や中級プログラマが陥る罠が、`CommandIDs`の監視や、単発のドキュメントマクロ実行によるアプローチだ。これらは以下の致命的な欠点を抱えている。

1. 手動操作とAPI操作の乖離:
ユーザーがエクスプローラーからドラッグ&ドロップで部品を追加した場合、あるいはコンテキストメニューから削除した場合、標準のVBAマクロのトリガーは発火しない。
2. メモリリークとCOMオブジェクトの解放漏れ:
イベントハンドラをVBAで扱う場合、`WithEvents`キーワードを使用するが、クラスモジュールのライフサイクル管理を誤ると、SolidWorks本体がフリーズするか、最悪の場合クラッシュする。
3. リエントラント(再入可能性)の問題:
イベントハンドラ内でコンポーネントを追加・削除する処理を書くと、イベントが連鎖して無限ループ(スタックオーバーフロー)を引き起こす。

これらを完全に克服するためには、「どのドキュメントの、どのイベントスロットを、どのクラスインスタンスで受けるか」を厳密に制御した設計が必要となる。

—

2. アーキテクチャ設計:堅牢なイベントフックの全体像

SolidWorksでアセンブリのコンポーネント追加・削除をフックするには、以下の2つのクラス(またはモジュール)が必要だ。

1. `Class1` (イベント受信用クラスモジュール): `SldWorks.AssemblyDoc` のイベントを `WithEvents` で受ける。
2. 標準モジュール (`MainModule`): SolidWorksアプリケーションの起動、アセンブリオープン時のイベントフックのバインド、およびCOM参照の維持を行う。

VBAはVB.NETやC#と異なり、イベントリスナーのガベージコレクション挙動がシビアである。そのため、イベントをフックしているクラスのインスタンスが途中で破棄(スコープアウト)されないよう、グローバル(またはそれに準ずる生存期間)で保持し続けることが鉄則だ。

—

3. プロダクションコード実装

以下のコードを、そのままあなたのSolidWorks VBAプロジェクトに実装してほしい。

① イベント受信用クラスモジュール:`EventAssembly`

※クラスモジュールの名称を `EventAssembly` に設定してください。

Option Explicit

‘ SolidWorks アセンブリドキュメントとしてイベントを宣言
Public WithEvents AsmDoc As SldWorks.AssemblyDoc
Private m_App As SldWorks.SldWorks

‘ 初期化メソッド(アプリケーションとドキュメントを紐付け)
Public Sub Init(ByVal swAppObj As SldWorks.SldWorks, ByVal swAsmObj As SldWorks.AssemblyDoc)
Set m_App = swAppObj
Set AsmDoc = swAsmObj

‘ イベントの有効化を明確にするためのログ(イミディエイトウィンドウ)
Debug.Print “[INFO] Assembly Event Hook Initialized for: ” & AsmDoc.GetPathName
End Sub

‘ ==========================================
‘ イベントハンドラ群
‘ ==========================================

‘ コンポーネント追加時イベント
Private Function AsmDoc_ComponentAdd2(ByVal ComponentModelDoc As Object, ByVal CompPath As String) As Long
On Error GoTo ErrorHandler

Dim swCompDoc As SldWorks.ModelDoc2
Set swCompDoc = ComponentModelDoc

Dim compName As String
If Not swCompDoc Is Nothing Then
compName = swCompDoc.GetTitle
Else
compName = CompPath
End If

‘ — 実務ロジック:外部DB連携や在庫処理のフック —
Debug.Print “[EVENT] コンポーネントが追加されました: ” & compName

‘ 例: 非同期的な外部データベース更新処理をここに記述
Call SyncDatabase_Inventory(compName, “ADD”)

AsmDoc_ComponentAdd2 = 0
Exit Function

ErrorHandler:
Debug.Print “[ERROR] AsmDoc_ComponentAdd2: ” & Err.Description
AsmDoc_ComponentAdd2 = 0
End Function

‘ コンポーネント削除時イベント
Private Function AsmDoc_ComponentDeleting2(ByVal ComponentModelDoc As Object, ByVal CompPath As String) As Long
On Error GoTo ErrorHandler

Dim swCompDoc As SldWorks.ModelDoc2
Set swCompDoc = ComponentModelDoc

Dim compName As String
If Not swCompDoc Is Nothing Then
compName = swCompDoc.GetTitle
Else
compName = CompPath
End If

‘ — 実務ロジック:削除時の処理 —
Debug.Print “[EVENT] コンポーネントが削除されます: ” & compName

‘ 例: 外部DBの在庫数ロールバック処理
Call SyncDatabase_Inventory(compName, “DELETE”)

AsmDoc_ComponentDeleting2 = 0
Exit Function

ErrorHandler:
Debug.Print “[ERROR] AsmDoc_ComponentDeleting2: ” & Err.Description
AsmDoc_ComponentDeleting2 = 0
End Function

‘ 補助関数:外部データベース(またはAPI/JSON)との通信モック
Private Sub SyncDatabase_Inventory(ByVal partName As String, ByVal actionType As String)
‘ ※ここにHTTP通信やADODBを用いたDB更新処理を記述する
‘ VBAからWscript.ShellやMSXML2.ServerXMLHTTPを叩く実務的アプローチ
Debug.Print ” -> [DB SYNC] 部品: ” & partName & ” / アクション: ” & actionType & ” をデータベースに反映しました。”
End Sub

② 標準モジュール:`MainModule`

※標準モジュールを作成し、以下のコードを貼り付けてください。

Option Explicit

‘ クラスインスタンスを破棄させないためにモジュールレベルで保持
Private g_EventAsmHandler As EventAssembly
Private swApp As SldWorks.SldWorks

‘ エントリポイント:このマクロを実行して監視を開始する
Public Sub StartAssemblyEventMonitoring()
Set swApp = Application.SldWorks

Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc

‘ アクティブドキュメントが存在するか、かつアセンブリかどうかを検証
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “エラー”
Exit Sub
End If

If swModel.GetType() <> swDocASSEMBLY Then
MsgBox “アクティブなドキュメントはアセンブリではありません。”, vbExclamation, “警告”
Exit Sub
End If

‘ クラスのインスタンス化とイベントのバインド
Set g_EventAsmHandler = New EventAssembly
g_EventAsmHandler.Init swApp, swModel

MsgBox “アセンブリのリアルタイムイベント監視を開始しました。” & vbCrLf & _
“イミディエイトウィンドウでイベント出力を確認してください。”, vbInformation, “監視開始”
End Sub

‘ 監視を停止(インスタンス解放)する場合
Public Sub StopAssemblyEventMonitoring()
Set g_EventAsmHandler = Nothing
MsgBox “アセンブリのイベント監視を終了しました。”, vbInformation, “監視終了”
End Sub

—

4. 実務運用上のクリティカルな注意点

このアーキテクチャを現場に導入するにあたり、プロフェッショナルとして知っておくべき「地雷」がいくつかある。

1. メモリ管理(COMの参照解放)

VBAは自動ガベージコレクションを持つが、SolidWorksのCOMオブジェクト(特に`SldWorks.ModelDoc2`や`AssemblyDoc`)は参照カウントが非常にシビアだ。イベントハンドラ内で不必要にオブジェクトを変数に格納し続けたり、ドキュメントを閉じた後に参照を残すと、SolidWorks終了時に「致命的なエラー(クラッシュ)」を引き起こす原因になる。
ドキュメントが閉じられる際のイベント(`DocumentLoad`や`Destroy`系)も併せてフックし、インスタンスのクリーンアップを行う設計を必ず組み込んでほしい。

2. 再入可能性(Re-entrancy)への対策

イベントハンドラ(`AsmDoc_ComponentAdd2`など)の内部で、さらにアセンブリの構造を変更するAPI(例:`AddComponent5`など)を呼び出してはならない。イベントが無限連鎖し、VBAのコールスタックが溢れて強制終了する。
データベースの更新や外部ログの出力といった「受動的な非同期処理」に留め、アセンブリ構造の書き換えを行う場合は、フラグ変数(`IsProcessing`など)を設けてイベントの二重発火をガードする防衛的プログラミングが必須となる。

—

総括

今回紹介した `SldWorksEvents` を用いたアセンブリ監視は、単なる「VBAの小技」の枠を超えた、エンタープライズ統合の第一歩だ。

手動によるヒューマンエラーの排除、リアルタイムなBOM・在庫同期、そして何より「裏側で確実に仕事をしてくれるシステム」の構築は、現場のエンジニアリングコストを劇的に劇変させる。

リファレンスのコピペを卒業し、イベント駆動型アーキテクチャを手に入れたあなたなら、このコードをベースにして、さらなる高度な自動化システムへと昇華させることができるはずだ。現場の無駄を、その手で完全に自動化し尽くしてほしい。

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