【SolidWorks VBA極限解説】GetSelectedObject6による動的選択オブジェクトの型判定と実戦的エラーハンドリング
シニアエンジニア、そして現場の自動化を極めんとする者たちよ。
日々の設計業務において、マクロが「予期せぬオブジェクトの選択」によって沈黙する、あるいはVBA特有のランタイムエラーを吐き出してクラッシュする悪夢に直面したことはないだろうか。
特に、ユーザーにGUI上で面やエッジを選択させ、それをトリガーにジオメトリを生成・操作するインタラクティブなマクロにおいて、「選択されたものが何であるか」の動的判定は、システムの実用性を左右する最大の分水嶺である。
今回は、`SldWorks.ModelDoc2::GetSelectedObject6` を駆使し、COMの深淵を覗きながら、堅牢かつ高速な型判定とエラーハンドリングの実装パターンを、チーフアーキテクトの視点から余すところなく解説する。
—
1. 選択オブジェクトの背後にあるCOMの真実
SolidWorks APIにおける選択メカニズムは、一見シンプルに見える。しかし、その内部ではISelectionMgr(選択マネージャー)が複雑なポインタとインデックスを管理している。
`GetSelectedObject6` は、選択リストの指定インデックスにあるオブジェクトへの参照を取得するための最も強力なメソッドだが、ここで大きな誤解が生じやすい。
「`GetObject`のように、取得したオブジェクトの型をVBAの `TypeOf` や `TypeName` で判定すれば十分だろう」——甘い。
VBAの `TypeName` はCOMオブジェクトのラッパー層を跨ぐため、遅延バインディング的なオーバーヘッドを生むだけでなく、SolidWorks特有の「一時オブジェクト(Transient Geometry)」や「コンテキスト依存のトポロジ」を正確に射抜けない。さらに、メモリリークの温床となるCOMポインタの解放漏れが、長時間のセッションでSolidWorks本体を不安定化させる。
真に堅牢なシステムを構築するためには、以下の3点を遵守しなければならない。
1. `Mark`(マスク)値による事前フィルタリング(APIに無駄な仕事をさせない)
2. `ObjectType2` 列挙体(SwSelectableObjectType_e)による厳密な型確認
3. 明示的なオブジェクト解放(`Set obj = Nothing`)によるメモリ管理
—
2. 実装パターン:動的型判定と鉄壁のエラーハンドリング
以下に、実務の現場でそのまま稼働できるプロダクション品質のコードを示す。
ユーザーが選択したエンティティが「平面(Planar Face)」であるか「円柱面(Cylindrical Face)」であるかを動的に判定し、それぞれに応じた処理へと分岐させるテンプレートだ。
Option Explicit
‘ ——————————————————————————–
‘ メインプロシージャ:選択オブジェクトの動的判定とエラーハンドリング
‘ ——————————————————————————–
Sub ExecuteDynamicSelectionHandler()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swSelMgr As SldWorks.SelectionMgr
Dim swSelObj As Object
Dim swFace As SldWorks.Face2
Dim swSurf As SldWorks.Surface
Dim selType As Long
Dim mark As Long
‘ 1. アプリケーションおよびドキュメントの取得
Set swApp = Application.SldWorks
If swApp Is Nothing Then Exit Sub
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “API Error”
Exit Sub
End If
‘ 2. 選択マネージャーの取得
Set swSelMgr = swModel.SelectionManager
If swSelMgr Is Nothing Then
Call CleanUp(swSelMgr, swModel, swApp)
Exit Sub
End If
‘ 3. 選択数の検証(1つだけ選択されていることを前提とする)
If swSelMgr.GetSelectedObjectCount2(-1) <> 1 Then
MsgBox “対象のエンティティを正確に1つだけ選択してください。”, vbExclamation, “Validation Error”
Call CleanUp(swSelMgr, swModel, swApp)
Exit Sub
End If
‘ 4. GetSelectedObject6によるオブジェクト取得と型情報の抽出
‘ ※ 第1引数: インデックス(1), 第2引数: マーク(今回は無視するため-1)
Set swSelObj = swSelMgr.GetSelectedObject6(1, -1)
If swSelObj Is Nothing Then
MsgBox “選択オブジェクトのポインタ取得に失敗しました。”, vbCritical, “Memory Error”
Call CleanUp(swSelMgr, swModel, swApp)
Exit Sub
End If
‘ 5. 選択オブジェクトのタイプを数値で高速判定 (SwSelectableObjectType_e)
selType = swSelMgr.GetSelectedObjectType3(1, -1)
‘ エラーハンドリングブロック(トランザクション保護)
On Error GoTo ErrorHandler
Select Case selType
Case swSelFACES ‘ 選択されたのが「面」である場合
Set swFace = swSelObj
Set swSurf = swFace.GetSurface
If swSurf.IsPlane Then
‘ — 平面の場合の処理 —
Call ProcessPlanarFace(swFace)
ElseIf swSurf.IsCylinder Then
‘ — 円柱面の場合の処理 —
Call ProcessCylindricalFace(swFace)
Else
MsgBox “サポートされていない面形状です(B-Repサーフェス)。”, vbInformation, “Type Mismatch”
End If
Case swSelEDGES ‘ 選択されたのが「エッジ」である場合
‘ — エッジ特有の処理をここに記述 —
MsgBox “エッジが選択されました。このマクロでは面を選択してください。”, vbInformation, “Validation”
Case Else
MsgBox “未知のオブジェクトタイプです。Type ID: ” & selType, vbExclamation, “Unsupported”
End Select
CleanExit:
‘ 6. 確実なリソース解放(メモリリークの根絶)
Call CleanUp(swSurf, swFace, swSelObj, swSelMgr, swModel, swApp)
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error Description: ” & Err.Description, vbCritical, “Critical Runtime Error”
Resume CleanExit
End Sub
‘ ——————————————————————————–
‘ サブ処理:平面に対するロジック
‘ ——————————————————————————–
Private Sub ProcessPlanarFace(ByRef face As SldWorks.Face2)
‘ 業務ロジックをここに実装
Debug.Print “平面を検知しました。面積: ” & face.GetArea
End Sub
‘ ——————————————————————————–
‘ サブ処理:円柱面に対するロジック
‘ ——————————————————————————–
Private Sub ProcessCylindricalFace(ByRef face As SldWorks.Face2)
Dim swSurf As SldWorks.Surface
Dim param As Variant
Set swSurf = face.GetSurface
param = swSurf.CylinderParams
‘ param(0)-(2): 軸上の点, param(3)-(5): 軸方向ベクトル, param(6): 半径
Debug.Print “円柱面を検知しました。半径: ” & param(6) 1000 & ” mm”
Set swSurf = Nothing
End Sub
‘ ——————————————————————————–
‘ メモリ最適化:COMオブジェクトの参照を完全に断ち切るクリーンアップルーチン
‘ ——————————————————————————–
Private Sub CleanUp(ParamArray objs() As Variant)
Dim i As Long
For i = LBound(objs) To UBound(objs)
If IsObject(objs(i)) Then
Set objs(i) = Nothing
End If
Next i
End Sub
—
3. チーフアーキテクトが指摘する「見落としがちな罠」
上記のコードには、単なる「動くサンプル」を超えた、現場で生き残るための知見が凝縮されている。
① `GetSelectedObjectType3` と `GetSelectedObject6` のコンビネーション
VBAにおいて、オブジェクトのキャスト(`Set swFace = swSelObj`)を行う前に、必ず `GetSelectedObjectType3` で型ID(`swSelFACES` など)を検証すべきである。
不適切な型キャストは、VBAランタイム上で「型が一致しません(Error 13)」を引き起こし、最悪の場合、SolidWorksのプロセスを巻き込んで異常終了する。APIの境界線では、常に「疑ってかかる」防御的プログラミングが必須だ。
② B-Rep(Boundary Representation)の評価コスト
面(`Face2`)を取得した後、それが平面なのか円柱なのかを判定するために `GetSurface` を呼び出し、さらに `IsPlane` や `IsCylinder` メソッドを叩いている。
サーフェス評価は内部でジオメトリカーネル(Parasolid)を叩くため、極めて重い処理である。もしアセンブリの深層や複雑なインポートモデルでこれを大量に行うと、マクロのパフォーマンスが劇的に低下する。
したがって、「必要な時だけ `GetSurface` を呼び出し、使い終わったら即座に `Nothing` を代入して解放する」 というライフサイクル管理が、プロフェッショナルのコードの絶対条件となる。
③ 可変長引数(ParamArray)による一括メモリ解放
VBAのVBE(Visual Basic Environment)のガベージコレクタは非常に気まぐれだ。特にCOMオブジェクトの参照カウント(Reference Counting)は、スコープを抜けただけでは即座に解放されないことが多々ある。
上記の `CleanUp` サブルーチンでは、`ParamArray` を用いて複数のオブジェクト参照を一度に `Nothing` クリアする設計にしている。これにより、長時間のバッチ処理やループ処理であっても、メモリ使用量が肥大化する(メモリリークを起こす)現象を完全に抑制できる。
—
総括
SolidWorks VBAにおける自動化の成否は、「APIをただ叩くこと」ではなく、「COMのライフサイクルとメモリの物理法則をコントロールすること」に他ならない。
今回解説した `GetSelectedObject6` を軸とした動的型判定とエラーハンドリングのパターンを血肉化し、あなたの設計現場から「理由不明のフリーズ」と「人為的誤操作によるクラッシュ」を根絶してほしい。
技術の限界を突破するのは、いつだって妥協なきエンジニアの知見である。
