【実務・中級編】【実務効率化】GetSelectedObject6を使った動的選択オブジェクトの型判定とエラーハンドリングの基礎 – SolidWorks VBA解析バイブル

スポンサーリンク

現場のエンジニアへ:その「生ポインタ」のままの選択操作、明日も動きますか?

SolidWorksマクロ開発において、ユーザーに画面上で「面」や「エッジ」を選択させ、それをトリガーにして後続の処理を走らせるUIは極めて一般的だ。

しかし、初心者が書いた、あるいはネットの断片的なコードを継ぎ接ぎしたマクロの多くは、実務の現場であっさり破綻する。
「ユーザーが円柱を選んでほしいのに、平面を選んでフリーズした」
「何も選択せずにボタンを押したら、ランタイムエラーでSolidWorksごと強制終了した」

――こうした現場の悲劇を根絶するために、我々チーフアーキテクトが用いるべきアプローチが `GetSelectedObject6` を駆使した動的型判定と、鉄壁のエラーハンドリング である。

今回は、実務でそのまま使える、極限まで堅牢性を高めた設計思想とプロダクションコードを伝授する。

1. なぜ「雑な選択取得」はバグの温床になるのか?

多くの開発者は、選択されたオブジェクトを `SldWorks.ModelDocExtension.GetSelectedObject6` で取得した後、甘い判定で処理を進める。

ここでSolidWorks APIの残酷な真実を思い出してほしい。
SolidWorksの選択バッファには、面(Face2)、エッジ(Edge)、スケッチエンティティ、さらにはコンポーネントまで、あらゆるものが混在して格納される。

‘ 【悪手】型を確かめずにいきなりキャストする例
Dim swFace As Face2
Set swFace = swModel.Extension.GetSelectedObject6(1, -1) ‘ ユーザーがエッジを選んでいたら即死

このコードは、ユーザーが意図しないエンティティを選択した瞬間に型ミスマッチエラーを引き起こす。さらに、APIが返すオブジェクトのポインタ寿命や、メモリリークの危険性を考慮していないコードは、長時間の連続稼働でSolidWorksを不安定にする。

プロのエンジニアであれば、「何が選択されているか分からない」という前提に立ち、VBAの `TypeName` 関数やCOMオブジェクトの型安全性を利用して厳密にバリデーション(検証)するべきだ。

2. 堅牢な設計:3つの鉄則

実務向けの自動化ツールを構築する際、以下の3原則を必ず死守せよ。

1. 事前カウンティング (`GetSelectionCount`): そもそもユーザーが何も選択していない状態、あるいは複数選択しすぎている状態を最初に弾く。
2. 安全な型キャストと `TypeName` 判定: 取得した `Object` 型のインスタンスが、本当に `Face2` なのか、さらに言えば「円柱面」なのかをプログラム側で能動的に判定する。
3. 明示的な参照解放: COMオブジェクトの参照をローカル変数に残したままにせず、処理の終わりに `Set obj = Nothing` で解放し、メモリ肥大化を防ぐ。

3. 【プロダクションコード】動的型判定&エラーハンドリング実装例

以下のコードは、ユーザーが選択したオブジェクトが「有効な面(Face2)」であるかを検証し、さらにそれが「円柱面(Cylindrical Face)」であるかどうかを数学的・API的に判定する実務レベルのモジュールである。

Option Explicit

‘ ==============================================================================
‘ 担当領域: パーツファイル・ジオメトリ動的判定
‘ 概要: ユーザーが選択したエンティティの型を安全に判定し、円柱面以外を排除する
‘ ==============================================================================
Sub ExecuteCylindricalFaceValidator()

Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swSelMgr As SldWorks.SelectionMgr
Dim swEntity As SldWorks.Entity
Dim swFace As SldWorks.Face2
Dim swSurface As SldWorks.Surface
Dim selCount As Long

‘ 1. アプリケーションおよびドキュメントの取得
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc

If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “致命的エラー”
Exit Sub
End If

‘ 2. セレクションマネージャーの取得
Set swSelMgr = swModel.SelectionManager
selCount = swSelMgr.GetSelectionCount

‘ 3. 選択数の事前バリデーション
If selCount = 0 Then
MsgBox “対象となる面が選択されていません。面を1つ選択して実行してください。”, vbExclamation, “入力エラー”
Exit Sub
End If

If selCount > 1 Then
MsgBox “選択数が多すぎます。1つの面のみを選択してください。”, vbExclamation, “入力エラー”
Exit Sub
End If

‘ 4. GetSelectedObject6 によるオブジェクトの安全な取得 (Mark= -1 はすべてのマークを対象とする)
‘ ※ 第1引数に選択インデックス(1)、第2引数にマーク(-1)を指定
Set swEntity = swSelMgr.GetSelectedObject6(1, -1)

If swEntity Is Nothing Then
MsgBox “オブジェクトの取得に失敗しました。ポインタが無効です。”, vbCritical, “APIエラー”
GoTo CleanUp
End If

‘ 5. TypeName による動的型判定(実務で最も確実な手法)
If TypeName(swEntity) <> “Face2” Then
MsgBox “選択されたオブジェクトは「面」ではありません。” & vbCrLf & _
“現在の選択型: ” & TypeName(swEntity), vbCritical, “型不一致エラー”
GoTo CleanUp
End If

‘ Face2 インターフェースへの安全なキャスト
Set swFace = swEntity

‘ 6. ジオメトリの深層判定:サーフェスタイプが「円柱」であるか?
Set swSurface = swFace.GetSurface
If swSurface Is Nothing Then
MsgBox “サーフェス情報の取得に失敗しました。”, vbCritical, “ジオメトリエラー”
GoTo CleanUp
End If

‘ SurfaceType 判定 (Cylindrical Surface の定数は 14 / swSurfaceTypeEnum.SE_CYLINDER)
‘ ※可読性と保守性を考慮し、直接定数または列挙体を使用
Const SURFACE_TYPE_CYLINDER As Long = 14

If swSurface.Identity <> SURFACE_TYPE_CYLINDER Then
MsgBox “選択された面は円柱面ではありません。” & vbCrLf & _
“目的のジオメトリ(穴や軸)を選択してください。”, vbExclamation, “ジオメトリ判定”
GoTo CleanUp
End If

‘ ==============================================================================
‘ ここから先は「安全に検証された円柱面」に対するコアロジックを記述
‘ ==============================================================================
MsgBox “検証成功:正しく円柱面が選択されました。後続処理を実行します。”, vbInformation, “成功”

CleanUp:
‘ 7. メモリリークを防ぐための参照解放(プロの作法)
Set swFace = Nothing
Set swEntity = Nothing
Set swSelMgr = Nothing
Set swModel = Nothing
Set swApp = Nothing

End Sub

4. チーフアーキテクトからの実践アドバイス

このコードをそのまま導入するだけで、ユーザーの誤操作によるヘルプデスクへの問い合わせや、原因不明のクラッシュを劇的に減らすことができる。

  • `TypeName` の優位性: VBAにおいて `TypeOf … Is` も有効だが、COMコンポーネントを扱うSolidWorks API環境下では、`TypeName(swEntity)` で文字列として厳密にクラス名(`Face2` や `Edge` など)を評価する方が、予期せぬランタイムエラーを回避しやすい。
  • 拡張性: 今回紹介した `Surface.Identity` の判定ロジックを応用すれば、平面(Planar)、円錐(Conical)、スプライン面などを動的に仕分けるルーター機構が簡単に作れる。

「動けばいいや」という妥協を捨て、堅牢なエラーハンドリングに裏打ちされた美しいコードベースを築き上げてほしい。君たちの開発プロジェクトの成功を祈る。

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