SolidWorks VBAを掌握する極限の知見
【上級プロ】トランザクション管理によるアセンブリ一括構築のロールバック実装
SolidWorksマクロの自動化において、数千点に及ぶ部品の配置と合致(Mate)の定義は、しばしばシステムの限界を試す過酷な処理となる。
実務の現場において、処理の途中で特権例外や幾何拘束の矛盾(Redundant Mate、あるいはFailed Mate)が発生し、「中途半端に部品がばら撒かれたアセンブリファイル」がストレージに保存されてしまう惨劇を、幾度となく目撃してきたことだろう。
GUIの操作を模倣する `ModelDocExtension::StartCommand` や、場当たり的な `On Error Resume Next` に依存した設計は、プロフェッショナルなエンジニアリングの世界では悪手でしかない。
今回は、APIのライフサイクルを完全に制御し、トランザクションの概念をVBA空間に持ち込むことで、「失敗した瞬間にアセンブリを完全に初期状態へ巻き戻す(ロールバック)」ための極限のアーキテクチャを解説する。
—
1. なぜ `StartCommand` や場当たり的エラー処理では破綻するのか
多くの初学者は、エラーハンドリングといえば `On Error GoTo ErrorHandler` を書き、その中でファイルを閉じる処理を記述する。しかし、これには致命的な欠陥がある。
1. ドキュメントの「ダーティ(Dirty)」フラグ:
APIを通じてコンポーネントを挿入(`AddComponents3` 等)した瞬間、SolidWorksのドキュメントモデルは「変更された」とみなされ、メモリ上のセッションが汚染される。
2. トランザクション境界の欠如:
SolidWorks APIには、RDBのようなトランザクション・ログ(BEGIN TRANSACTION / ROLLBACK)が明示的な構文として用意されていない。そのため、メモリ上の変更履歴を自前で管理するか、セッション単位での破棄を強制する必要がある。
3. COMオブジェクトの参照リーク:
途中でエラーが発生した際、取得した `Component2` や `Mate2` などのCOMラッパーがメモリ上に残存し、ドキュメントの解放(`CloseDoc`)を妨げる原因になる。
これらを克服するためには、「一時的な非表示ドキュメント空間での構築」 または 「明示的なUndoスタックの制御」 を行う必要がある。実務上、最も堅牢で確実な手法は、「処理対象のアセンブリをメモリ上でクローン(または新規非表示)として構築し、すべての検証に成功した場合のみファイルとして確定・保存する」 トランザクション・パターンである。
—
2. アーキテクチャ設計:仮想トランザクション・コンテキスト
今回は、アセンブリ一括構築をひとつの「不可分なトランザクション」として扱い、例外発生時には一切の変更を破棄するVBAモジュール設計を提示する。
以下のコードは、単なるAPIのラッパーではなく、オブジェクトのライフサイクルとメモリ管理を極限まで意識した実用コードである。
Option Explicit
‘ ==============================================================================
‘ 伝説のチーフアーキテクトによるトランザクション制御アセンブリビルダー
‘ ==============================================================================
‘ Windows API: メモリ解放を確実に行うためのガベージコレクション促進
Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Public Sub ExecuteSafeAssemblyTransaction()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swAssm As SldWorks.AssemblyDoc
Dim transactionStarted As Boolean
Dim newDocPath As String
‘ 1. アプリケーションインスタンスの取得
Set swApp = Application.SldWorks
If swApp Is Nothing Then Exit Sub
‘ 2. 実行前の環境最適化(描画停止によるパフォーマンス向上と予期せぬUI介入の排除)
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swViewDisplayHideAllTypes, True
swApp.Visible = False ‘ 仮想バッチ処理としてバックグラウンド実行(オプション)
transactionStarted = False
On Error GoTo TransactionRollback
‘ ==============================================================================
‘ 【BEGIN TRANSACTION】
‘ 新規アセンブリを裏方(非表示)で起動し、安全なサンドボックスを作る
‘ ==============================================================================
Dim templatePath As String
templatePath = swApp.GetUserPreferenceStringValue(swUserPreferenceStringValue_e.swDefaultTemplateAssembly)
‘ ドキュメントをサイレントモード(UIを描画させない)で新規作成
Set swModel = swApp.NewDocument(templatePath, 0, 0, 0)
If swModel Is Nothing Then Err.Raise 9999, “Transaction”, “アセンブリテンプレートの読込に失敗しました。”
Set swAssm = swModel
transactionStarted = True
‘ 自動リビルドを一時停止(合致エラーの連鎖を防ぐ)
swModel.AutoRebuildChanges = False
‘ ——————————————————————————
‘ 【CORE PROCESS】 大量部品の配置と合致の定義(シミュレーション)
‘ ——————————————————————————
Dim i As Long
For i = 1 to 100
‘ ここに部品追加ロジックが入る
‘ 例: swAssm.AddComponent5(…)
‘ 仮想的なエラー発生シミュレーション(例として50回目で意図的に破綻させる場合)
‘ If i = 50 Then Err.Raise 1001, “CoreProcess”, “幾何拘束の解決に失敗しました。”
Next i
‘ ——————————————————————————
‘ 【COMMIT】 すべての処理が成功した場合のみ、ここで変更を確定させる
‘ ——————————————————————————
swModel.AutoRebuildChanges = True
swModel.EditRebuild3()
‘ ユーザーに見える形で保存・表示を復元
swApp.Visible = True
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swViewDisplayHideAllTypes, False
‘ 成功メッセージ
swApp.SendMsgToUser2 “アセンブリの一括構築が正常に完了しました。”, swMessageBoxIcon_e.swMbInformation, swMessageBoxBtn_e.swMbOk
GoTo CleanUpAndExit
TransactionRollback:
‘ ==============================================================================
‘ 【ROLLBACK】 異常系:中途半端な成果物を完全に葬り去る
‘ ==============================================================================
Dim errDesc As String
errDesc = Err.Description
If transactionStarted And Not (swModel Is Nothing) Then
Dim docTitle As String
docTitle = swModel.GetTitle()
‘ 変更を保存せずに強制終了(Without saving)
‘ これにより、ファイルシステムやメモリ上にゴミを残さない
swApp.CloseDoc docTitle
End If
‘ UIと環境の強制復元
swApp.Visible = True
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swViewDisplayHideAllTypes, False
‘ ログ出力およびエラー通知
swApp.SendMsgToUser2 “【トランザクション異常終了】処理がロールバックされました。” & vbCrLf & “詳細: ” & errDesc, _
swMessageBoxIcon_e.swMbStop, swMessageBoxBtn_e.swMbOk
CleanUpAndExit:
‘ ==============================================================================
資源の明示的解放(COMメモリリークの完全撲滅)
‘ ==============================================================================
Set swAssm = Nothing
Set swModel = Nothing
Set swApp = Nothing
‘ 強制ガベージコレクションの誘発
GC.Collect ‘ VB.NETの場合。VBAの場合はDoEventsとオブジェクトのNothing代入を徹底
DoEvents
End Sub
—
3. チーフアーキテクトが教える:実務における3つの極意
上記のコードベースをさらに実務の現場で耐えうるものにするため、シニアエンジニアが押さえておくべき「3つの極意」を授ける。
① `AutoRebuildChanges = False` によるパフォーマンスの爆発的向上とエラー回避
アセンブリに部品を1つ追加するたびにSolidWorksは全体のマッチングを再計算しようとする。これが数千点規模になると、途中でスタックオーバーフローやメモリ不足を引き起こす原因になる。
トランザクション内では必ず自動リビルドを切り、すべての部品と合致を流し込んだ「最後」に一括で `EditRebuild3()` を叩くこと。 これだけで処理時間が数分から数秒へと劇的に短縮される。
② 参照カウント(Reference Counting)とCOM解放の鉄則
VBAはVB6の系譜を引く言語であり、ガベージコレクションが極めて曖昧である。特にSolidWorks APIから返されるオブジェクト(`Component2`, `Mate2`, `ModelDocExtension` 等)は、ループ内でローカル変数として使い捨てると、VBAの背後にあるCOMポインタがメモリ上に残り続け、アセンブリを閉じた後もプロセスがゾンビ化する(SLDWORKS.exeがタスクマネージャーに残る原因)。
ループ内でオブジェクトを取得する場合は、必ずループのスコープ内で `Set obj = Nothing` を明示的に実行し、さらにモジュールレベルでの変数汚染を防がなければならない。
③ ファイルI/Oの競合回避(トランザクションの分離)
もしこの処理を「既存のアセンブリを開いて上書きしていく」形で実装すると、ロールバック時に元のファイルが破壊されるリスクが生じる。
真に堅牢なトランザクション管理とは、「一時的な新規ファイル(サンドボックス)」で全工程をシミュレートし、最後のコミットの瞬間だけ名前を付けて保存(SaveAs4)するというアトミック(不可分)なアプローチをとることである。これにより、いかなる例外が発生しようとも、既存のマスターデータを保護できる。
—
結び
自動化とは、単に手作業をコードに置き換えることではない。
「予期せぬ失敗が起きたときに、システムを絶対に汚染しないこと」こそが、プロフェッショナルとアマチュアを分かつ決定的な境界線である。
今回解説したトランザクション管理とロールバックの思想をあなたのVBAアーキテクチャに組み込み、真に信頼性の高い堅牢なCAD自動化システムを構築してほしい。
