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

スポンサーリンク

こんにちは!SolidWorks自動化の世界へようこそ。
マクロの記録ボタンを押して生成されたコードを眺める段階を卒業し、「さあ、数千点規模の大規模アセンブリをプログラムで縦横無尽に操ってやるぞ!」と意気込んでいるあなたへ。

今回は、SolidWorks VBAエンジニアとして避けて通れない、しかし多くの人が見落としがち(そして大惨事になりがち)な「COMオブジェクトのメモリリーク完全撲滅」という極限の知見を授けましょう。

ここをクリアすれば、あなたも単なる「コードのコピペ職人」から、システムを安定稼働させる「真のチーフアーキテクト」へステップアップできます。一緒に本質を学んでいきましょう!

—

1. なぜ、大規模アセンブリのループ処理でSolidWorksがフリーズするのか?

数千点もの部品(Component)を持つ巨大なアセンブリをVBAで処理するとき、こんな恐怖を体験したことはありませんか?

  • 「最初はサクサク動いていたのに、ループの後半から急激に動作が重くなり、最終的に固まった(あるいはVBAが強制終了した)」
  • 「タスクマネージャーを見ると、Excelのメモリ消費量が右肩上がりに膨れ上がり、PCのファンが唸りを上げている」

これは、VBAのコードが間違っているというよりも、背後でうごめく「COM(Component Object Model)のメモリ管理の仕組み」を誤解していることが原因です。

図解:VBAとSolidWorks(COM)の裏側の世界

[VBA (Excel)] –(参照)–> [SolidWorks API (COM Server)]
(変数) (巨大な3Dモデルデータ群)

VBAで `Set swComp = swAssy.GetComponentByName(…)` のように記述すると、裏側ではSolidWorksのメモリ上にいる「3Dの部品オブジェクト」と、VBA側の「変数(`swComp`)」が太いパイプ(参照)で結ばれます。

問題は、「VBAのループが次の周回に進んだとき、古い変数が自動的にきれいさっぱり消えてくれるわけではない」という点です。VBAのガベージコレクションは気まぐれで、特にCOMオブジェクトの参照カウントは、人間が意図的に断ち切ってやらないと、メモリの宇宙空間を漂い続けてしまうのです。

—

2. 破滅への第一歩:やってはいけない「NGコード」

まずは、多くの初学者や中級者がやりがちな「危ういコード」を見てみましょう。どこが問題かわかりますか?

‘ 【絶対に真似してはいけないNGコードの例】
Sub BadAssemblyLoop()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swAssy As SldWorks.AssemblyDoc
Dim vComps As Variant
Dim i As Long
Dim swComp As SldWorks.Component2

Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swAssy = swModel

‘ アセンブリ内の全コンポーネントを取得
vComps = swAssy.GetComponents(True)

‘ 数千回のループ
For i = LBound(vComps) To UBound(vComps)
Set swComp = vComps(i)

‘ なんらかの重い処理(例:コンポーネントの名称取得やマトリックス操作など)
Debug.Print swComp.Name2

‘ 【致命的】
‘ ループが次に行くとき、swComp変数に新しいオブジェクトが上書きされるが、
‘ 古いオブジェクトへの参照がCOMのメモリ空間に残余しやすい!
Next i

MsgBox “処理完了!”
End Sub

このコードを数千回まわすと、VBAとSolidWorksをつなぐブリッジのメモリが飽和し、メモリリークを引き起こします。「たかがマクロだし、終わったらExcelを閉じればいいや」なんて思っていませんか? 業務用の自動化ツールとして組み込む場合、これは致命傷になります。

—

3. チーフアーキテクト伝授:COMメモリリークを完全撲滅する3大鉄則

ここからが本題です。複雑なアセンブリループを何万回回そうとも、メモリ消費量をピクリともさせないための「デストラクタ的アプローチ(手動クリーンアップ)」の鉄則を授けます。

鉄則①:ループ内での変数使い回しを避け、必ず最後に `Set xxx = Nothing` する

VBAには厳密な「デストラクタ(C#やC++にあるような、オブジェクト消滅時に自動実行される仕組み)」がありません。そのため、「使い終わったオブジェクト変数は、自分で明示的に `Nothing` を代入して参照カウンターをゼロにする」必要があります。

鉄則②:配列(Variant)で一括取得したコンポーネントも、個別に解放する

`GetComponents` メソッドなどで配列として取得したオブジェクトも、ループ内で変数に代入して処理する場合、その都度適切な解放が必要です。

鉄則③:親から子へ、ローカル変数はスコープごとに確実に処分する

手続きの粒度(関数やサブルーチン)を細かく分け、サブルーチンを抜けるタイミングでローカル変数が綺麗に消える構造を作ります。

—

4. 実戦投入可能な模範解答:完全メモリ管理版アセンブリ処理コード

それでは、上記すべての鉄則を網羅した、実務でそのまま使える堅牢(ロバスト)なコードを公開します。

‘ ==============================================================================
‘ 【上級プロ仕様】メモリリーク完全撲滅版アセンブリコンポーネント走査マクロ
‘ ==============================================================================
Sub PerfectAssemblyLoop()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swAssy As SldWorks.AssemblyDoc

‘ アプリケーションの取得
Set swApp = Application.SldWorks
If swApp Is Nothing Then Exit Sub

Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントがありません。”, vbExclamation
Exit Sub
End If

‘ アセンブリドキュメントかチェック
If swModel.GetType <> swDocASSEMBLY Then
MsgBox “アクティブなドキュメントはアセンブリではありません。”, vbCritical
Exit Sub
End If

Set swAssy = swModel

‘ 処理開始(パフォーマンス向上のための画面描画停止)
swApp.Visible = False ‘ 必要に応じて
swModel.EditRebuild

Dim vComps As Variant
vComps = swAssy.GetComponents(True) ‘ TopLevelOnly = True (必要に応じてFalseに変更)

If IsEmpty(vComps) Then
MsgBox “コンポーネントが存在しません。”, vbInformation
GoTo CleanUp_Exit
End If

Dim i As Long
Dim swComp As SldWorks.Component2
Dim compName As String

On Error GoTo ErrorHandler ‘ 予期せぬエラーでもメモリ解放を通すためのトラップ

For i = LBound(vComps) To UBound(vComps)
‘ 1. オブジェクトのセット
Set swComp = vComps(i)

If Not swComp Is Nothing Then
‘ — ここに実際の業務処理を記述 —
compName = swComp.Name2
‘ 例: Debug.Print compName
‘ ———————————-

‘ 2. 処理が終わったら即座に変数を明示的解放
Set swComp = Nothing
End If
Next i

MsgBox “数千点のコンポーネント処理がメモリリークなしで完了しました!”, vbInformation

CleanUp_Exit:
‘ ==============================================================================
‘ 【重要】すべてのCOMオブジェクト変数に Nothing を代入して参照カウントを確実に落とす
‘ ==============================================================================
Set swComp = Nothing
vComps = Empty
Set swAssy = Nothing
Set swModel = Nothing
Set swApp = Nothing

Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp_Exit

End Sub

—

5. コードのここがポイント!プロのこだわり解説

1. `On Error GoTo ErrorHandler` による安全網
万が一、ループの途中でエラー(部品の欠損やアクセス違反など)が発生した場合でも、そのままプログラムが中断するとメモリが宙ぶらりんになります。エラーハンドラを必ず経由させ、最後に必ず `Nothing` を通る構造にしているのがポイントです。
2. `CleanUp_Exit` ラベルでの徹底的な `Nothing` クリア
プロシージャの終了間際に、使っていたすべてのSolidWorks関連オブジェクト(`swComp`, `swAssy`, `swModel`, `swApp`)を上から順に `Nothing` で潰していきます。このひと手間で、VBAのメモリ空間は常にクリーンな状態を保てます。

—

最後に:ここをクリアすれば、SolidWorks VBAの基本はバッチリですよ!

数千点のアセンブリを扱う自動化の世界へようこそ。メモリ管理という「裏側のルール」を理解したあなたなら、もう巨大なモデルを相手にしても怖じ気づくことはありません。

「動けばいいや」のコードから、「美しく、リソースを汚さないプロフェッショナルなコード」へ。
この知見をあなたの引き出しに加えれば、どんなに複雑な社内ニッチの自動化要求が来ても、余裕の表情でクリアできるようになります。

明日からのSolidWorksライフを、より快適でスマートなものにしていきましょう!

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