SolidWorks VBAを掌握する極限の知見:ModelDoc2の基本とインターフェース・キャストの極意
SolidWorks APIの開発において、すべての起点は `SldWorks.ModelDoc2` オブジェクトにある。
アクティブなドキュメントを取得する `IActiveDoc2` であれ、ファイルを開く `OpenDoc6` であれ、返却されるのはこの汎用インターフェースだ。
しかし、シニアエンジニアであれば誰もが知っている通り、`ModelDoc2` はあくまで「共通の皮をかぶった器」に過ぎない。
部品(Part)固有のフィーチャー操作、アセンブリ(Assembly)固有の合致(Mate)制御、そして図面(Drawing)固有のビューやシート操作を行おうとした時、`ModelDoc2` のままではAPIの海に溺れることになる。
今回は、この `ModelDoc2` から各専用インターフェース(`PartDoc`、`AssemblyDoc`、`DrawingDoc`)への安全かつ高速なキャスト(型変換)と、メモリ管理、そしてレガシー環境における極限の最適化手法を解説する。
—
1. なぜ「キャスト」が必要なのか? ― オブジェクトモデルの裏側
SolidWorksのCOMアーキテクチャにおいて、APIはインターフェース指向で設計されている。
`ModelDoc2` は3つのドキュメントタイプ(Part, Assembly, Drawing)の共通項をまとめたものであり、それぞれの固有機能は派生インターフェースに隠蔽されている。
VBAは動的型付け言語のように見えるが、COMオブジェクトを扱う際は裏でV-table(仮想メソッドテーブル)を引いている。
不適切なインターフェースのままメソッドを叩けば、実行時エラー `-2147467259 (Automation error)` や、最悪の場合 SolidWorks ごとのプロセスöll(強制終了)を招く。
ここで、確実かつ安全に型を判定し、キャストを行うためのパターンを示す。
厳密な型判定とキャストの実装パターン
以下のコードは、現在アクティブなドキュメントのタイプを安全に判別し、それぞれの専用インターフェースにダウンキャストして固有の処理を実行する実用的なテンプレートである。
‘ ==============================================================================
‘ 担当: チーフアーキテクト
‘ 概要: ModelDoc2から各ドキュメント専用インターフェースへの安全なキャストと制御
‘ ==============================================================================
Sub ExecuteTypeSafeOperation()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim docType As Long
‘ アプリケーションインスタンスの取得(GetLWContext等の過剰なラッパーは排除し直接取得)
Set swApp = Application.SldWorks
If swApp is Nothing Then Exit Sub
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical
Exit Sub
End If
‘ ドキュメントタイプの取得
docType = swModel.GetType()
Select Case docType
Case swDocPART
Call ProcessPartDocument(swModel)
Case swDocASSEMBLY
Call ProcessAssemblyDocument(swModel)
Case swDocDRAWING
Call ProcessDrawingDocument(swModel)
Case Else
MsgBox “未対応のドキュメントタイプです。”, vbExclamation
End Select
‘ 参照の明示的な解放(メモリリーク防止の鉄則)
Set swModel = Nothing
Set swApp = Nothing
End Sub
Private Sub ProcessPartDocument(ByVal swModel As SldWorks.ModelDoc2)
Dim swPart As SldWorks.PartDoc
‘ Implicit(暗黙的)なキャストではなく、型安全な代入
Set swPart = swModel
If swPart Is Nothing Then
MsgBox “PartDocへのキャストに失敗しました。”, vbCritical
Exit Sub
End If
‘ 【部品固有の処理】例: ユーザー定義プロパティの取得やボディ数のカウントなど
Debug.WriteLine “部品ドキュメントを処理中: ” & swModel.GetPathName()
‘ オブジェクトの解放
Set swPart = Nothing
End Sub
Private Sub ProcessAssemblyDocument(ByVal swModel As SldWorks.ModelDoc2)
Dim swAssy As SldWorks.AssemblyDoc
Set swAssy = swModel
If swAssy Is Nothing Then Exit Sub
‘ 【アセンブリ固有の処理】例: コンポーネントの強制再構築
Debug.WriteLine “アセンブリドキュメントを処理中…”
swAssy.ResolveAllLightWeightComponents False ‘ 軽量コンポーネントの解決
Set swAssy = Nothing
End Sub
Private Sub ProcessDrawingDocument(ByVal swModel As SldWorks.ModelDoc2)
Dim swDraw As SldWorks.DrawingDoc
Set swDraw = swModel
If swDraw Is Nothing Then Exit Sub
‘ 【図面固有の処理】例: シート情報の取得
Debug.WriteLine “図面ドキュメントを処理中…”
Set swDraw = Nothing
End Sub
—
2. メモリ最適化とCOMオブジェクトのライフサイクル管理
VBAによるSolidWorksマクロ開発で最も頻発する障害は、メモリリークとそれに起因する「SolidWorksの動作不安定化」である。
VBAのガーベージコレクションはCOMオブジェクトの参照カウント(Reference Counting)を完全に管理しきれないケースがある。特にループ内で `Extension` や `Feature` オブジェクトを大量に生成・破棄する場合、明示的な解放を行わないと瞬く間にメモリが肥大化する。
鉄則:オブジェクト変数の即座の `Nothing` 代入
ループ処理やスコープの抜け際において、使用したオブジェクト変数には必ず `Set xxx = Nothing` を明示すること。
‘ 悪例(メモリリークの温床)
Dim i As Long
For i = 0 To 1000
Dim swFeat As SldWorks.Feature
Set swFeat = swModel.FirstFeature()
‘ 処理…
‘ swFeatをNothingにせずループを回すと、参照が残る場合がある
Next i
‘ ─── 正しい極限最適化パターン ───
Dim swFeat As SldWorks.Feature
Dim swNextFeat As SldWorks.Feature
Set swFeat = swModel.FirstFeature()
Do While Not swFeat Is Nothing
‘ フィーチャー名取得などの処理
Debug.WriteLine swFeat.Name
‘ 次のフィーチャーへ
Set swNextFeat = swFeat.GetNextFeature()
‘ 現在の参照を即座に破棄
Set swFeat = Nothing
Set swFeat = swNextFeat
Loop
Set swNextFeat = Nothing
この「次を取得してから現行を破棄する」イディオムは、大規模なアセンブリツリーや複雑なフィーチャーツリーを走査する際、メモリ消費量をフラットに保つための必須テクニックである。
—
3. レガシー環境とシステム間連携におけるリスクヘッジ
社内ニッチなPDMシステムや、Excel/Access等からの外部VBA連携(Out-of-Process Automation)において、`ModelDoc2` のキャストとドキュメント切り替えはさらに慎重さを要求される。
外部プロセスから `SldWorks.Application` を起動、あるいは接続する場合、SolidWorksのUIスレッドとVBA側のスレッド境界でMarshaling(マーシャリング)が発生する。ここでドキュメントのロード状態が完全に完了する前にキャストを試みると、キャッチしにくいCOM例外が発生する。
堅牢な非同期・同期ロード待機パターン
Public Function OpenAndCastDocument(ByVal filePath As String, ByRef outPart As SldWorks.PartDoc) As Boolean
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim errors As Long
Dim warnings As Long
Set swApp = Application.SldWorks
‘ ドキュメントをサイレント、または通常オープン
Set swModel = swApp.OpenDoc6(filePath, swDocPART_PAZ, swOpenDocOptions_Silent, “”, errors, warnings)
If swModel Is Nothing Then
‘ エラーハンドリング(errorsの値に応じた詳細ログ出力)
OpenAndCastDocument = False
Exit Function
End If
‘ 型安全性の厳密な検証
If swModel.GetType() <> swDocPART Then
MsgBox “指定されたファイルは部品ではありません。”, vbCritical
‘ 開いてしまったドキュメントを閉じる
swApp.CloseDoc swModel.GetTitle()
OpenAndCastDocument = False
Exit Function
End If
‘ キャスト実行
Set outPart = swModel
OpenAndCastDocument = True
‘ swModel自体の参照は不要(outPartが保持しているため)だが、スコープ変数の整理として
Set swModel = Nothing
Set swApp = Nothing
End Function
—
4. チーフアーキテクトからの提言
SolidWorks VBAは「動けばいい」というフェーズを過ぎると、保守性とパフォーマンスの壁に突き当たる。
`ModelDoc2` から各専用インターフェースへの適切なキャスト、徹底的なオブジェクトのライフサイクル管理(`Nothing` 詰めの徹底)、そしてドキュメントタイプの事前検証。これらをコードの標準(コーディング規約)として組み込むことこそが、数千部品を超える巨大アセンブリを操る安定した自動化システムの基盤となる。
安易なバインドや、型を無視したメソッド呼び出しは百害あって一利なしだ。
APIの仕様書(API Help)の奥底にあるインターフェース階層を常に意識し、堅牢でエレガントなコードを紡ぎ出してほしい。
