【テクニカル・上級編】【上級プロ】COMオブジェクトの参照カウントとデストラクタ的アプローチ:複雑なアセンブリループ処理におけるメモリリーク完全撲滅の鉄則 – SolidWorks VBA解析バイブル

スポンサーリンク

SolidWorks VBAを掌握する極限の知見

【上級プロ】COMオブジェクトの参照カウントとデストラクタ的アプローチ:複雑なアセンブリループ処理におけるメモリリーク完全撲滅の鉄則

数千点規模のコンポーネントが複雑にネストされた大規模アセンブリ。その自動生成や一括処理をVBAで実装した経験があるエンジニアなら、一度は直面したことがあるはずだ。
処理の後半になるにつれて著しく低下するパフォーマンス、突如として訪れる「メモリ不足(Out of Memory)」エラー、そしてタスクマネージャー上で肥大化し続け、VBA終了後もゾンビのように残存する `SLDWORKS.exe` のプロセス。

原因は明白だ。VBAのガベージコレクタ(厳密にはCOMの参照カウント管理機構)に対する無知と、オブジェクトのライフサイクルを無視した雑なコーディングにある。

本稿では、レガシーかつ単一スレッドで動作するVBAの限界を突破し、SolidWorks APIのCOMメモリリークを完全に撲滅するための「デストラクタ的アプローチ」と厳格なコーディング規約を、実務直結のコードとともに提示する。

—

1. 根源的理解:なぜSolidWorks VBAはメモリリークするのか

VBAは、裏でCOM(Component Object Model)の参照カウント(Reference Counting)によってオブジェクトの生存期間を管理している。
`Set swComp = swAssem.AddComponent5(…)` のようなAPIを叩いた瞬間、SolidWorks側(C++製ネイティブ空間)で生成されたCOMオブジェクトの参照カウンターが「+1」され、VBAの変数 `swComp` にそのポインタが渡される。

問題は、VBAが「スコープを抜けるまで」あるいは「明示的に `Nothing` が代入されるまで」その参照を手放さない点にある。さらに厄介なのは、以下のような「暗黙のCOMオブジェクト生成(メンバチェーン)」だ。

‘ 【アンチパターン】これぞメモリリークの温床
Dim i As Long
For i = 1 to 5000
‘ swAssembly.GetComponents(False) は毎回「新しい配列(SafeArray / COMオブジェクトの集まり)」をヒープ上に生成する
‘ ループ変数や一時変数を経由しないため、この配列の参照カウントを解放する術をVBAプログラマは失う
Debug.Print swAssembly.GetComponents(False)(i).Name2
Next i

このコードを実行すると、ループの回数分だけCOMラッパーと内部ポインタの参照が宙ぶらりんになり、VBAのランタイムが自動解放しきれず、SolidWorksのプロセス空間をジワジワと蝕んでいく。これが「数千点ループの悲劇」の正体だ。

—

2. 鉄則:オブジェクトの「生成逆順」解放とNullクリア規約

複雑なアセンブリを走査・構築する際、メモリリークをゼロにするための鉄則はただ一つ。
「取得した順序の逆で `Set ◯◯ = Nothing` を実行し、参照カウントを確実にゼロへ導くこと」である。

以下に、数千点規模のコンポーネントを安全に走査・処理するための「デストラクタ的パターン」を取り入れた模範的なプロシージャを示す。

実用コード:安全なコンポーネント走査とメモリ解放パターン

Option Explicit

Sub ProcessMassiveAssembly_Extremum()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swAssem As SldWorks.AssemblyDoc

‘ 1. アプリケーションインスタンスの取得(GetLatestVersion等での遅延バインディング回避)
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc

‘ 型チェック
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントがありません。”, vbCritical
Exit Sub
End If

If swModel.GetType <> swDocASSEMBLY Then
MsgBox “対象はアセンブリである必要があります。”, vbCritical
Exit Sub
End If

Set swAssem = swModel

Dim vComps As Variant
Dim i As Long

‘ 2. コンポーネント配列の取得(一度Variant変数で受けることが極めて重要)
vComps = swAssem.GetComponents(False)

If Not IsEmpty(vComps) Then
Dim swComp As SldWorks.Component2
Dim swChildModel As SldWorks.ModelDoc2

For i = LBound(vComps) To UBound(vComps)
‘ エラーハンドリングのスコープ
On Error GoTo ErrorHandler

Set swComp = vComps(i)

If Not swComp Is Nothing Then
‘ 子コンポーネントのドキュメント取得(ここで新たなCOMオブジェクトが生成される)
Set swChildModel = swComp.GetModelDoc2

If Not swChildModel Is Nothing Then
‘ — ここに実際の重い処理を記述 —
‘ 例: 特定のコンポーネントのコンフィギュレーション切替やプロパティ操作
‘ Debug.Print swChildModel.GetTitle
End If

End If

NextIteration:
‘ 3. ループ内オブジェクトの厳格な個別解放(生成とは逆順、または末端から)
Set swChildModel = Nothing
Set swComp = Nothing
Next i
End If

CleanUp:
‘ 4. ルートオブジェクトおよび配列の明示的解放
vComps = Empty ‘ SafeArrayの解放
Set swAssem = Nothing
Set swModel = Nothing
Set swApp = Nothing

‘ ガベージコレクションの強制催促(VBA単体では完全ではないが、COM解放のトリガーとして有効)
DoEvents
Exit Sub

ErrorHandler:
Debug.Print “Error at index ” & i & “: ” & Err.Description
Resume NextIteration

End Sub

—

3. チーフアーキテクトが実践する高度な最適化テクニック

単に `Set x = Nothing` を書くだけでなく、数万行規模のインテグレーションや夜間バッチ処理を安定稼働させるためには、以下のアーキテクチャ的配慮が不可欠である。

A. メンバチェーンの完全禁止

前述した通り、`swApp.ActiveDoc.Extension.SelectByID2…` のようなドットつなぎ(メンバチェーン)は、途中で生成される一時的なCOMオブジェクトの参照を保持する変数が存在しないため、VBAから永遠に解放できなくなる。
複雑な処理を行う際は、必ずワンステップずつローカル変数に受け、それぞれを個別に `Nothing` クリアしろ。

B. 巨大アセンブリにおける「イテレーション単位の明示的解放」

コンポーネント数が5,000を超える場合、1つの巨大なForループ内で処理し続けると、VBAの内部ヒープが断片化する。
もし可能であれば、処理をチャンク(小分け)にするか、定期的に `DoEvents` を挟んでCOMのメッセージポンプを処理させ、OS側へのメモリ返還を促すこと。

‘ 1000件ごとにインターバルとメモリ整理を挟む設計思想
If i Mod 1000 = 0 Then
DoEvents
‘ 必要に応じてGCを意識したウェイトやログ出力
End If

C. エラーハンドリング時(On Error)のメモリリーク防止

プログラムが予期せぬエラーで `Exit Sub` や `Resume` する際、途中で変数が解放されずに残ると、その瞬間にメモリリークが確定する。
厳格なシステムでは、エラー発生時であっても確実に `CleanUp:` ラベルへジャンプし、全COM変数の `Nothing` 代過を通過させる構造(RAIIパターンのVBA的模倣)を徹底すること。

—

4. 結言:レガシーの制約をねじ伏せるプロフェッショナリズム

VBAはレガシーな言語である。しかし、SolidWorksの背後でうごめく強力なCOMアーキテクチャの本質を理解していれば、VBAは単なるマクロの域を超え、堅牢なCAD自動化プラットフォームへと昇華する。

「動けばいい」というアマチュアのコードと、「数千回ループしてもメモリ使用量が1バイトもリークしない」プロフェッショナルのコードの差は、この 「参照カウントに対する敬意と、オブジェクトライフサイクルの完全な支配」 に他ならない。

あなたが次に巨大アセンブリの自動化スクリプトを書くとき、タスクマネージャーのメモリグラフが平坦に安定し続ける美しいコードベースを構築してほしい。それこそが、世界最高峰のエンジニアリングである。

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