SolidWorks APIの深淵:大規模アセンブリ構築における「メモリ・リーク」という名の亡霊を葬る
SolidWorksのAPI開発において、小規模なスクリプトから数千パーツ規模のアセンブリ自動構築へ足を踏み入れた瞬間、多くのエンジニアは「SolidWorksの強制終了」という壁に突き当たる。
その原因の9割は、コードのロジックではなく、COMオブジェクトの参照管理の甘さにある。VBAのガベージコレクション(GC)を過信してはならない。本稿では、レガシーなVBA環境下で、メモリ消費を極限まで抑制し、安定稼働を実現するための「参照解放の鉄則」を紐解く。
—
1. 参照解放の「正しい」作法:Nothingは儀式ではない
VBAにおいて、オブジェクト変数を `Nothing` に代入することは、単なる気休めではない。これはWindowsのCOMマーシャラーに対し、「この参照を即座に解放せよ」と告げる明示的な命令である。
特にアセンブリ構築において、`Mate2` や `Component2` インターフェースをループ内で生成し続けると、メモリは瞬く間に枯渇する。
NGな実装(メモリを食い荒らす例)
‘ これを数千回繰り返すとメモリリークの温床になる
For i = 1 To 1000
Set swComp = swAssy.AddComponent6(…)
‘ ここでswCompを明示的にNothingにしないと、次のループで参照が積み上がる
Next i
厳格な参照解放パターン
Dim swComp As SldWorks.Component2
Dim i As Long
For i = 1 To 1000
Set swComp = swAssy.AddComponent6(…)
‘ ここでオブジェクトの処理を完結させる
If Not swComp Is Nothing Then
‘ … 合致処理など …
End If
‘ 重要:スコープ内であっても、ループの最後で必ず解放する
Set swComp = Nothing
Next i
—
2. 大規模構築におけるパフォーマンスの罠:IAssemblyDocのキャッシュ戦略
数千のパーツを挿入する際、`swApp.ActiveDoc` を毎回呼び出してはならない。これはプロセスの間を跨ぐオーバーヘッドを発生させる。
アーキテクトが取るべき戦略は、「ルート・アセンブリの参照をスコープ最上位で固定し、一度だけ取得する」ことだ。
参照最適化のコードパターン
‘ クラスモジュールや標準モジュールの最上位で宣言
Private m_swApp As SldWorks.SldWorks
Private m_swAssy As SldWorks.AssemblyDoc
‘ 初期化時に一度だけバインドし、セッション中使い回す
Public Sub InitializeAssembly(swApp As SldWorks.SldWorks)
Set m_swApp = swApp
Set m_swAssy = m_swApp.ActiveDoc
End Sub
‘ 終了時に確実に破棄する
Public Sub Terminate()
Set m_swAssy = Nothing
Set m_swApp = Nothing
End Sub
このアプローチにより、COMの再バインドによるオーバーヘッドを排除し、SolidWorksの安定性を劇的に向上させる。
—
3. Windows APIによる強制的なメモリ解放のヒント
VBAのガーベジコレクションが機能しない場合、あるいはSolidWorksが背後で掴んでいるメモリを物理的に解放させたい場合、Windows APIの `SetProcessWorkingSetSize` を活用するという「禁じ手」がある。
これは、プロセスのワーキングセット(メモリ使用量)をOSに強制的に再割り当てさせるものだ。大規模アセンブリ構築後にこれを実行することで、SolidWorksの「膨張」を抑制できる可能性がある。
If VBA7 Then
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” _
(ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As LongPtr, _
ByVal dwMaximumWorkingSetSize As LongPtr) As Long
Private Declare PtrSafe Function GetCurrentProcess Lib “kernel32” () As LongPtr
End If
‘ メモリをクリーンアップする関数
Public Sub ForceMemoryCleanup()
Call SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1)
DoEvents
End Sub
※注意: このAPIは極めて強力であり、頻繁な呼び出しはパフォーマンス低下を招く。あくまでバッチ処理の区切り目でのみ使用すること。
—
4. 最後に:エンジニアとしての矜持
数千パーツのアセンブリを自動制御する際、コードの記述量よりも「そのコードがSolidWorksのプロセスに対してどのような負荷をかけているか」を想像できる能力が、シニアエンジニアとそうでない者を分かつ。
1. スコープを最小限に絞る(可能な限り関数内で完結させ、終了時にNothing化する)
2. ループ内のオブジェクト生成を回避する(メモリバッファの再利用を意識する)
3. エラーハンドリングでメモリリークを防ぐ(例外発生時でも `Finally` 的な処理でNothing化する)
これらを徹底することで、あなたの書くSolidWorks VBAは、単なるスクリプトから、堅牢なエンジニアリング・ツールへと昇華する。
SolidWorksという巨大なプラットフォームを制御する者は、常にプロセスの安定性に責任を持つべきだ。それが、我々自動化エンジニアの誇りである。
