SolidWorks VBAを掌握する極限の知見:`ModelDoc2.Extension.GetSelectionInfo2` を用いた選択要素の安全な参照取得
長年、SolidWorks VBAはもとより、レガシーなVB6、そして現代のVB.NETに至るまで、数多の自動化システムを世に送り出してきた。その過程で、我々が直面してきたのは、単なる機能実装の壁ではない。オブジェクトのライフサイクル、メモリの挙動、そして時として理解不能なAPIの挙動。これら全てを理解し、制御することこそが、安定したシステムを構築する唯一の道である。
特に、ユーザーインタラクションを伴うマクロ開発において、「ユーザーが今、何を選択したのか」を正確に把握し、その選択された要素を安全にコード内で扱うことは、バグの温床となりやすい部分だ。今回は、SolidWorks VBAの `ModelDoc2.Extension.GetSelectionInfo2` メソッドに焦点を当て、その真髄を掘り下げていく。
なぜ `GetSelectionInfo2` なのか? – 選択要素識別の核心
SolidWorks VBAで、ユーザーがモデル上で選択した要素(フィーチャ、面、エッジ、頂点など)を取得する手段はいくつか存在する。しかし、最も汎用的で、かつ詳細な情報を得られるのが `GetSelectionInfo2` メソッドだ。
`GetSelectionInfo2` は、単に選択されたオブジェクトを返すだけでなく、そのオブジェクトの種類、選択された数、そして場合によっては親要素の情報まで提供してくれる。これを理解せずに、例えば `ActiveSelections` プロパティなどに直接アクセスしようものなら、型ミスマッチによる予期せぬエラーに遭遇する可能性が極めて高い。
`GetSelectionInfo2` の引数と戻り値
`GetSelectionInfo2` メソッドのシグネチャは以下の通りだ。
.net
Function GetSelectionInfo2( _
ByRef SelectionType As Long, _
ByRef NumSelected As Long, _
ByRef ObjectSW As Object, _
Optional ByRef SelectedFeature As Object = Nothing, _
Optional ByRef Parent As Object = Nothing _
) As Boolean
- `SelectionType` (Long): 選択された要素のタイプを示す定数。これが我々にとって最も重要な情報源となる。
- `NumSelected` (Long): 選択された要素の数。
- `ObjectSW` (Object): 選択された要素の代表的なオブジェクト。これを適切な型にキャストして使用する。
- `SelectedFeature` (Object, Optional): 選択された要素がフィーチャの一部である場合、そのフィーチャオブジェクト。
- `Parent` (Object, Optional): 選択された要素の親オブジェクト。
`GetSelectionInfo2` は、成功した場合 `True` を、失敗した場合 `False` を返す。この戻り値を確認するだけでも、基本的なエラーハンドリングは可能だ。
`SelectionType` 定数の理解 – 選択要素の「正体」を見抜く
`SelectionType` に格納される定数は、SolidWorks APIの根幹をなす部分だ。これらを理解することが、正確な判定の鍵となる。代表的なものをいくつか挙げてみよう。
| 定数名 | 値 | 説明 |
| :————————- | :— | :—————————————– |
| `swSelERROR` | 0 | エラー |
| `swSelNONE` | 1 | 何も選択されていない |
| `swSelFEATURES` | 2 | フィーチャ(Feature) |
| `swSelPLANES` | 3 | 面(Plane) |
| `swSelEDGES` | 4 | エッジ(Edge) |
| `swSelVERTICES` | 5 | 頂点(Vertex) |
| `swSelFACES` | 6 | 面(Face) – `swSelPLANES` とは異なる場合がある |
| `swSelSKETCHPOINTS` | 7 | スケッチ点(Sketch Point) |
| `swSelSKETCHSEGMENTS` | 8 | スケッチ線分(Sketch Segment) |
| `swSelCURVES` | 9 | カーブ(Curve) |
| `swSelCOMPONENTS` | 10 | アセンブリコンポーネント(Component2) |
| `swSelBODIES` | 11 | ボディ(Body2) |
| `swSelPOINTS` | 12 | 点(Point) |
| `swSelANNOTATIONS` | 13 | 注記(Annotation) |
| `swSelBENDS` | 14 | ベンド(Bend) |
| `swSelWELDS` | 15 | ウェルド(Weld) |
| `swSelLAYERS` | 16 | レイヤー(Layer) |
| `swSelANNOTATIONTEXT` | 17 | 注記テキスト(Annotation Text) |
| `swSelREFERENCEGEOMETRY` | 18 | 参照ジオメトリ(Plane, Axis, Point, etc.) |
| `swSelEXTRUSION` | 19 | 押し出し(Extrusion) |
| `swSelREVOLVE` | 20 | 回転(Revolve) |
| `swSelSWEEP` | 21 | スイープ(Sweep) |
| `swSelLOFT` | 22 | ロフト(Loft) |
| `swSelHOLE` | 23 | 穴(Hole) |
| `swSelDATUMPLANES` | 24 | データム平面(Datum Plane) |
| `swSelDATUMAXES` | 25 | データム軸(Datum Axis) |
| `swSelDATUMPOINTS` | 26 | データム点(Datum Point) |
| `swSelSKETCHPLANES` | 27 | スケッチ平面(Sketch Plane) |
| `swSelVIEWSTATES` | 28 | ビューの状態(View State) |
| `swSelFACE_PATCH` | 29 | パッチ面(Patch Face) |
| `swSelFACE_FACEBLANKING` | 30 | 面ブランキング(Face Blanking) |
| `swSelFACE_FACEORIENT` | 31 | 面オリエント(Face Orient) |
| `swSelFACE_FACEPROTRUSION` | 32 | 面突出(Face Protrusion) |
| `swSelFACE_FACETEXTURE` | 33 | 面テクスチャ(Face Texture) |
| `swSelFACE_FACEMATERIAL` | 34 | 面マテリアル(Face Material) |
| `swSelFACE_FACECOLOR` | 35 | 面カラー(Face Color) |
| `swSelFACE_FACECOSMETIC` | 36 | 面化粧(Face Cosmetic) |
| `swSelFACE_FACEGRAPHIC` | 37 | 面グラフィック(Face Graphic) |
| `swSelFACE_FACEBODY` | 38 | 面ボディ(Face Body) |
| `swSelFACE_FACEUSER` | 39 | 面ユーザー(Face User) |
| `swSelFACE_FACEREF` | 40 | 面参照(Face Ref) |
| `swSelFACE_FACEMODEL` | 41 | 面モデル(Face Model) |
| `swSelFACE_FACEEXTRUSION` | 42 | 面押し出し(Face Extrusion) |
| `swSelFACE_FACEREVOLVE` | 43 | 面回転(Face Revolve) |
| `swSelFACE_FACELOFT` | 44 | 面ロフト(Face Loft) |
| `swSelFACE_FACEHOLE` | 45 | 面穴(Face Hole) |
| `swSelFACE_FACEBASE` | 46 | 面ベース(Face Base) |
| `swSelFACE_FACEUNDEF` | 47 | 面未定義(Face Undefined) |
| `swSelFACE_FACEBOUNDARY` | 48 | 面境界(Face Boundary) |
| `swSelFACE_FACEPROJECT` | 49 | 面投影(Face Project) |
| `swSelFACE_FACESHIFT` | 50 | 面シフト(Face Shift) |
| `swSelFACE_FACEBREP` | 51 | 面BREP(Face BREP) |
| `swSelEDGE_EDGEBASE` | 100 | エッジベース(Edge Base) |
| `swSelEDGE_EDGEORIENT` | 101 | エッジオリエント(Edge Orient) |
| `swSelEDGE_EDGEPROTRUSION` | 102 | エッジ突出(Edge Protrusion) |
| `swSelEDGE_EDGETEXTURE` | 103 | エッジテクスチャ(Edge Texture) |
| `swSelEDGE_EDGECOLOR` | 104 | エッジカラー(Edge Color) |
| `swSelEDGE_EDGECOSMETIC` | 105 | エッジ化粧(Edge Cosmetic) |
| `swSelEDGE_EDGEGRAPHIC` | 106 | エッジグラフィック(Edge Graphic) |
| `swSelEDGE_EDGEBODY` | 107 | エッジボディ(Edge Body) |
| `swSelEDGE_EDGEUSER` | 108 | エッジユーザー(Edge User) |
| `swSelEDGE_EDGEREF` | 109 | エッジ参照(Edge Ref) |
| `swSelEDGE_EDGEMODEL` | 110 | エッジモデル(Edge Model) |
| `swSelEDGE_EDGEEXTRUSION` | 111 | エッジ押し出し(Edge Extrusion) |
| `swSelEDGE_EDGEREVOLVE` | 112 | エッジ回転(Edge Revolve) |
| `swSelEDGE_EDGELOFT` | 113 | エッジロフト(Edge Loft) |
| `swSelEDGE_EDGEHOLE` | 114 | エッジ穴(Edge Hole) |
| `swSelEDGE_EDGEUNDEF` | 115 | エッジ未定義(Edge Undefined) |
| `swSelEDGE_EDGEBOUNDARY` | 116 | エッジ境界(Edge Boundary) |
| `swSelEDGE_EDGEPROJECT` | 117 | エッジ投影(Edge Project) |
| `swSelEDGE_EDGESECURE` | 118 | エッジセキュア(Edge Secure) |
| `swSelEDGE_EDGEBREP` | 119 | エッジBREP(Edge BREP) |
| `swSelVERTEX_VERTEXBASE` | 200 | 頂点ベース(Vertex Base) |
| `swSelVERTEX_VERTEXORIENT` | 201 | 頂点オリエント(Vertex Orient) |
| `swSelVERTEX_VERTEXPROTRUSION` | 202 | 頂点突出(Vertex Protrusion) |
| `swSelVERTEX_VERTEXTEXTURE` | 203 | 頂点テクスチャ(Vertex Texture) |
| `swSelVERTEX_VERTEXCOLOR` | 204 | 頂点カラー(Vertex Color) |
| `swSelVERTEX_VERTEXCOSMETIC` | 205 | 頂点化粧(Vertex Cosmetic) |
| `swSelVERTEX_VERTEXGRAPHIC` | 206 | 頂点グラフィック(Vertex Graphic) |
| `swSelVERTEX_VERTEXBODY` | 207 | 頂点ボディ(Vertex Body) |
| `swSelVERTEX_VERTEXUSER` | 208 | 頂点ユーザー(Vertex User) |
| `swSelVERTEX_VERTEXREF` | 209 | 頂点参照(Vertex Ref) |
| `swSelVERTEX_VERTEXMODEL` | 210 | 頂点モデル(Vertex Model) |
| `swSelVERTEX_VERTEXUNDEF` | 211 | 頂点未定義(Vertex Undefined) |
| `swSelVERTEX_VERTEXBREP` | 212 | 頂点BREP(Vertex BREP) |
| `swSelVERTEX_VERTEXPROJECT` | 213 | 頂点投影(Vertex Project) |
| `swSelVERTEX_VERTEXSECURE` | 214 | 頂点セキュア(Vertex Secure) |
| `swSelVERTEX_VERTEXINTERSECT` | 215 | 頂点交差(Vertex Intersect) |
注意: 上記は一部であり、SolidWorksのバージョンによって追加・変更される可能性がある。常に最新のAPIドキュメントを参照することを推奨する。
安全な参照取得のロジック – 堅牢なコードのために
では、これらの知識を基に、安全な参照取得のロジックを構築しよう。ここでは、ユーザーが単一のフィーチャ、面、またはエッジを選択した場合を想定したVBAコード例を示す。
.net
Option Explicit
‘==============================================================================
‘ Sub GetAndProcessSelectedGeometry()
‘
‘ ユーザーが選択したフィーチャ、面、エッジを安全に取得し、
‘ そのタイプに応じて処理を行うVBAマクロ。
‘
‘ [引数]
‘ なし
‘
‘ [戻り値]
‘ なし
‘
‘ [処理内容]
‘ 1. 現在アクティブなドキュメントを取得。
‘ 2. GetSelectionInfo2 を使用して、選択された要素の情報を取得。
‘ 3. 選択された要素のタイプを判定。
‘ 4. タイプに応じて、対応するオブジェクト (Feature, Face, Edge) を取得。
‘ 5. 取得したオブジェクトに対して、基本的な処理(例:名前の表示)を実行。
‘ 6. エラーハンドリングとリソース解放を徹底。
‘
‘ [注意点]
‘ – このコードは、単一の要素が選択されていることを前提としています。
‘ 複数選択や、想定外の要素が選択されている場合は、別途ハンドリングが必要です。
‘ – オブジェクトの解放は、マクロの安定稼働に不可欠です。
‘ 特に、COMオブジェクトは明示的に解放しないと、メモリリークの原因となります。
‘==============================================================================
Sub GetAndProcessSelectedGeometry()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swExtension As SldWorks.Extension
Dim swSelMgr As SldWorks.SelectionMgr
Dim lSelectionType As Long
Dim lNumSelected As Long
Dim vSelectedObject As Variant ‘ 汎用的なオブジェクトとして受け取る
Dim swFeature As SldWorks.Feature
Dim swFace As SldWorks.Face2
Dim swEdge As SldWorks.Edge
Dim bStatus As Boolean
‘ — 初期化 —
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントがありません。”, vbExclamation
Exit Sub
End If
Set swExtension = swModel.Extension
Set swSelMgr = swModel.SelectionManager
‘ — GetSelectionInfo2 の呼び出し —
‘ SelectionType: 選択された要素のタイプ
‘ NumSelected: 選択された要素の数
‘ vSelectedObject: 選択された要素の代表的なオブジェクト (Variant型で受け取る)
‘ SelectedFeature: 選択された要素がフィーチャの一部である場合のフィーチャオブジェクト
‘ Parent: 選択された要素の親オブジェクト (今回は使用しない)
bStatus = swExtension.GetSelectionInfo2(lSelectionType, lNumSelected, vSelectedObject, Nothing, Nothing)
If Not bStatus Then
MsgBox “選択情報の取得に失敗しました。”, vbCritical
‘ リソース解放
Set swSelMgr = Nothing
Set swExtension = Nothing
Set swModel = Nothing
Set swApp = Nothing
Exit Sub
End If
‘ — 選択状況の判定 —
If lNumSelected = 0 Then
MsgBox “何も選択されていません。”, vbInformation
ElseIf lNumSelected > 1 Then
MsgBox “複数の要素が選択されています。単一の要素を選択してください。”, vbExclamation
Else
‘ 単一の要素が選択されている場合
‘ — SelectionType による判定 —
Select Case lSelectionType
Case swSelFEATURES ‘ フィーチャが選択された場合
‘ vSelectedObject は Feature オブジェクトとして取得できるはず
On Error Resume Next ‘ エラー発生時に処理を続行
Set swFeature = vSelectedObject
On Error GoTo 0 ‘ エラーハンドリングを元に戻す
If Not swFeature Is Nothing Then
MsgBox “選択されたフィーチャ: ” & swFeature.Name, vbInformation
‘ ここにフィーチャに対する具体的な処理を記述
‘ 例: swFeature.Select2 True, Nothing
Else
MsgBox “フィーチャオブジェクトの取得に失敗しました。”, vbCritical
End If
Case swSelFACES, swSelPLANES, swSelREFERENCEGEOMETRY ‘ 面が選択された場合
‘ vSelectedObject は Face2 オブジェクトとして取得できるはず
On Error Resume Next
Set swFace = vSelectedObject
On Error GoTo 0
If Not swFace Is Nothing Then
MsgBox “選択された面 (Type: ” & lSelectionType & “)”, vbInformation
‘ ここに面に対する具体的な処理を記述
‘ 例: swFace.Select True
Else
MsgBox “面オブジェクトの取得に失敗しました。”, vbCritical
End If
Case swSelEDGES ‘ エッジが選択された場合
‘ vSelectedObject は Edge オブジェクトとして取得できるはず
On Error Resume Next
Set swEdge = vSelectedObject
On Error GoTo 0
If Not swEdge Is Nothing Then
MsgBox “選択されたエッジ”, vbInformation
‘ ここにエッジに対する具体的な処理を記述
‘ 例: swEdge.Select True
Else
MsgBox “エッジオブジェクトの取得に失敗しました。”, vbCritical
End If
Case Else ‘ その他の選択タイプ
MsgBox “サポートされていないタイプの要素が選択されました (Type: ” & lSelectionType & “)”, vbExclamation
End Select
End If
‘ — リソース解放 —
‘ COMオブジェクトは、使用後に明示的に解放することが重要。
‘ Set Nothing を実行することで、COMオブジェクトへの参照が解除され、
‘ マネージドコード側でガベージコレクションの対象となる。
‘ これを怠ると、メモリリークや予期せぬ動作の原因となる。
Set swFeature = Nothing
Set swFace = Nothing
Set swEdge = Nothing
Set swSelMgr = Nothing
Set swExtension = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub
パフォーマンスとメモリ最適化の極意 – レガシー環境でも生き残るために
長年VBAシステムやレガシーアーキテクチャに携わってきて痛感するのは、「パフォーマンス」と「メモリ管理」は、システム開発の根幹であり、決して後回しにできないということだ。特に、SolidWorksのようなリソースを多く消費するアプリケーションを操作するVBAマクロにおいては、その影響は顕著に現れる。
オブジェクトの明示的解放 (`Set Object = Nothing`)
上記のコード例でも繰り返し示しているが、`Set Object = Nothing` は単なる「お作法」ではない。これは、COMオブジェクトへの参照を解除し、システムリソースの解放を促すための重要な命令だ。
- メモリリークの防止: COMオブジェクトは、VBAの変数がそのオブジェクトを参照している限り、メモリ上に保持され続ける。`Nothing` を代入することで参照が解除され、不要になったオブジェクトはガベージコレクションの対象となる。これを怠ると、マクロの実行回数が増えるにつれてメモリ使用量が増加し、最終的にはSolidWorksやOS全体の動作が不安定になる可能性がある。
- リソースの早期解放: 特に、大きなデータ構造を持つオブジェクトや、ファイルハンドルを保持するオブジェクトなどは、不要になったら速やかに解放することが望ましい。
- レガシー環境への配慮: VB6などの古い環境では、COMオブジェクトのメモリ管理は現代の.NET Frameworkほど洗練されていない。そのため、明示的な解放がより一層重要となる。SolidWorks VBAも、その基盤にはCOM技術が深く関わっていることを忘れてはならない。
`GetSelectionInfo2` の利用頻度とパフォーマンス
`GetSelectionInfo2` は非常に便利なメソッドだが、API呼び出しは常に一定のオーバーヘッドを伴う。もし、マクロ内で何度も選択状態を確認する必要がある場合、無闇に `GetSelectionInfo2` を呼び出すのは避けるべきだ。
- 初回取得と変数での保持: 選択状態を複数回参照する必要がある場合は、初回に `GetSelectionInfo2` を呼び出し、取得した情報をローカル変数(`lSelectionType` や `vSelectedObject` など)に保持しておき、それらを再利用する。
- `SelectionMgr` の活用: より高度な選択操作や、複数選択に対応する場合、`SelectionMgr` オブジェクトのメソッド(`GetSelectedObjects` など)を適切に利用することも検討する。ただし、`GetSelectedObjects` は、選択されたオブジェクトの配列を返すため、その後の型判定と処理はより複雑になる。
Windows API の呼び出しとその注意点
SolidWorks VBAでは、直接Windows APIを呼び出すことは稀だが、場合によってはCOMオブジェクトの低レベルな操作や、OSレベルの情報を取得するために必要となることがある。
- `Declare` ステートメント: Windows API関数をVBAから呼び出すためには、`Declare` ステートメントを使用して関数のシグネチャを宣言する必要がある。
- データ型の対応: C言語などで定義されているAPI関数のデータ型と、VBAのデータ型とのマッピングを正確に行うことが重要。ポインタ、構造体、コールバック関数などは特に注意が必要。
- メモリ管理の複雑さ: Windows APIを直接呼び出す場合、メモリの確保・解放は開発者の責任となる。`GlobalAlloc` や `LocalAlloc` などで確保したメモリは、対応する `GlobalFree` や `LocalFree` で必ず解放しなければ、深刻なメモリリークを引き起こす。
- クロスプラットフォーム非互換性: Windows APIはOS依存であるため、作成したマクロはWindows環境以外では動作しない。
- レガシーシステムとの親和性: 既存のレガシーシステムがWindows APIを多用している場合、VBAマクロからそれらを連携させるために、API呼び出しが必要になることがある。しかし、APIの仕様変更や、OSのアップデートによって互換性が失われるリスクも常に考慮する必要がある。
結論として、Windows APIの直接呼び出しは、その複雑さとリスクを十分に理解した上で、他に手段がない場合に限定すべきである。SolidWorks VBAのAPIで提供されている機能で実現できるのであれば、そちらを優先するべきだ。
システム間連携の極限 – SolidWorks VBA をハブとして
SolidWorks VBAは、単にSolidWorks内の操作を自動化するだけではない。その強力なAPIと、Windows環境での動作という特性を活かせば、他のシステムとの連携のハブとしても機能させることができる。
- ファイルI/Oとデータベース連携: VBAはファイル操作(読み書き、コピー、削除など)や、ADO (ActiveX Data Objects) を介したデータベースアクセスも可能だ。これにより、SolidWorksの設計データ(部品表、図面情報など)を外部データベースに登録したり、外部データに基づいてSolidWorksモデルを自動生成したりすることができる。
- 他のアプリケーションとの連携 (COM/OLE Automation): Excel、Access、OutlookなどのMicrosoft Officeアプリケーションはもちろん、COMインターフェースを公開している他のアプリケーションとも連携できる。例えば、Excelで作成したパラメータリストから、自動的にSolidWorksのパーツを生成・更新するといったワークフローが実現可能だ。
- Webサービス/API連携 (限定的): VBAから直接Webサービス(REST APIなど)を呼び出すのは、標準機能だけでは少し手間がかかる。`MSXML2.XMLHTTP` オブジェクトなどを使用すれば可能だが、エラーハンドリングやデータ形式(JSON/XML)のパースは、VB.NETなどに比べると煩雑になる。より高度なWeb連携が必要な場合は、VB.NETやC#で開発し、それをSolidWorks VBAから呼び出す、といったアーキテクチャも検討に値する。
- レガシーシステムとの橋渡し: 現代のシステムから見れば「レガシー」であっても、企業活動においては依然として重要な役割を果たしているシステムは多い。SolidWorks VBAは、これらのレガシーシステムが提供するインターフェース(ファイル出力、COMなど)を利用して、現代的なシステムと連携させるための「橋渡し役」として、その価値を発揮することがある。
連携における注意点 – 「境界」を意識する
システム間連携においては、各システム(SolidWorks、データベース、他のアプリケーション)のAPI仕様、データ形式、エラーハンドリング、そして「境界」を正確に理解することが極めて重要だ。
- データ変換: システム間でデータをやり取りする際には、データ型の変換や、単位の整合性、文字コードの変換などが必要になる場合がある。
- トランザクション管理: データベース連携などでは、一連の処理が成功した場合のみコミットし、失敗した場合はロールバックするといった、トランザクション管理を考慮する必要がある。
- エラーハンドリングの連鎖: あるシステムで発生したエラーが、連携している他のシステムにも影響を及ぼす可能性がある。各システムのエラーを適切に捕捉し、上位システムへ通知する仕組みが重要だ。
- パフォーマンスボトルネック: 連携処理は、個々のシステム内の処理よりも遅くなる傾向がある。どの部分がボトルネックになっているのかを特定し、最適化を図ることが求められる。
まとめ – 選択情報の正確な把握が、安定システムへの第一歩
`ModelDoc2.Extension.GetSelectionInfo2` は、SolidWorks VBAによる自動化において、ユーザーの意図を正確に汲み取るための強力なツールである。その `SelectionType` を正しく判別し、取得したオブジェクトを安全に参照することで、型ミスマッチによるエラーを回避し、堅牢なマクロの基盤を築くことができる。
そして、我々が目指すべきは、単に動くコードを書くことではない。メモリリークに怯えることなく、レガシー環境でも安定稼働し、さらには他のシステムとも円滑に連携できる、「真に価値のある自動化」を実現することだ。そのために、オブジェクトのライフサイクル、APIの挙動、そしてシステム間の「境界」を深く理解し、常に最善のアーキテクチャを追求し続けることが、我々エンジニアの責務なのである。
この知見が、あなたのSolidWorks VBA開発の一助となれば幸いだ。
