【実務・中級編】【上級プロ】レガシーなVBAプロシージャをクラスモジュールベースの「アセンブリ自動化フレームワーク」へモダンリファクタリング – SolidWorks VBA解析バイブル

スポンサーリンク

【上級プロ】レガシーなVBAプロシージャをクラスモジュールベースの「アセンブリ自動化フレームワーク」へモダンリファクタリング

こんにちは。大規模なSolidWorks設計自動化プロジェクトをいくつも統括してきたチーフアーキテクトだ。

現場のVBAエンジニアから、よくこんな悲痛な叫びを聞く。

  • 「以前の担当者が書いたアセンブリ自動化マクロが、3000行のスパゲッティコードになっていて誰も直せない」
  • 「ちょっと合致(Mate)の条件を変えただけで、あちこちで`.Select2`エラー(COM例外)が頻発する」
  • 「どの部品をどこに配置しているのか、変数スコープがグチャグチャでデバッグに何日もかかる」

君が今書いている、あるいは直そうとしているそのコード、標準モジュールに延々と`SldWorks.ModelDoc2`や`SldWorks.AssemblyDoc`の操作手続きを書き並べていないか?

モデリングやアセンブリの自動化において、「手続き型(Procedural)」の限界は早い段階で訪れる。SolidWorks COM APIは状態(State)に極めて依存しており、選択状態のクリア漏れ、ドキュメントのポインタズレ、メモリリークが即座にクラッシュや不正なアセンブリを生む。

今回は、散らかったレガシーマクロを捨て、オブジェクト指向設計に基づいた「アセンブリ自動化フレームワーク」へ刷新する極限のテクニックを伝授する。

—

1. なぜ「手続き型」のSolidWorks VBAは破綻するのか?

レガシーなVBAコードの典型例を見てみよう。

‘ 【アンチパターン】典型的なスパゲッティ・プロシージャ
Sub AddPartLegacy()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc

‘ ファイルを開く
Dim partPath As String
partPath = “C:\Parts\Bracket.sldprt”
Dim err As Long, warn As Long
Set swModel = swApp.OpenDoc6(partPath, 1, 0, “”, err, warn)

‘ 選択してアセンブリに挿入(この時点でアクティブドキュメントのコンテキストが狂う)
swModel.Extension.SelectByID2 “Face1”, “FACE”, 0, 0, 0, False, 0, Nothing, 0
‘ … 延々と続くカオスなAPI呼び出し
End Sub

このアプローチの何が致命的か?
1. コンテキストの暗黙的依存: `ActiveDoc`や`Extension.SelectByID2`は、SolidWorksの「画面上の選択状態」という極めて不安定なグローバルステートに依存している。UIが少しでも動いたり、他のウィンドウにフォーカスが移ると一瞬で破綻する。
2. エラーハンドリングの欠如: APIが失敗した後のポインタ解放(Release)やクリーンアップがなされないため、COMコンポーネントがメモリ上に残り続け、SolidWorks自体がフリーズする。
3. 拡張性のゼロ: ボルトやブラケットを追加・配置するロジックがハードコーディングされており、パラメータ変更に耐えられない。

プロフェッショナルな自動化エンジニアなら、アセンブリを「コンポーネント(部品)というオブジェクトの集合体」として捉え、データと操作をカプセル化すべきだ。

—

2. クラスモジュールによるモダン設計の全体像

今回構築するフレームワークは、以下のアーキテクチャで設計する。

[Main Standard Module] (エントリーポイント)
│
▼
[AssemblyContext クラス] (アセンブリ全体のライフサイクル管理・トランザクション保証)
│
├─► [ComponentItem クラス] (個別パーツのメタデータ・配置マトリクス管理)
│
└─► [MateManager クラス] (合致定義の抽象化・セーフ実行ラッパー)

この設計により、メインの処理は「何を、どこに、どう組むか」というビジネスロジックだけに純化され、SolidWorks APIの泥臭い操作はすべてクラス内部に隠蔽(カプセル化)される。

—

3. 実装コード:プロダクション・フレームワーク

以下のコードは、実際の現場でそのまま動かせる、堅牢性を極限まで高めたクラスモジュールおよび標準モジュールの実装だ。

① クラスモジュール: `ComponentItem` (配置パーツの抽象化)

パーツのファイルパス、コンフィギュレーション、そして配置座標(トランスフォーメーション)を保持する。

‘ ==========================================
‘ クラスモジュール名: ComponentItem
‘ ==========================================
Option Explicit

Private m_FilePath As String
Private m_Configuration As String
Private m_X As Double, m_Y As Double, m_Z As Double
Private m_ComponentRef As SldWorks.Component2

Public Property Let FilePath(ByVal Value As String): m_FilePath = Value: End Property
Public Property Get FilePath() As String: FilePath = m_FilePath: End Property

Public Property Let Configuration(ByVal Value As String): m_Configuration = Value: End Property
Public Property Get Configuration() As String: Configuration = m_Configuration: End Property

Public Sub SetPosition(ByVal X As Double, ByVal Y As Double, ByVal Z As Double)
m_X = X: m_Y = Y: m_Z = Z
End Sub

‘ パーティションをアセンブリに挿入する実処理
Friend Function Insert(ByVal swAssyModel As SldWorks.ModelDoc2) As Boolean
Dim swAssyDoc As SldWorks.AssemblyDoc
Set swAssyDoc = swAssyModel

Dim errCode As Long
‘ 画面選択に依存せず、直接コンポーネントをアセンブリに追加
Set m_ComponentRef = swAssyDoc.AddComponent5( _
m_FilePath, _
m_Configuration, _
“”, False, “”, _
m_X, m_Y, m_Z, _
errCode)

If errCode <> 0 Then
MsgBox “パーツの挿入に失敗しました: ” & m_FilePath & ” (Error: ” & errCode & “)”, vbCritical
Insert = False
Exit Function
End If

Insert = True
End Function

Public Property Get ComponentRef() As SldWorks.Component2
Set ComponentRef = m_ComponentRef
End Property

② クラスモジュール: `MateManager` (合致定義の安全化)

レガシーコードで最もバグを生む「面やエッジの選択と合致(Mate)」を、オブジェクト指向で安全にラップする。

‘ ==========================================
‘ クラスモジュール名: MateManager
‘ ==========================================
Option Explicit

Private m_SwApp As SldWorks.SldWorks

Public Sub Initialize(ByVal swApp As SldWorks.SldWorks)
Set m_SwApp = swApp
End Sub

‘ 2つのエンティティ間に「同軸(Coaxial)」合致を定義する
Public Function AddCoaxialMate(ByVal comp1 As SldWorks.Component2, ByVal entityName1 As String, _
ByVal comp2 As SldWorks.Component2, ByVal entityName2 As String) As Boolean
AddCoaxialMate = False

‘ 1つ目のエンティティを選択
If Not SelectEntityByComponent(comp1, entityName1, False) Then Exit Function
‘ 2つ目のエンティティを選択(Append = True)
If Not SelectEntityByComponent(comp2, entityName2, True) Then
m_SwApp.ActiveDoc.ClearSelection2 True
Exit Function
End If

Dim swModel As SldWorks.ModelDoc2
Set swModel = m_SwApp.ActiveDoc

Dim errCode As Long
‘ swMateCOAXIAL = 4
Dim swMateRet As SldWorks.Mate2
Set swMateRet = swModel.AddMate3(4, 0, False, 0, 0, 0, 0, 0, 0, 0, 0, False, errCode)

swModel.ClearSelection2 True

If errCode = 0 Then
AddCoaxialMate = True
Else
MsgBox “合致の作成に失敗しました。Error Code: ” & errCode, vbExclamation
End If
End Function

‘ 内部ヘルパー: コンポーネント内の特定エンティティ(面・軸)を安全に選択する
Private Function SelectEntityByComponent(ByVal comp As SldWorks.Component2, ByVal entityName As String, ByVal append As Boolean) As Boolean
SelectEntityByComponent = False
Dim swModelDoc As SldWorks.ModelDoc2
Set swModelDoc = comp.GetModelDoc2

If swModelDoc Is Nothing Then Exit Function

‘ Featureからエンティティを取得して選択
Dim swSelMgr As SldWorks.SelectionMgr
Set swSelMgr = m_SwApp.ActiveDoc.SelectionManager

Dim bRet As Boolean
bRet = comp.Select4(False, 0, append)
If Not bRet Then Exit Function

‘ 拡張APIによる安全な名前選択
Dim swExt As SldWorks.ModelDocExtension
Set swExt = m_SwApp.ActiveDoc.Extension

SelectEntityByComponent = swExt.SelectByID2(entityName & “@” & comp.Name2, “FACE”, 0, 0, 0, append, 1, Nothing, 0)
End Function

③ 標準モジュール: `MainExecution` (モダンなエントリーポイント)

驚くほどクリーンで、何をやっているかが一目でわかるメインルーチン。

‘ ==========================================
‘ 標準モジュール名: MainExecution
‘ ==========================================
Option Explicit

Sub BuildAssemblyAutomation()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks

If swApp Is Nothing Then
MsgBox “SolidWorksが起動していません。”, vbCritical
Exit Sub
End If

‘ 新規アセンブリドキュメントの作成 (swDocumentTypes.swDocASSEMBLY = 2)
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.NewDocument(“C:\ProgramData\SolidWorks\SOLIDWORKS 2023\templates\Assembly.asmdt”, 0, 0, 0)

If swModel Is Nothing Then
MsgBox “アセンブリテンプレートの読み込みに失敗しました。”, vbCritical
Exit Sub
End If

‘ — フレームワークの初期化 —
Dim mateMgr As New MateManager
mateMgr.Initialize swApp

‘ — 1. ベース部品の配置 —
Dim basePart As New ComponentItem
basePart.FilePath = “C:\Automation\Data\BasePlate.sldprt”
basePart.Configuration = “Default”
basePart.SetPosition 0, 0, 0

If Not basePart.Insert(swModel) Then GoTo Cleanup

‘ — 2. ボス部品の配置 —
Dim bossPart As New ComponentItem
bossPart.FilePath = “C:\Automation\Data\Boss.sldprt”
bossPart.Configuration = “Default”
bossPart.SetPosition 0.1, 0, 0.05

If Not bossPart.Insert(swModel) Then GoTo Cleanup

‘ — 3. 自動合致の適用 —
‘ ベースの穴とボスの軸を同軸合致させる
Dim result As Boolean
result = mateMgr.AddCoaxialMate(basePart.ComponentRef, “穴1”, bossPart.ComponentRef, “ボス外径”)

If result Then
MsgBox “アセンブリの自動生成と合致定義が完了しました!”, vbInformation
End If

Cleanup:
‘ 参照の解放
Set mateMgr = Nothing
Set basePart = Nothing
Set bossPart = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub

—

4. プロジェクト成功のための実践的アドバイス

1. `Friend`キーワードの活用:
クラス間の結合度を下げるため、外部の標準モジュールから隠蔽したいメソッドやプロパティには`Friend`スコープを積極的に使え。これにより「どこからでも呼び出せるカオス」を防げる。
2. データベース/CSV連携の勘所:
エクセルや外部DBから部品リスト(BOM)を読み込んでアセンブリを動的生成する場合、`ComponentItem`のインスタンスをコレクション(Collection)やDictionaryで管理せよ。配列のインデックス管理で破綻するリスクを完全に排除できる。
3. エラー時のロールバック設計:
途中で合致エラーやファイルが見つからないエラーが発生した場合、作成途中の中途半端なアセンブリを残さないよう、エラーハンドラ内でドキュメントを強制クローズ(`CloseDoc`)する処理を必ず組み込むこと。

—

5. 結び:スパゲッティコードからの脱却は今すぐできる

レガシーコードの改修は、最初は億劫に感じるかもしれない。だが、一度この「クラスモジュールベースのフレームワーク設計」を導入すれば、次からの機能追加(新しい合致タイプの追加、サブアセンブリの階層化など)は驚くほど容易になる。

「動けばいいや」のコードは、明日には技術的負債として組織の足を引っ張る。
プロフェッショナルなエンジニアであるならば、美しく、堅牢で、拡張性のあるコードベースでSolidWorksを完全制御してほしい。あなたの開発現場が劇的に変わることを約束する。

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