【実務・中級編】【上級プロ】大規模アセンブリの軽量化とメモリ最適化:ライトウェイト(Lightweight)モードと解凍状態をVBAで動的制御するメモリ管理術 – SolidWorks VBA解析バイブル

スポンサーリンク

1. なぜあなたのマクロは数千点のアセンブリで沈黙するのか

数千点のパーツで構成されるトップアセンブリを走査し、特定のカスタムプロパティを更新したり、特定の干渉チェックや合致(Mate)の再定義を自動実行しようとした瞬間——SolidWorksがフリーズし、タスクマネージャーのメモリ消費量が激増、最終的に「メモリ不足」のエラーダイアログと共にクラッシュする。

心当たりがあるはずです。

多くのエンジニアは、この問題を「マシンのスペック不足」や「SolidWorksの不安定さ」のせいにします。しかし、アーキテクトの視点から言えば、それは誤解です。真因は、APIコールに伴うCOMオブジェクトのライフサイクルと、B-Rep(境界表現)ジオメトリのオンデマンド展開に対する無理解にあります。

暗黙の解凍(Silent Resolve)の恐怖

アセンブリをライトウェイト(Lightweight)モードで開いていても、VBAで以下のようなコードを無邪気に叩いた瞬間、破滅が始まります。

‘ 破滅のトリガー:Lightweightコンポーネントに対して安易にModelDoc2や特定フィーチャーへアクセスする
Dim swComp As SldWorks.Component2
Set swComp = …
Dim swCompModel As SldWorks.ModelDoc2
Set swCompModel = swComp.GetModelDoc2() ‘ ここで内部的に完全解凍(Resolve)が走る場合がある!

SolidWorks APIは、ジオメトリ情報(面、エッジ、フィーチャー等)を要求された際、コンポーネントがライトウェイト状態であれば「親切にも」バックグラウンドで完全解凍を試みます。ループ処理の中でこれを繰り返せば、数GBのRAMは数分で枯渇し、Windowsの仮想メモリすら食い尽くします。

数万点規模のプラントや大型産業機械を相手にするなら、「すべてのコンポーネントをライトウェイトで維持し、変更が必要な“その1点”だけをオンデマンドで解凍し、処理が終わり次第即座にライトウェイトへ叩き落とし、COM参照を断ち切る」というメモリの完全掌握が不可欠です。

—

2. アーキテクチャ設計:オンデマンド・リゾルブ戦略

大規模アセンブリを安全に自動制御するためのワークフローは、以下の3原則で設計します。

[トップアセンブリをLightweightモード指定でオープン]
│
▼
[全コンポーネントのトラバース(走査)]
※この段階では座標変換(Transform)と名前のみ参照
│
▼
[操作対象コンポーネントの特定]
│
▼
┌───────────────────────────────┐
│ ★ 動的リゾルブ・サイクル │
│ 1. 対象のみ Resolved に昇格 │
│ 2. 最小限の処理(合致・編集) │
│ 3. 即座に Lightweight へ降格 │
│ 4. COMポインタの完全破棄 │
└───────────────────────────────┘
│
▼
[保存 & メモリパージ]

ステート遷移のAPI仕様

コンポーネントの抑制状態(Suppression State)を切り替えるには、`IComponent2::SetSuppression2` を使用します。

  • `swComponentSuppressionState_e.swComponentLightweight` (1)
  • `swComponentSuppressionState_e.swComponentFullyResolved` (2)

ここで重要なのは、「解凍状態からライトウェイトへの遷移は、アセンブリに変更が残っていると拒否される場合がある」という点です。そのため、編集が完了したら直ちにフィーチャーマネージャーツリーをクリーンにし、更新通知を最小化するコーディングが求められます。

—

3. 実装:堅牢なメモリ最適化プロダクションコード

以下に示すコードは、そのまま本番環境に投入可能なテンプレートです。

このモジュールは、指定されたアセンブリを強制的にライトウェイトモードで開き、ツリーを走査。特定の条件(例:特定のプレフィックスを持つ部品や合致ターゲット)に一致するコンポーネントのみを1つずつ解凍して処理し、即座にライトウェイトへ復帰させます。

Option Explicit

‘ ==============================================================================
‘ Module : M_DynamicAssemblyOptimizer
‘ Description: 大規模アセンブリのオンデマンド・リゾルブ制御とメモリ最適化
‘ ==============================================================================

Private Const TARGET_ASSEMBLY_PATH As String = “C:\Projects\MegaAssembly\TopAssembly.SLDASM”

Public Sub Main()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks

If swApp Is Nothing Then Exit Sub

‘ 1. パフォーマンスのためのUI・イベント凍結
OptimizeSystemPerformance swApp, True

Dim swModel As SldWorks.ModelDoc2
Dim errors As Long, warnings As Long

‘ 2. 大規模ドキュメント指定(Lightweight明示オープン)
Dim swDocSpec As SldWorks.DocumentSpecification
Set swDocSpec = swApp.GetOpenDocSpec(TARGET_ASSEMBLY_PATH)

swDocSpec.DocumentType = swDocASSEMBLY
swDocSpec.ReadOnly = False
swDocSpec.Silent = True
swDocSpec.ConfigurationName = “”
‘ 【重要】明示的にライトウェイトで読み込む
swDocSpec.LightWeight = True

Set swModel = swApp.OpenDoc7(swDocSpec)

If swModel Is Nothing Then
OptimizeSystemPerformance swApp, False
MsgBox “ドキュメントの読み込みに失敗しました。パスを確認してください。”, vbCritical
Exit Sub
End If

Dim swAssy As SldWorks.AssemblyDoc
Set swAssy = swModel

‘ 3. アセンブリ走査とオンデマンド処理
ProcessAssemblyComponentsOnDemand swAssy

‘ 4. 後処理・復元
OptimizeSystemPerformance swApp, False
MsgBox “オンデマンド走査・処理が完了しました。”, vbInformation
End Sub

‘ ——————————————————————————
‘ 画面描画とバックグラウンド処理の抑制による高速化
‘ ——————————————————————————
Private Sub OptimizeSystemPerformance(ByVal swApp As SldWorks.SldWorks, ByVal bEnable As Boolean)
If bEnable Then
swApp.CommandInProgress = True
‘ UIの再描画抑制
swApp.EnableBackgroundProcessing = False
Else
swApp.CommandInProgress = False
swApp.EnableBackgroundProcessing = True
End If
End Sub

‘ ——————————————————————————
‘ コンポーネントのトラバースと動的解凍・軽量化のループ
‘ ——————————————————————————
Private Sub ProcessAssemblyComponentsOnDemand(ByVal swAssy As SldWorks.AssemblyDoc)
Dim swConf As SldWorks.Configuration
Dim swRootComp As SldWorks.Component2
Dim swModel As SldWorks.ModelDoc2
Set swModel = swAssy

Set swConf = swModel.GetActiveConfiguration
Set swRootComp = swConf.GetRootComponent3(True)

Dim vChildren As Variant
vChildren = swRootComp.GetChildren

If IsEmpty(vChildren) Then Exit Sub

Dim i As Long
Dim swChildComp As SldWorks.Component2

For i = LBound(vChildren) To UBound(vChildren)
Set swChildComp = vChildren(i)

‘ 抑制(Suppressed)コンポーネントはスキップ
If swChildComp.GetSuppression2 <> swComponentSuppressed Then

‘ 例: 条件判定(ここでは特定の名称を含む部品を対象とする)
If InStr(1, swChildComp.Name2, “ACTUATOR”, vbTextCompare) > 0 Then

‘ === 【最重要】オンデマンド・リゾルブの実行 ===
ExecuteOnDemandOperation swAssy, swChildComp

End If

End If

‘ COM参照をループ内で明示的に切断する(ガベージコレクション促進)
Set swChildComp = Nothing
Next i
End Sub

‘ ——————————————————————————
‘ 単一コンポーネントを一時的に解凍し、処理後に即座にLightweightへ戻す
‘ ——————————————————————————
Private Sub ExecuteOnDemandOperation(ByVal swAssy As SldWorks.AssemblyDoc, ByVal swComp As SldWorks.Component2)
Dim originalState As Long
originalState = swComp.GetSuppression2

‘ すでにResolvedでない場合のみ、昇格させる
If originalState = swComponentLightweight Then
Dim resolveResult As Long
resolveResult = swComp.SetSuppression2(swComponentFullyResolved)

If resolveResult <> swSuppressionResolveSuccess Then
Debug.Print “解凍に失敗: ” & swComp.Name2
Exit Sub
End If
End If

‘ ———————————————————————-
‘ 【実処理ブロック】
‘ ジオメトリへのアクセス、合致の定義、プロパティの書き込みなどを安全に行う
‘ ———————————————————————-
Dim swCompModel As SldWorks.ModelDoc2
Set swCompModel = swComp.GetModelDoc2

If Not swCompModel Is Nothing Then
‘ 例: 内部モデルのカスタムプロパティを更新
Dim cpm As SldWorks.CustomPropertyManager
Set cpm = swCompModel.Extension.CustomPropertyManager(“”)
cpm.Add3 “BatchProcessed”, swCustomInfoYesOrNo, “Yes”, swCustomPropertyDeleteAndAdd

‘ モデルのダーティフラグを解消するため保存を強制(必要に応じて)
‘ swCompModel.Save3 swSaveDefaults, 0, 0
End If

‘ モデル参照の破棄(これを残したままライトウェイト化すると失敗する)
Set cpm = Nothing
Set swCompModel = Nothing

‘ ———————————————————————-
‘ 【状態復帰】即座にライトウェイトへ降格させてRAMを解放
‘ ———————————————————————-
If originalState = swComponentLightweight Then
Dim demoteResult As Long
demoteResult = swComp.SetSuppression2(swComponentLightweight)

If demoteResult <> swSuppressionResolveSuccess Then
Debug.Print “軽量化復旧に失敗: ” & swComp.Name2
End If
End If

‘ 定期的にWindowsのメッセージキューをさばき、メモリリークを抑止
DoEvents
End Sub

—

4. プロダクション運用で絶対に外せない3つの掟

大規模アセンブリの自動化を成功させるには、上記コードに加えて「運用環境の防壁」を敷く必要があります。

1. `GetModelDoc2` の戻り値に対する盲信を捨てる

ライトウェイト状態のコンポーネントに対して `swComp.GetModelDoc2` を呼ぶと、SolidWorksはバックグラウンドで該当パーツのRAM展開を試行しますが、参照モードやメモリ制約によっては`Nothing`を返します。
これをハンドリングせずに `swCompModel.Extension…` を実行すれば、実行時エラー91(オブジェクト変数が設定されていません)でプロセスは即死します。
コード内で示した通り、「必ず `SetSuppression2(swComponentFullyResolved)` を通してからポインタを取得する」。これが鉄則です。

2. COM参照のライフサイクルを完結させる

VBAは内部で参照カウント方式を採用しています。
`For` ループの内部で `Dim swCompModel As SldWorks.ModelDoc2` にオブジェクトを代入し、ループの最後で `Set swCompModel = Nothing` を明示しない場合、内部ポインタが残存し、コンポーネントを `SetSuppression2(swComponentLightweight)` で軽量化に戻そうとしてもSolidWorksが「まだ参照中である」と判断して拒否します。使い終わったオブジェクトは、そのスコープ内で即座に破棄してください。

3. PDM・排他制御(Concurrency)との衝突

エンタープライズ環境(SOLIDWORKS PDM等)でこの処理を走らせる場合、ライトウェイトで開いた部品のローカルキャッシュが存在しないと、動的解凍時にバックグラウンドでボルト(Vault)からのサイレント取得が発生します。
ネットワーク帯域の飽和やチェックアウト競合によるスクリプト停止を防ぐため、「バッチ実行前にPDM APIで必要なツリー構造をあらかじめローカルキャッシュに同期させておく」アーキテクチャ設計を推奨します。

—

5. まとめ:メモリを統御する者が大規模アセンブリを制す

大規模アセンブリの自動化において、コードの「短さ」は美徳ではありません。
重要なのは、「いつ、どのメモリが確保され、どのタイミングで破棄されるか」を完全にコントロール下に置くことです。

1. `IDocumentSpecification` でアセンブリを安全に(Lightweightで)開く
2. 走査はトポロジー(ツリー階層)のみで行い、ジオメトリには触れない
3. 必要なパーツのみをオンデマンドで完全解凍し、用が済んだら即座にライトウェイトへ落とす
4. COMオブジェクトを自らの手で `Nothing` に還す

この基本設計を遵守する限り、あなたの書いたマクロがメモリ不足でクラッシュすることは二度とありません。規模が数万点になろうとも、淡々と、最速で、極めて堅牢に処理を完遂できるようになります。ツール作成者の枠を超え、真のアーキテクトとして堅牢なコードを組み立ててください。

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