【履歴ツリー構造の解析】Feature.GetFirstSubFeatureとGetNextFeatureを使ったフィーチャー巡回アルゴリズム
SolidWorks VBAの自動化において、最もプリミティブでありながら、最もエンジニアの技量が問われる領域が「フィーチャーマネージャー・デザインツリーの走査」だ。
単に `GetFirstFeature` でルートを舐めるだけのコードは、インターネットの海に溢れている。しかし、実務で遭遇する複雑なモデル――コンフィギュレーションによる構成部品の差異、マルチボディ、さらにはサンドイッチされたサブフィーチャーやフォルダー構造――を前にした瞬間、そうした初歩的なコードは沈黙する。
今回は、SolidWorks APIのオブジェクトモデルの深層に踏み込み、`Feature.GetFirstSubFeature` と `GetNextFeature` を駆使して、ツリーの階層構造を完全に掌握するための再帰的巡回アルゴリズムを解説する。
—
1. 圧倒的な正確さが求められる理由:ツリー構造の裏側
SolidWorksのフィーチャーは、単なるリニアなリストではない。それは「有向グラフ(Directed Graph)」、あるいは厳密には「親子関係を持つ階層ツリー」である。
例えば、以下のような構造を考えてみてほしい。
- 押し出しフィーチャー(親)
- フィレット(サブフィーチャー:親の面を参照)
- シェル(サブフィーチャー)
- パターンフィーチャー
- スキップされたインスタンス群
親フィーチャーが消滅すれば、サブフィーチャーの存在基盤も失われる。したがって、設計変更の自動検知、モデルの軽量化診断、あるいは社内PLMシステムへのデータ同期を行うためには、この「親から子へ、そして兄弟へ」というポインタの連鎖を正確に辿るアルゴリズムが不可欠となる。
ここでVBAエンジニアが直面するのが、「COMオブジェクトのメモリ管理と参照リーク」という厳しき現実である。
—
2. オブジェクトのライフサイクルとメモリ最適化の極意
VBAはガベージコレクションを持たない言語ではないが、その実態はCOM(Component Object Model)の参照カウンタ方式である。APIを呼び出すたびに生成される `Feature` などのCOMラッパーオブジェクトは、明示的に解放しなければ、SolidWorksのプロセス内にメモリリークとして蓄積される。
特に、数千のフィーチャーを持つ巨大なアセンブリや複雑なインポートモデルを再帰処理する場合、オブジェクトの解放漏れは、じわじわとメモリを蝕み、やがて「Automationエラー」やSolidWorks自体の強制終了を引き起こす。
鉄則:ローカル変数の即時解放
再帰呼出し(Recursion)を行う関数内では、取得したオブジェクトは必ずループの終わりに `Set feat = Nothing` によって解放しなければならない。VBAのスコープを抜けるまで参照が残る仕様を過信してはならない。
—
3. 実装コード:完全なる再帰巡回アルゴリズム
以下に、実務の現場で即座に利用できる、堅牢性とパフォーマンスを極限まで高めたVBAコードを示す。このコードは、ルートから最深部のサブフィーチャーまで漏れなく走査し、イミディエイトウィンドウに階層構造を描き出す。
‘ ==============================================================================
‘ módulo: Module_TreeTraversal
‘ 概要 : フィーチャーマネージャー・デザインツリー再帰巡回エンジン
‘ 著者 : 伝説のチーフアーキテクト
‘ ==============================================================================
Option Explicit
Public Sub RunFeatureTraversal()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swSelMgr As SldWorks.SelectionMgr
‘ 1. アプリケーションおよびドキュメントの取得
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “エラー”
Exit Sub
End If
Debug.Print “=== ツリー解析開始: ” & swModel.GetTitle & ” ===”
‘ 2. 処理時間の計測開始(パフォーマンス監視)
Dim startTime As Double
startTime = Timer
‘ 3. ルートフィーチャーの取得と巡回開始
Dim swFirstFeat As SldWorks.Feature
Set swFirstFeat = swModel.FirstFeature
If Not swFirstFeat Is Nothing Then
Call TraverseFeature(swFirstFeat, 0)
End If
Debug.Print “=== ツリー解析終了 (処理時間: ” & Format(Timer – startTime, “0.000”) & “秒) ===”
‘ 4. ルートオブジェクトのクリーンアップ
Set swFirstFeat = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub
‘ ——————————————————————————
‘ 内部関数: 再帰的にフィーチャーおよびサブフィーチャーを巡回する
‘ ——————————————————————————
Private Sub TraverseFeature(ByVal swFeat As SldWorks.Feature, ByVal indentLevel As Long)
Dim currentFeat As SldWorks.Feature
Dim subFeat As SldWorks.Feature
Dim nextFeat As SldWorks.Feature
Dim indentStr As String
‘ インデントの生成(階層構造の視覚化)
If indentLevel > 0 Then
indentStr = String(indentLevel 4, ” “) & “├── ”
Else
indentStr = “”
End If
Set currentFeat = swFeat
‘ 兄弟フィーチャーを辿るメインループ
Do While Not currentFeat Is Nothing
‘ フィーチャー情報の取得と出力(必要に応じて拡張)
Dim featName As String
Dim featType As String
featName = currentFeat.Name
featType = currentFeat.GetTypeName2()
Debug.Print indentStr & “[” & featType & “] ” & featName
‘ 【重要】サブフィーチャー(子)が存在する場合は再帰呼び出し
Set subFeat = currentFeat.GetFirstSubFeature()
If Not subFeat Is Nothing Then
‘ 再帰的にサブツリーを掘り下げる(インデントを1つ深くする)
Call TraverseFeature(subFeat, indentLevel + 1)
End If
‘ 次の兄弟フィーチャーへポインタを移動
Set nextFeat = currentFeat.GetNextFeature()
‘ 現在のオブジェクト参照を明示的に破棄(メモリリーク防止)
Set currentFeat = Nothing
Set subFeat = Nothing
‘ 次のループへ
Set currentFeat = nextFeat
Set nextFeat = Nothing
Loop
End Sub
—
4. コードの解説とアーキテクチャの急所
このコードが他の凡百のサンプルと決定的に異なるのは、以下の設計思想に基づいているためだ。
1. `GetFirstSubFeature` と `GetNextFeature` のコンビネーション
ツリーの深さ方向への潜航は `GetFirstSubFeature` が担い、同一階層の横方向への移動は `GetNextFeature` が担当する。この二次元的なマトリクスを `Do While` ループと再帰呼出しによって完全に網羅している。
2. メモリの完全解放(`Set … = Nothing` の徹底)
VBAのループ内でCOMオブジェクトを次々に代入していくと、ポインタが上書きされる前に古い参照が宙に浮き、参照カウントが落ちないケースがある。上記のコードでは、`nextFeat` に次の参照を退避させた上で、現在の `currentFeat` を明示的に `Nothing` にクリアしている。この一手間が、数万回に及ぶループ実行時におけるメモリ消費量をフラットに保つ。
3. パフォーマンスの最適化
無駄な `Debug.Print` を極力減らし、文字列結合のオーバーヘッドを最小限に抑えている。大規模アセンブリや複雑なサーフェスモデルにおいて、このコードは一瞬でツリーを解析しきる。
—
5. 発展:システム間連携(PLM/ERP)への応用
このアルゴリズムを習得した者にとって、単なるデバッグ用のツリー出力は通過点に過ぎない。
実務においては、この巡回ロジックの内部に「特定のカスタムプロパティの検証ロジック」や「指定したフィーチャータイプ(例: `HoleWiz` や `SheetMetal`)のパラメータ抽出ロジック」を組み込むことで、社内ニッチなPLM/ERPシステムとの強力なデータブリッジを構築できる。
例えば、図面発行前に特定の加工フィーチャー(タップ穴など)の寸法が設計基準を満たしているかを自動検証し、違反があれば即座にポップアップで警告を発する「デザイン・チェッカー」のコアエンジンとして、そのまま組み込むことが可能だ。
結びに代えて
SolidWorks VBAは、APIの挙動とCOMのメモリモデルを正しく理解しさえすれば、市販のいかなる専用アドインをも凌駕するカスタム自動化ツールを生み出すポテンシャルを秘めている。
妥協のないコードを書き、CADの内部構造を完全に掌握すること――それこそが、真の自動化エンジニアの誇りである。
