【実務・中級編】【アドイン開発へのステップ】VBAマクロを立派なSolidWorks Add-in(COMコンポーネント)へ昇華させる手法 – SolidWorks VBA解析バイブル

スポンサーリンク

VBAマクロの限界を超え、SolidWorks Add-in(COMコンポーネント)へ昇華させる究極のステップ

SolidWorks VBAマクロを駆使し、日々の業務効率化に貢献されている皆様、お疲れ様です。
しかし、マクロの実行に手間がかかる、複雑な処理になるとパフォーマンスが低下する、あるいはSolidWorksの起動と同時に自動で実行したい、といった壁にぶつかっていませんか?

その悩み、私たちが解決します。

本記事では、長年SolidWorks VBAとAPIに携わってきた経験から、単なるマクロの実行に留まらず、SolidWorks起動時にリボンメニューへ独自のボタンを追加し、常駐型の強力なアドインとして動作させるための移行ステップを、バグの起きない堅牢な設計、ファイル・データベース連携の注意点、そしてコピペで動く保守性の高いプロダクションコード例を交えながら、徹底的に解説していきます。

これは、単なるリファレンスをなぞるような解説ではありません。オブジェクトのライフサイクル、パフォーマンスの重み、そして「なぜその書き方は非効率なのか」「どう設計すべきか」という、開発プロジェクトのリーダーが部下に伝えるような、ロジカルかつシャープな知見を皆様にお伝えするものです。

なぜAdd-in(COMコンポーネント)化が必要なのか? VBAマクロの限界

VBAマクロは手軽に始められる強力なツールですが、その特性上、いくつかの限界があります。

  • 実行の煩雑さ: マクロを実行するには、VBAエディタを開くか、[マクロ実行]ダイアログから対象のマクロを選択する必要があります。これは、頻繁に利用する機能にとっては非効率です。
  • 常駐性の欠如: SolidWorks起動時に自動で読み込まれ、常に利用可能な状態にしておくことができません。
  • UI統合の限界: リボンメニューに独自のボタンを追加するような、高度なUI統合はVBA単体では困難です。
  • パフォーマンス: 複雑な処理や大量のデータ操作を行う場合、VBAの実行速度では限界が見えてくることがあります。
  • 保守性: コードが長大化すると、デバッグや改修が難しくなり、保守性が低下しがちです。

これらの限界を突破し、真の業務自動化ツールとしてSolidWorksを拡張するためには、COMコンポーネントとして実装されるAdd-in化が不可欠です。Add-inは、SolidWorksのプロセス内で動作し、リボンメニューへのボタン追加、イベントハンドリング、そしてより高度なUI/UXの提供を可能にします。

Add-in開発へのロードマップ:VBAからVB.NETへの移行

Add-in開発の主流は、VB.NET(Visual Basic .NET)です。VBAとVB.NETは構文が似ている部分も多く、VBAで培ったSolidWorks APIの知識はそのまま活かせます。

ここからは、VBAマクロからAdd-inへの移行ステップを、具体的なコード例と共に解説します。

ステップ1:Add-inの基本構造を理解する

Add-inは、COM(Component Object Model)インターフェースを実装したDLL(Dynamic Link Library)またはEXE(実行ファイル)として作成されます。SolidWorksは、このCOMインターフェースを通じてAdd-inと通信します。

Add-inとして最低限実装すべきインターフェースは `ISldWorks` です。このインターフェースを通じて、SolidWorksアプリケーション本体へのアクセスや、イベントの購読が可能になります。

ステップ2:VB.NETプロジェクトの作成とCOM公開設定

Visual Studio(Community Editionで十分です)を使用して、新しい「クラスライブラリ」プロジェクトを作成します。

1. Visual Studioを起動し、「新しいプロジェクトの作成」を選択します。
2. テンプレートとして「クラスライブラリ (.NET Framework)」を選択します。(.NET Core/.NET 5以降はCOMインターフェースの扱いに注意が必要です。SolidWorks Add-inでは.NET Frameworkが一般的です。)
3. プロジェクト名(例: `MySolidWorksAddin`)と場所を指定し、「作成」をクリックします。

次に、このクラスライブラリをCOMコンポーネントとして利用できるように設定します。

  • プロジェクトのプロパティを開く: ソリューションエクスプローラーでプロジェクト名を右クリックし、「プロパティ」を選択します。
  • ビルドタブ:
  • 「COM オブジェクトの登録」を「レジストリに登録する」に変更します。
  • アプリケーションタブ:
  • 「アセンブリのCOM相互運用機能」をチェックします。
  • 「COM オブジェクトとして公開」を「すべてのCOMクライアント」または「False」以外に設定します。(「True」で問題ありません。)
  • 署名タブ:
  • 「アセンブリに署名する」にチェックを入れ、「厳密な名前のキーファイル」で新しいキーファイルを作成します。(Add-inの信頼性を高めるため、実運用では強く推奨されます。)

ステップ3:`ISldWorks` インターフェースの実装

Add-inの核となるクラスを作成し、`SolidWorks/Add-In` のCOMインターフェースを実装します。

.net
‘ MyAddin.vb

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swcommands
Imports SolidWorks.Interop.swconst
Imports System.Runtime.InteropServices

‘ COMコンポーネントとして公開するための属性
‘ ここにユニークなGUIDを入力します。Visual Studioで生成できます。

‘ インターフェースベースのCOM公開を強制
Public Class MyAddin
Implements ISldWorksAddin

Private m_swApp As SldWorks
Private m_addinID As Integer

‘————————————————————————–
‘ ISldWorksAddin インターフェースの実装
‘————————————————————————–

‘ Add-inの初期化時にSolidWorksから呼び出されます。
‘ ここでSolidWorksアプリケーションオブジェクトへの参照を取得し、
‘ UI要素(ボタンなど)の追加を行います。
Public Function OnConnect(ByVal App As Object, ByVal AddInInst As Integer, ByVal Cookie As Integer) As Boolean Implements ISldWorksAddin.OnConnect
m_swApp = DirectCast(App, SldWorks)
m_addinID = AddInInst

‘ ここでAdd-inの初期化処理を行います。
‘ 例: リボンメニューへのボタン追加
AddRibbonButton()

Return True
End Function

‘ Add-inが切断される際にSolidWorksから呼び出されます。
‘ リソースの解放やクリーンアップ処理を行います。
Public Function OnDisconnect() As Boolean Implements ISldWorksAddin.OnDisconnect
‘ ここでAdd-inのクリーンアップ処理を行います。
‘ 例: イベントハンドラの解除、UI要素の削除
RemoveRibbonButton()

‘ オブジェクトの解放
System.GC.Collect()
System.GC.WaitForPendingFinalizers()

m_swApp = Nothing
Return True
End Function

‘————————————————————————–
‘ カスタムメソッド
‘————————————————————————–

‘ リボンメニューにボタンを追加するメソッド
Private Sub AddRibbonButton()
‘ TODO: リボンボタン追加の具体的な実装
‘ SolidWorks APIを使用して、リボンタブ、パネル、ボタンを作成します。
‘ ボタンクリック時のイベントハンドラを設定します。
MsgBox(“Add-in Connected!”) ‘ 仮のメッセージ
End Sub

‘ リボンメニューからボタンを削除するメソッド
Private Sub RemoveRibbonButton()
‘ TODO: リボンボタン削除の具体的な実装
‘ AddRibbonButton で追加したUI要素を削除します。
End Sub

‘ ボタンクリック時に実行されるメソッド(例)
Public Sub OnMyButtonClick()
‘ ここにボタンクリック時の処理を記述します。
‘ VBAマクロのコードをここに移植します。
MsgBox(“Hello from Add-in!”)
End Sub

End Class

【重要】GUIDの生成と設定

`[Guid(“YOUR_GUID_HERE”)]` の部分は、必ずユニークなGUID(Globally Unique Identifier)に置き換えてください。Visual Studioでは、プロジェクトのプロパティを開き、「アプリケーション」タブの「アセンブリ情報の表示」から「新しいGUIDの生成」ボタンをクリックして取得できます。

【重要】COM公開設定の確認

`[ComVisible(True)]` 属性は、このクラスがCOMからアクセス可能であることを示します。
`[ClassInterface(ClassInterfaceType.None)]` は、COMクライアントがインターフェースベースのアクセスのみを行うように強制し、より堅牢な設計を促します。

ステップ4:リボンメニューへのボタン追加(VB.NET)

VBAでは、リボンメニューへのボタン追加は直接行えませんでしたが、VB.NET Add-inではAPIを通じて可能です。

.net
‘ MyAddin.vb の続き

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swcommands
Imports SolidWorks.Interop.swconst
Imports System.Runtime.InteropServices
Imports System.Reflection ‘ Reflectionを使用するために必要

‘ … (MyAddinクラス定義の続き) …

‘ リボンメニューにボタンを追加するメソッド
Private Sub AddRibbonButton()
Try
‘ SolidWorksのメインリボンタブを取得
Dim ribbonTabs As Object() = m_swApp.GetRibbonTabs()

Dim customTabExists As Boolean = False
Dim targetTab As Object = Nothing

‘ カスタムタブが存在するか確認、または既存のタブにパネルを追加
For Each tab As Object In ribbonTabs
If tab.ToString() = “MyCustomTab” Then ‘ カスタムタブ名(任意)
targetTab = tab
customTabExists = True
Exit For
End If
Next

‘ カスタムタブが存在しない場合は作成
If Not customTabExists Then
targetTab = m_swApp.AddRibbonTab2(“MyCustomTab”, “”) ‘ 第二引数はコンテキストヘルプID
End If

‘ カスタムタブにパネルを追加(または既存のタブに)
Dim panel As Object = Nothing
Dim panelExists As Boolean = False
Dim panels As Object() = m_swApp.GetRibbonPanels2(targetTab) ‘ カスタムタブのパネルを取得

For Each p As Object In panels
If p.ToString() = “MyCustomPanel” Then ‘ パネル名(任意)
panel = p
panelExists = True
Exit For
End If
Next

If Not panelExists Then
panel = m_swApp.AddRibbonPanel2(targetTab, “MyCustomPanel”, “”) ‘ 第二引数はコンテキストヘルプID
End If

‘ ボタンを作成
‘ ボタンのText、Tooltip、Image(アイコン)、CallbackMethodを指定
‘ CallbackMethod は、Add-inクラス内のメソッド名を文字列で指定します。
‘ Click イベントは、COM経由でAdd-inクラスのメソッドを呼び出します。
Dim button As Object = m_swApp.AddCommandMgrButton2( _
panel, _
“MyButton”, _
swCommandMgrButtonTextDisplay_e.swCommandMgrButtonText_IconAndText, _
“実行する”, _
“Click Here!”, _
Nothing, ‘ IconFile
Nothing, ‘ IconFileLarge
“OnMyButtonClick”, _ ‘ コールバックメソッド名
“” _ ‘ Tooltip
)

‘ ボタンのイベントハンドラを設定(Add-inクラスのメソッドを呼び出す)
‘ この部分は、COMのメカニズムにより、ボタンがクリックされると
‘ 指定したCallbackMethodが実行されます。

‘ UIを更新
m_swApp.UpdateRibbonTab2(targetTab)

‘ 成功メッセージ
Console.WriteLine(“Ribbon button added successfully.”)

Catch ex As Exception
MessageBox.Show($”Error adding ribbon button: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

‘ リボンメニューからボタンを削除するメソッド
Private Sub RemoveRibbonButton()
Try
‘ TODO: ボタン削除の実装
‘ AddCommandMgrButton2で取得したボタンオブジェクトを削除するAPIを使用します。
‘ GetRibbonTabByName, GetRibbonPanelByName などでUI要素を取得し、削除します。
‘ 例:
‘ Dim tab As Object = m_swApp.GetRibbonTabByName(“MyCustomTab”)
‘ If tab IsNot Nothing Then
‘ Dim panel As Object = m_swApp.GetRibbonPanelByName(tab, “MyCustomPanel”)
‘ If panel IsNot Nothing Then
‘ Dim button As Object = m_swApp.GetCommandMgrButton(panel, “MyButton”)
‘ If button IsNot Nothing Then
‘ m_swApp.DeleteCommandMgrButton(button)
‘ End If
‘ m_swApp.DeleteRibbonPanel(panel)
‘ End If
‘ m_swApp.DeleteRibbonTab(tab)
‘ End If
Console.WriteLine(“Ribbon button removed.”)
Catch ex As Exception
MessageBox.Show($”Error removing ribbon button: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

‘ ボタンクリック時に実行されるメソッド(例)
Public Sub OnMyButtonClick()
Try
‘ ここにボタンクリック時の処理を記述します。
‘ VBAマクロのコードをここに移植します。

‘ 例:アクティブドキュメントの情報を取得
Dim swModel As ModelDoc2 = CType(m_swApp.ActiveDoc, ModelDoc2)
If swModel Is Nothing Then
MessageBox.Show(“アクティブなドキュメントがありません。”, “Add-in Message”, MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If

Dim docName As String = swModel.GetTitle()
MessageBox.Show($”Hello from Add-in! You clicked the button in: {docName}”, “Add-in Message”, MessageBoxButtons.OK, MessageBoxIcon.Information)

‘ VBAマクロから移植した複雑な処理をここに記述
‘ 例:
‘ Call MyComplexMacroFunction(swModel)

Catch ex As Exception
MessageBox.Show($”Error executing button action: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

‘ VBAマクロから移植する処理の例(参考)
‘ Private Sub MyComplexMacroFunction(ByVal swModel As ModelDoc2)
‘ ‘ 複雑なフィーチャー操作、図面作成、レポート出力などをここに実装
‘ ‘ ファイルI/O、データベースアクセスなども
‘ End Sub

End Class

【補足】`AddCommandMgrButton2` とコールバックメソッド

`AddCommandMgrButton2` メソッドは、リボンボタンの作成と、そのボタンがクリックされたときに実行されるメソッドを指定するために使用します。`”OnMyButtonClick”` のように、Add-inクラス内のパブリックメソッド名を文字列で指定します。SolidWorksはCOMの仕組みを利用して、このメソッドを呼び出します。

ステップ5:Add-inの登録と実行

Add-inをSolidWorksで利用可能にするには、COM登録が必要です。

1. ビルド: Visual Studioでプロジェクトをビルドします。これにより、指定したCOM登録設定が適用されます。
2. 登録:

  • 管理者権限でコマンドプロンプトを開きます。
  • ビルドされたDLL(例: `MySolidWorksAddin.dll`)が格納されているディレクトリに移動します。
  • `regasm.exe` コマンドを使用して登録します。
  • 32bit版 SolidWorks の場合:

%windir%\Microsoft.NET\Framework\v4.0.30319\regasm.exe YourAddinDllPath\MySolidWorksAddin.dll /tlb /codebase

(.NET Frameworkのバージョンは、プロジェクトで指定したバージョンに合わせてください。例ではv4.0を示しています。)

  • 64bit版 SolidWorks の場合:

%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm.exe YourAddinDllPath\MySolidWorksAddin.dll /tlb /codebase

(`regasm.exe` のパスは、お使いのOSと.NET Frameworkのバージョンによって異なる場合があります。)

  • `/tlb` オプションは、タイプライブラリを生成・登録します。
  • `/codebase` オプションは、DLLの場所をレジストリに記録します。

3. SolidWorksでのAdd-in有効化:

  • SolidWorksを起動します。
  • 「ツール」 > 「アドイン」を選択します。
  • 「アドインマネージャー」ダイアログが表示されます。
  • 「アドイン」タブのリストに、作成したAdd-in(クラス名)が表示されているはずです。
  • 「ソリューション」または「ドキュメント」のチェックボックスをオンにします。
  • 「ソリューション」をチェックすると、SolidWorks起動時に常に読み込まれます。
  • 「ドキュメント」をチェックすると、特定のドキュメントを開いたときに読み込まれます。
  • 「OK」をクリックします。

これで、SolidWorks起動時にカスタムタブとボタンが表示され、クリックすると指定したメソッドが実行されるようになります。

ステップ6:堅牢な設計のための考慮事項(バグを生まないために)

VBAマクロで発生しがちなバグは、Add-in化によっても油断すると発生します。以下に、堅牢なAdd-inを設計するための重要なポイントを挙げます。

  • エラーハンドリングの徹底:
  • SolidWorks APIの呼び出しやファイル操作、データベースアクセスなど、例外が発生しうる全ての箇所で`Try…Catch`ブロックを使用します。
  • エラーメッセージは、ユーザーが理解しやすいように具体的に記述します。
  • ログファイルへのエラー記録も実装すると、デバッグが容易になります。
  • なぜ非効率か: エラーハンドリングを怠ると、予期せぬクラッシュやデータ破損の原因となり、デバッグに多大な時間を費やすことになります。
  • オブジェクトのライフサイクル管理:
  • COMオブジェクト(特に `SldWorks` オブジェクト)は、適切に解放しないと、SolidWorksのプロセスが終了しても残り続け、リソースリークや予期せぬ動作を引き起こします。
  • `OnDisconnect` メソッドや、不要になったタイミングで `Marshal.ReleaseComObject()` を使用して明示的に解放します。
  • なぜ非効率か: オブジェクトの解放漏れは、メモリ使用量の増大、パフォーマンス低下、そしてSolidWorks自体の不安定化を招きます。Add-inの再読み込み時に問題が発生する典型的な原因です。
  • イベントハンドリングの注意:
  • SolidWorksのイベント(ドキュメント変更、保存など)を購読する場合、Add-inがアンロードされる際にイベントハンドラを解除しないと、COMエラーが発生します。
  • `OnDisconnect` で必ずイベントハンドラを解除します。
  • なぜ非効率か: イベントハンドラが残存すると、Add-inがアンロードされても、SolidWorksの内部で不正な参照が残り、クラッシュや不安定な動作を引き起こします。
  • UIスレッドの考慮:
  • Add-inの処理は、SolidWorksのUIスレッドで実行されます。時間のかかる処理をUIスレッドで実行すると、UIがフリーズ(応答なし)状態になります。
  • 時間のかかる処理は、バックグラウンドスレッドで実行し、結果をUIスレッドに通知するなどの工夫が必要です。`System.Threading.Tasks` や `BackgroundWorker` を活用します。
  • なぜ非効率か: UIがフリーズすると、ユーザーエクスペリエンスが著しく低下し、Add-inが使い物にならなくなります。
  • Add-inの再読み込み:
  • 開発中にAdd-inを更新する場合、SolidWorksを再起動するか、「アドインマネージャー」で一度無効化してから再度有効化する必要があります。
  • `OnDisconnect` でリソースが適切に解放されていないと、再読み込み時に問題が発生しやすくなります。
  • なぜ非効率か: Add-inの更新・デバッグサイクルが長くなると、開発効率が著しく低下します。

ステップ7:ファイル・データベース連携における注意点

Add-inは、VBAマクロと同様に、ファイル操作やデータベース連携を行う機会が多いでしょう。これらの操作でバグやパフォーマンス低下を招かないための注意点を解説します。

ファイル連携

  • ファイルパスの管理:
  • 相対パスではなく、絶対パスを基本とします。
  • ユーザー設定や共通設定ファイルからパスを読み込むようにすると、保守性が高まります。
  • ネットワークパス(UNCパス)へのアクセスも考慮し、権限の問題やタイムアウトを考慮した処理を実装します。
  • なぜ非効率か: 不適切なパス指定は、ファイルが見つからずエラーになる、または意図しない場所にファイルが作成される原因となります。
  • ファイルロックと排他制御:
  • 複数のAdd-inインスタンスや他のプロセスが同時に同じファイルを操作する場合、ファイルロック競合が発生する可能性があります。
  • ファイルを開く際には `FileShare` オプションを適切に指定し、必要に応じてロック機構を導入します。
  • なぜ非効率か: ファイルロックの競合は、データの破損や処理の中断を招き、デバッグが困難な問題を引き起こします。
  • 大量のファイル処理:
  • 大量のファイルを一度に処理する場合、メモリ使用量に注意が必要です。ストリーム処理や、一度にメモリに読み込むファイル数を制限するなどの工夫が必要です。
  • なぜ非効率か: 大量のファイルを一度にメモリに読み込むと、OutOfMemoryExceptionが発生したり、システム全体のパフォーマンスが低下します。
  • ファイルI/Oの非同期処理:
  • ファイル書き込みなど、時間のかかるI/O処理は、バックグラウンドスレッドで非同期に実行し、UIの応答性を維持します。
  • なぜ非効率か: 同期的なI/O処理はUIスレッドをブロックし、アプリケーションが応答不能になります。

データベース連携

  • 接続文字列の管理:
  • 接続文字列は、設定ファイル(XML, JSONなど)や、Windowsのレジストリに安全に保存します。コード内に直接埋め込むのは避けます。
  • なぜ非効率か: 接続文字列をコードにハードコーディングすると、パスワードなどの機密情報が漏洩するリスクがあり、変更のたびにコードの再ビルドが必要になります。
  • トランザクション管理:
  • データベースへの一連の操作は、トランザクションとして管理し、一貫性を保ちます。エラーが発生した場合はロールバックします。
  • なぜ非効率か: トランザクションが適切に管理されていないと、一部のデータだけが更新されてしまい、データベースの一貫性が失われます。
  • SQLインジェクション対策:
  • ユーザーからの入力値をSQLクエリに直接埋め込むのではなく、パラメータ化クエリ(ストアドプロシージャや `SqlCommand` の `Parameters` コレクション)を使用します。
  • なぜ非効率か: SQLインジェクションは、データベースへの不正アクセスやデータ漏洩、改ざんといった深刻なセキュリティリスクを引き起こします。
  • 接続プーリングの活用:
  • データベース接続はコストのかかる処理です。ADO.NETなどのライブラリが提供する接続プーリング機能を活用し、接続/切断のオーバーヘッドを削減します。
  • なぜ非効率か: 接続プーリングを使用しないと、データベース接続の確立と解放に多くの時間が費やされ、パフォーマンスが著しく低下します。
  • 大量データ処理の最適化:
  • `SqlDataReader` を使用してデータを一行ずつ読み込む、バッチ処理を適用する、インデックスを適切に設計するなど、大量データを効率的に扱うための工夫が必要です。
  • なぜ非効率か: 大量のデータを一度にメモリに読み込んだり、非効率なクエリを実行したりすると、パフォーマンスが著しく低下し、タイムアウトエラーやメモリ不足を引き起こします。

ステップ8:保守性の高いプロダクションコード例

以下に、上記で解説したポイントを踏まえた、保守性の高いAdd-inのコード例を示します。

.net
‘ MyAddin.vb

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swcommands
Imports SolidWorks.Interop.swconst
Imports System.Runtime.InteropServices
Imports System.Reflection
Imports System.Windows.Forms ‘ MessageBox用
Imports System.IO ‘ ファイル操作用
Imports System.Data.SqlClient ‘ DB連携例用 (System.Data.DataSetExtensions を参照に追加)
Imports System.Configuration ‘ 設定ファイル読み込み用 (System.Configuration.dll を参照に追加)

‘ GUIDはプロジェクトプロパティで生成したものに置き換えてください。



Public Class MyAddin
Implements ISldWorksAddin

Private m_swApp As SldWorks
Private m_addinID As Integer

‘ UI要素の参照を保持(必要に応じて)
Private m_customTab As Object
Private m_customPanel As Object
Private m_myButton As Object

‘————————————————————————–
‘ ISldWorksAddin インターフェースの実装
‘————————————————————————–

Public Function OnConnect(ByVal App As Object, ByVal AddInInst As Integer, ByVal Cookie As Integer) As Boolean Implements ISldWorksAddin.OnConnect
m_swApp = DirectCast(App, SldWorks)
m_addinID = AddInInst

Try
‘ SolidWorksイベントの購読 (例: ドキュメント保存前イベント)
‘ AddHandler m_swApp.DocumentSaveNotify, AddressOf OnDocumentSaveNotify

AddRibbonButton()
LogMessage(“Add-in connected and ribbon button added.”)
Return True

Catch ex As Exception
LogErrorMessage($”Error during Add-in connection: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
MessageBox.Show($”Add-in initialization failed: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
End Try
End Function

Public Function OnDisconnect() As Boolean Implements ISldWorksAddin.OnDisconnect
Try
‘ SolidWorksイベントの購読解除
‘ RemoveHandler m_swApp.DocumentSaveNotify, AddressOf OnDocumentSaveNotify

RemoveRibbonButton()
LogMessage(“Add-in disconnected and ribbon button removed.”)

‘ COMオブジェクトの明示的な解放
If m_swApp IsNot Nothing Then
Marshal.ReleaseComObject(m_swApp)
m_swApp = Nothing
End If

GC.Collect()
GC.WaitForPendingFinalizers()
Return True

Catch ex As Exception
LogErrorMessage($”Error during Add-in disconnection: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
MessageBox.Show($”Add-in cleanup failed: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
End Try
End Function

‘————————————————————————–
‘ UI要素の追加・削除
‘————————————————————————–

Private Sub AddRibbonButton()
Try
Dim ribbonManager As CommandMgr = m_swApp.CommandMgr
Dim customTabCaption As String = “MyTools” ‘ カスタムタブの表示名
Dim customPanelCaption As String = “Automation” ‘ パネルの表示名
Dim buttonCaption As String = “Process Parts” ‘ ボタンの表示名
Dim buttonTooltip As String = “Process selected parts” ‘ ボタンのツールチップ
Dim callbackMethodName As String = “ProcessSelectedParts” ‘ 実行するメソッド名

‘ カスタムタブの追加または取得
m_customTab = ribbonManager.GetRibbonTabByName(customTabCaption)
If m_customTab Is Nothing Then
m_customTab = ribbonManager.AddRibbonTab2(customTabCaption, “”)
End If

‘ パネルの追加または取得
m_customPanel = ribbonManager.GetRibbonPanelByName(m_customTab, customPanelCaption)
If m_customPanel Is Nothing Then
m_customPanel = ribbonManager.AddRibbonPanel2(m_customTab, customPanelCaption, “”)
End If

‘ ボタンの追加
‘ ここではアイコンは省略していますが、実際にはPathを指定します。
‘ 例: IconFile=”C:\Path\To\Your\Icon.ico”
m_myButton = ribbonManager.AddCommandMgrButton2( _
m_customPanel, _
“UniqueButtonID”, _
swCommandMgrButtonTextDisplay_e.swCommandMgrButtonText_IconAndText, _
buttonCaption, _
buttonTooltip, _
Nothing, _
Nothing, _
callbackMethodName, _
buttonTooltip _
)

‘ コマンドマネージャーのUIを更新
m_swApp.UpdateRibbonTab2(m_customTab)
LogMessage($”Ribbon button ‘{buttonCaption}’ added to tab ‘{customTabCaption}’, panel ‘{customPanelCaption}’.”)

Catch ex As Exception
LogErrorMessage($”Error adding ribbon button: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
MessageBox.Show($”Failed to add button to ribbon: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

Private Sub RemoveRibbonButton()
Try
Dim ribbonManager As CommandMgr = m_swApp.CommandMgr

‘ ボタンの削除
If m_myButton IsNot Nothing Then
ribbonManager.DeleteCommandMgrButton(m_myButton)
m_myButton = Nothing
LogMessage(“Ribbon button deleted.”)
End If

‘ パネルの削除 (ボタンがない場合のみ)
If m_customPanel IsNot Nothing AndAlso ribbonManager.GetCommandMgrButtons(m_customPanel).Length = 0 Then
ribbonManager.DeleteRibbonPanel(m_customPanel)
m_customPanel = Nothing
LogMessage(“Ribbon panel deleted.”)
End If

‘ タブの削除 (パネルがない場合のみ)
If m_customTab IsNot Nothing AndAlso ribbonManager.GetRibbonPanels2(m_customTab).Length = 0 Then
ribbonManager.DeleteRibbonTab(m_customTab)
m_customTab = Nothing
LogMessage(“Ribbon tab deleted.”)
End If

‘ UIの更新
If m_customTab IsNot Nothing Then
m_swApp.UpdateRibbonTab2(m_customTab)
End If

Catch ex As Exception
LogErrorMessage($”Error removing ribbon button: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
MessageBox.Show($”Failed to remove ribbon button: {ex.Message}”, “Add-in Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

‘————————————————————————–
‘ コールバックメソッド (ボタンクリック時に実行)
‘————————————————————————–

‘ VBAマクロでいうところの、Sub Main() のような役割
Public Sub ProcessSelectedParts()
Try
Dim swModel As ModelDoc2 = CType(m_swApp.ActiveDoc, ModelDoc2)
If swModel Is Nothing Then
MessageBox.Show(“アクティブなドキュメントがありません。”, “Process Parts”, MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If

‘ ここにVBAマクロから移植した、または新規に実装する複雑な処理を記述します。
‘ 例: 選択された部品の処理、図面作成、レポート生成など

‘ ファイルパスの取得 (設定ファイルから読み込む例)
Dim outputFolderPath As String = GetSetting(“AppSettings”, “FolderPath”, “C:\Temp\Output”)
If Not Directory.Exists(outputFolderPath) Then
Directory.CreateDirectory(outputFolderPath)
End If

Dim processedCount As Integer = ProcessDocument(swModel, outputFolderPath)

MessageBox.Show($”Processed {processedCount} items from {swModel.GetTitle()}.”, “Process Parts Complete”, MessageBoxButtons.OK, MessageBoxIcon.Information)

Catch ex As Exception
LogErrorMessage($”Error in ProcessSelectedParts: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
MessageBox.Show($”An error occurred during processing: {ex.Message}”, “Processing Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub

‘————————————————————————–
‘ 業務ロジックの実装 (VBAマクロから移植)
‘————————————————————————–

‘ ドキュメントを処理するメインメソッド
Private Function ProcessDocument(ByVal swModel As ModelDoc2, ByVal outputPath As String) As Integer
Dim processedCount As Integer = 0
‘ TODO: ここにVBAマクロの主要な処理を移植します。
‘ 例:
‘ – 図面ビューの更新
‘ – 部品表の取得と加工
‘ – 特定のフィーチャーの更新
‘ – ファイルのエクスポート (PDF, STEPなど)

‘ ファイルエクスポートの例 (PDF)
Dim exportPath As String = Path.Combine(outputPath, swModel.GetTitle() & “.pdf”)
If swModel.Extension.SaveAs(exportPath, swSaveAsCurrentVersion_e.swSaveAsCurrentVersion, swSaveAsOptions_e.swSaveAsOptions_Silent, Nothing, Nothing, Nothing) = False Then
Throw New Exception($”Failed to save document as PDF: {exportPath}”)
End If
processedCount += 1
LogMessage($”Document saved as PDF: {exportPath}”)

‘ データベース連携の例 (GetDatabaseConnectionString() は後述)
‘ If Not SaveToDatabase(swModel) Then
‘ LogErrorMessage($”Failed to save data for document: {swModel.GetTitle()}”)
‘ End If

Return processedCount
End Function

‘ データベースへデータを保存する例 (ADO.NET)
‘ Private Function SaveToDatabase(ByVal swModel As ModelDoc2) As Boolean
‘ Dim connectionString As String = GetDatabaseConnectionString()
‘ Using connection As New SqlConnection(connectionString)
‘ Try
‘ connection.Open()
‘ ‘ TODO: INSERT または UPDATE 文を記述
‘ ‘ 例:
‘ ‘ Dim command As New SqlCommand(“INSERT INTO Documents (DocName, LastModified) VALUES (@DocName, @LastModified)”, connection)
‘ ‘ command.Parameters.AddWithValue(“@DocName”, swModel.GetTitle())
‘ ‘ command.Parameters.AddWithValue(“@LastModified”, DateTime.Now)
‘ ‘ command.ExecuteNonQuery()
‘ LogMessage($”Data for ‘{swModel.GetTitle()}’ saved to database.”)
‘ Return True
‘ Catch ex As Exception
‘ LogErrorMessage($”Database error for ‘{swModel.GetTitle()}’: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
‘ Return False
‘ End Try
‘ End Using
‘ End Function

‘————————————————————————–
‘ 設定・ユーティリティ
‘————————————————————————–

‘ 設定ファイル (App.config) から値を取得するヘルパーメソッド
Private Function GetSetting(ByVal section As String, ByVal key As String, ByVal defaultValue As String) As String
Try
‘ App.config ファイルのパスを動的に取得
Dim assemblyLocation As String = Assembly.GetExecutingAssembly().Location
Dim configFilePath As String = Path.Combine(Path.GetDirectoryName(assemblyLocation), “MySolidWorksAddin.dll.config”) ‘ DLL名と一致させる

Dim configuration As Configuration = ConfigurationManager.OpenExeConfiguration(configFilePath)
Dim appSettings As AppSettingsSection = CType(configuration.GetSection(“appSettings”), AppSettingsSection)

Dim settingValue As String = appSettings.Settings(key)?.Value
If String.IsNullOrEmpty(settingValue) Then
Return defaultValue
Else
Return settingValue
End If
Catch ex As Exception
LogErrorMessage($”Error reading setting ‘{key}’: {ex.Message}”)
Return defaultValue ‘ エラー時はデフォルト値を返す
End Try
End Function

‘ データベース接続文字列を取得するヘルパーメソッド
‘ Private Function GetDatabaseConnectionString() As String
‘ ‘ App.config から接続文字列を取得する例
‘ ‘
‘ ‘
‘ ‘

‘ Try
‘ Dim connString As ConnectionStringSettings = ConfigurationManager.ConnectionStrings(“MyDatabase”)
‘ If connString Is Nothing Then
‘ Throw New ConfigurationErrorsException(“Connection string ‘MyDatabase’ not found.”)
‘ End If
‘ Return connString.ConnectionString
‘ Catch ex As Exception
‘ LogErrorMessage($”Error getting database connection string: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
‘ Throw New ApplicationException(“Failed to retrieve database connection string.”, ex)
‘ End Try
‘ End Function

‘ ログ出力メソッド (ファイルログ)
Private Sub LogMessage(message As String)
Dim logFilePath As String = GetSetting(“AppSettings”, “LogPath”, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), “MySolidWorksAddinLog.txt”))
Try
File.AppendAllText(logFilePath, $”{DateTime.Now:yyyy-MM-dd HH:mm:ss} – INFO: {message}{Environment.NewLine}”)
Catch ex As Exception
‘ ログファイルへの書き込みに失敗した場合でも、クラッシュさせない
Console.WriteLine($”Failed to write to log file: {ex.Message}”)
End Try
End Sub

‘ エラーログ出力メソッド (ファイルログ)
Private Sub LogErrorMessage(message As String)
Dim logFilePath As String = GetSetting(“AppSettings”, “LogPath”, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), “MySolidWorksAddinLog.txt”))
Try
File.AppendAllText(logFilePath, $”{DateTime.Now:yyyy-MM-dd HH:mm:ss} – ERROR: {message}{Environment.NewLine}”)
Catch ex As Exception
Console.WriteLine($”Failed to write to error log file: {ex.Message}”)
End Try
End Sub

‘————————————————————————–
‘ SolidWorks イベントハンドラ (例)
‘————————————————————————–

‘ Private Sub OnDocumentSaveNotify(ByVal Doc As Object)
‘ Try
‘ Dim swModel As ModelDoc2 = CType(Doc, ModelDoc2)
‘ LogMessage($”Document save notification for: {swModel.GetTitle()}”)
‘ ‘ ここで保存前の追加処理などを実装
‘ Catch ex As Exception
‘ LogErrorMessage($”Error in OnDocumentSaveNotify: {ex.Message}{Environment.NewLine}{ex.StackTrace}”)
‘ End Try
‘ End Sub

End Class

【保守性を高めるためのポイント】

1. 設定ファイル (.config) の活用:

  • ファイルパス、データベース接続文字列、ログパスなどの設定値を、コードから分離します。これにより、設定変更のためにコードの再ビルドや再デプロイが不要になります。
  • `App.config`(または `MySolidWorksAddin.dll.config`)ファイルを作成し、以下のように記述します。










  • なぜ重要か: 設定とコードの分離は、保守性、セキュリティ、柔軟性を劇的に向上させます。

2. ログ出力の徹底:

  • `LogMessage` および `LogErrorMessage` メソッドで、処理の進捗やエラー情報をファイルに記録します。
  • これは、Add-inがユーザーの環境でどのように動作しているかを把握し、バグの原因を特定するための最も強力な手段です。
  • なぜ重要か: ログは、複雑なAdd-inのデバッグにおいて、最も信頼できる情報源となります。

3. モジュール化と責務の分割:

  • `ProcessDocument` や `SaveToDatabase` のように、特定の業務ロジックを個別のメソッドに分割します。
  • これにより、コードの可読性が向上し、各部分のテストや改修が容易になります。
  • なぜ重要か: 巨大な単一メソッドは、理解も保守も困難です。責務を明確に分割することで、コードの品質が向上します。

4. COMオブジェクトの明示的な解放:

  • `Marshal.ReleaseComObject()` を使用して、SolidWorksのCOMオブジェクトを適切に解放します。これにより、リソースリークを防ぎます。
  • なぜ重要か: COMオブジェクトの解放漏れは、SolidWorksやシステム全体の不安定化の直接的な原因となります。

5. エラーハンドリングと例外処理:

  • `Try…Catch` ブロックを適切に使用し、例外発生時にはログに記録します。
  • ユーザーには、分かりやすいエラーメッセージを表示します。
  • なぜ重要か: 堅牢なアプリケーションは、予期せぬ状況にも適切に対処できなければなりません。

まとめ:Add-in開発はSolidWorks活用の新たなステージ

VBAマクロからAdd-inへの移行は、単なるツールの「パワーアップ」ではありません。それは、SolidWorksの機能を拡張し、より高度な自動化と効率化を実現するための、開発の新たなステージへの挑戦です。

本記事で解説したステップと考慮事項は、皆様がバグの少ない、保守性の高い、そして実用的なSolidWorks Add-inを開発するための一助となるはずです。

Add-in開発は、最初は少し敷居が高く感じるかもしれません。しかし、VB.NETとSolidWorks APIの力を組み合わせることで、皆様の業務効率化の可能性は無限に広がります。

もし、Add-in開発でさらに踏み込んだ質問や、特定の機能の実装方法について知りたいことがあれば、遠慮なくお尋ねください。私たちの経験を活かし、皆様のSolidWorks活用を全力でサポートします。

さあ、あなたもSolidWorks Add-in開発の世界へ飛び込み、業務自動化の新たな地平を切り開きましょう!

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