SolidWorks VBAを掌握する極限の知見
第42回:マルチボディ非干渉化と個別名前変更の極意 – `Body2` オブジェクトのライフサイクルとメモリ管理の深淵
—
序:マルチボディ自動化の暗黒面とVBAの限界
こんにちは。チーフアーキテクトの私だ。
現場の設計者から「1つのパーツファイル内にある複数のソリッドボディを、自動で個別のファイルに切り出し、それぞれ適切な部品番号を付与して保存したい」という要求を受けたことはないだろうか?
一見、単なる「ボディのエクスポート」に思えるこの処理だが、SolidWorks VBAの底流にあるCOMコンポーネントの挙動、特に`Body2` オブジェクトのメモリ管理とポインタの寿命を理解していない者にとっては、地獄への片道切符となる。
大容量のアセンブリや、数十のフィーチャが絡み合う複雑なマルチボディパーツにおいて、安易なコードは深刻なメモリリークを引き起こし、最悪の場合はSolidWorks本体を沈黙させる。
今回は、`Body2.GetBodyType` と `Body2.GetVolume` を駆使し、幾何学的な特徴からボディを完璧に識別・分離し、安全に外部へ吐き出すための「実務直結の極限コード」をここに公開する。
—
1. 核心的アーキテクチャ:なぜ `Body2` の解放漏れがクラッシュを招くのか
SolidWorks APIにおいて、`PartDoc::GetBodies2` や `Feature::GetBodies` を呼び出すとき、背後ではCOMの参照カウンタがインクリメントされている。
VBAのガベージコレクション(正確にはCOMラッパーの解放メカニズム)は極めて怠惰であり、特にループ内で取得したローカルな `Body2` オブジェクトは、スコープを抜けても即座にメモリから消去されない。
鉄則:明示的な `Set obj = Nothing` の強制
ループの1イテレーションごとに `Body2` 変数を使い回す場合、前回の参照が残ったまま次のオブジェクトを指すと、メモリリークの温床となる。
さらに、ボディのボリューム(体積)やバウンディングボックスを計算する際、内部で一時的なジオメトリ評価が走るため、処理の終わりに必ず明示的な解放を行わなければならない。
—
2. 実装コード:非干渉ボディ自動分離と個別名前変更エンジン
以下のコードは、アクティブなマルチボディパーツから全てのソリッドボディを列挙し、体積によるフィルタリング(あるいは識別)を行った上で、個別のコンフィギュレーションまたは独立したパーツとして出力、さらにカスタムプロパティを付与して保存する実務仕様のマクロである。
‘ Option Explicitを必ず記述し、暗黙の変数の宣言を防ぐ(メモリと型の厳格化)
Option Explicit
Sub Main()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swPart As SldWorks.PartDoc
Dim vBodies As Variant
Dim swBody As SldWorks.Body2
Dim i As Long
‘ アプリケーションインスタンスの取得
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “ドキュメントが開かれていません。”, vbCritical
Exit Sub
End If
‘ パーツドキュメント以外の実行をガード
If swModel.GetType <> swDocPART Then
MsgBox “このマクロはパーツファイルでのみ実行可能です。”, vbCritical
Exit Sub
End If
Set swPart = swModel
‘ 0 = 視覚的なボディを含まない、純粋なソリッドボディのみを取得
vBodies = swPart.GetBodies2(swSolidBody, False)
If IsEmpty(vBodies) Then
MsgBox “ソリッドボディが存在しません。”, vbExclamation
Exit Sub
End If
Dim bodyCount As Long
bodyCount = UBound(vBodies) – LBound(vBodies) + 1
Debug.Print “検出されたソリッドボディ数: ” & bodyCount
Dim processedCount As Long
processedCount = 0
For i = LBound(vBodies) To UBound(vBodies)
Set swBody = vBodies(i)
‘ 念のため有効なボディか検証
If Not swBody Is Nothing Then
‘ 1. ボディタイプの確認 (swSolidBody = 0)
If swBody.GetType = swSolidBody Then
‘ 2. 幾何学情報の取得(体積と名前の判定)
Dim bodyVolume As Double
bodyVolume = swBody.GetVolume ‘ 単位はm^3 (SolidWorks内部単位)
Dim originalName As String
originalName = swBody.Name
‘ — 実務ロジック:体積や位置による識別・リネーム —
Dim newBodyName As String
If bodyVolume > 0.0001 Then ‘ 例: 100cm^3より大きい場合
newBodyName = “MainStruct_” & Format(processedCount + 1, “00”)
Else
newBodyName = “SubComponent_” & Format(processedCount + 1, “00”)
End If
‘ ボディ自体のFeatureNameまたはDisplayPropertiesを操作して名前変更
‘ 注: 厳密にはFeatureツリー上の名前変更にはFeatureオブジェクトの操作が必要
Call RenameSolidBody(swModel, swBody, newBodyName)
‘ 3. 個別エクスポート処理(必要に応じてファイルパスを指定)
‘ Call ExportSingleBody(swModel, swBody, “C:\ExportFolder\” & newBodyName & “.sldprt”)
processedCount = processedCount + 1
End If
End If
‘ 【極めて重要】メモリリーク防衛のための参照解放
Set swBody = Nothing
Next i
‘ 配列自体の解放
Erase vBodies
MsgBox “処理完了: ” & processedCount & ” 個のボディを処理しました。”, vbInformation
End Sub
‘ =================================================================
‘ 補助関数: ソリッドボディのフィーチャ名(ツリー上の名前)を変更する
‘ =================================================================
Private Sub RenameSolidBody(swModel As SldWorks.ModelDoc2, swBody As SldWorks.Body2, targetName As String)
Dim swFeat As SldWorks.Feature
Set swFeat = swBody.GetFeature
If Not swFeat Is Nothing Then
Dim currentFeatName As String
currentFeatName = swFeat.Name
‘ フィーチャ名(ボディ名)の変更
Dim bRet As Boolean
bRet = swModel.Extension.SelectByID2(currentFeatName, “BODYFEATURE”, 0, 0, 0, False, 0, Nothing, 0)
If bRet Then
swModel.SetName(targetName)
swModel.ClearSelection2 True
End If
Set swFeat = Nothing
End If
End Sub
—
3. チーフアーキテクトが指摘する「陥りがちな罠」
1. `GetBodies2` の引数ミス
第2引数の `TopLevelOnly` に `True` を渡すと、アセンブリやコンテキスト内の外部参照ボディを取りこぼす。マルチボディパーツ単体をターゲットにする場合でも、正確な評価を行うためには設計意図に合わせたフラグ設定が不可欠である。
2. 内部キャッシュと再描画の競合
大量のボディを一括リネーム・エクスポートする際、裏で画面の再描画(Graphics Update)が走ると、VBAの実行速度が著しく低下するだけでなく、COM例外(RPC_E_CANTCALLOUT_IN_CALL等)を引き起こす。処理の冒頭で `swModel.ModelViewManager.SetLockUpdate True` をかけ、終了時に解除する手法(ビューロック)を実務では推奨する。
3. 体積(`GetVolume`)の単位系
SolidWorks APIが返す数値は常にSI単位系(メートル法: m, kg, s)である。UI上で「mm^3」で見慣れている数値とは桁が大きく異なるため、条件分岐のしきい値を設定する際は `1000^3` などの換算ミスに細心の注意を払うこと。
—
結:自動化の信頼性は「後始末」の美しさに宿る
動くだけのコードであれば、素人でも書ける。しかし、数百回連続で実行してもメモリ使用量が微塵も増えず、例外処理が完璧に組み込まれたコードこそが、真の「プロダクション・グレード」である。
`Body2` オブジェクトを制する者は、SolidWorksの形状操作を制する。今回の知見をあなたの開発環境に組み込み、レガシーな手作業の呪縛から設計現場を解放してほしい健闘を祈る。
