【テクニカル・上級編】【上級プロ】マルチプロセッサ環境を模倣したバックグラウンド処理:大規模アセンブリのエクスポート中にUIのフリーズを防ぐ進捗管理 – SolidWorks VBA解析バイブル

スポンサーリンク

SolidWorks VBAを掌握する極限の知見

【上級プロ】マルチプロセッサ環境を模倣したバックグラウンド処理:大規模アセンブリのエクスポート中にUIのフリーズを防ぐ進捗管理

SolidWorksを用いた大規模アセンブリ(数万点規模の構成部品)の自動処理において、開発者が最も直面し、そして絶望するのが「UIの完全なフリーズ」と「メモリリークによる突然のクラッシュ」だ。

VBAはシングルスレッドで動作する。そのため、`SwApp.ActivateDoc3` や `ISldWorks.WriteLine`、あるいは重いエクスポート処理(STEPやParasolidへの変換など)をループ内で回すと、Windowsメッセージキューが処理されなくなり、OSから「応答なし」の烙印を押される。さらに、COMオブジェクトの解放漏れが重なれば、数個のアセンブリを処理しただけでSolidWorks本体が沈黙する。

本稿では、シニアエンジニアおよび社内システム管理者が、この非力なVBA環境下で「擬似的な非同期処理(バックグラウンド処理)」を実現し、滑らかに動くプログレスバーと鉄壁のメモリ管理を両立させる極限の知見を公開する。

—

1. なぜVBAの処理はフリーズするのか? 根本原因の直視

Windowsアプリケーションは、UIスレッドがメッセージループ(`GetMessage` / `DispatchMessage`)を高速で回すことで、ユーザーからのマウス操作や再描画要求に応答している。
VBAで重い処理を実行すると、このメッセージループが完全に占有される。結果として、プログレスバーの再描画すら行われなくなる。

これを打破するためには、以下の2つのアプローチをコードに組み込む必要がある。
1. Windows APIの `DoEvents`(または明示的なメッセージポンプ)によるUIへの制御権の返却
2. COMオブジェクトのスコープ管理と明示的な `Nothing` 代入によるGC(ガベージコレクション)の誘導

—

2. 実装アーキテクチャ:非同期風プログレス管理フォーム

単なる `DoEvents` の乱用は、再入可能性(Reentrancy)の問題を引き起こし、ユーザーが処理中に「キャンセル」ボタンを押した際などに致命的なクラッシュを招く。
これを防ぐため、「処理のブロック」と「イベントの安全な消化」を制御するカスタムフォームクラスを構築する。

【実装コード:進捗管理用ユーザーフォーム `CProgressForm`】

以下のコードは、バックグラウンド処理の進行状況を視覚化しつつ、OSのフリーズを防ぎ、かつメモリの爆発を防ぐための実用コードである。

‘ Option Explicit は絶対の戒律
Option Explicit

‘ Windows API宣言:UIのフリーズを防ぎつつCPU負荷を抑制するためのスリープとメッセージ処理
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32″ (ByVal dwMilliseconds As Long)
End If

‘ 内部状態管理
Private m_CancelRequested As Boolean
Private m_StartTime As Double

‘ キャンセルフラグの取得
Public Property Get IsCancelRequested() As Boolean
IsCancelRequested = m_CancelRequested
End Property

‘ プログレスバーの更新とメッセージポンプの強制実行
Public Sub UpdateProgress(ByVal current As Long, ByVal total As Long, ByVal statusMessage As String)
Dim percent As Double

If total > 0 Then
percent = CDbl(current) / CDbl(total)
Else
percent = 0
End If

‘ UIコントロールの更新(ラベル、幅など)
Me.LblStatus.Caption = statusMessage & ” (” & current & ” / ” & total & “)”
Me.BarCore.Width = Int((Me.BarBase.Width – 4) percent)
Me.LblPercent.Caption = Format(percent, “0.0%”)

‘ 重要:Windowsメッセージキューを処理し、UIの「応答なし」を防ぐ
‘ 同時に、連続呼び出しによるCPU100%張り付きを防ぐため微小なウェイトを入れる
DoEvents
Sleep 10

‘ イベントドリブンでキャンセルボタンが押されたかを判定
If m_CancelRequested Then
Err.Raise 9999, “ExportEngine”, “ユーザーによって処理がキャンセルされました。”
End If
End Sub

Private Sub UserForm_Initialize()
m_CancelRequested = False
m_StartTime = Timer
End Sub

Private Sub BtnCancel_Click()
m_CancelRequested = True
Me.BtnCancel.Enabled = False
Me.LblStatus.Caption = “キャンセル処理中… 終了を待っています。”
End Sub

—

3. 大規模アセンブリのエクスポートとメモリ最適化の極意

SolidWorks APIにおいて、アセンブリを開閉する処理は最もメモリを消費する。特に `ISldWorks.OpenDoc7` を用いる際、軽量モード(Lightweight)や、表示なし(Silent)モードを適切に使い分けなければ、すぐにメモリ不足に陥る。

以下に、大量のアセンブリファイルを順次読み込み、STEP形式でサイレントエクスポートしながら、メモリリークを完全に封じ込めるメインエンジンのコードを示す。

【実装コード:メイン処理モジュール `ModAssemblyExporter`】

Option Explicit

Public Sub ExecuteBatchExport()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks

‘ パフォーマンス向上のための環境設定(画面描画・警告の抑制)
Dim userPromptState As Boolean
Dim safetyState As Boolean
userPromptState = swApp.SetUserPreferenceToggle(swUserPreferenceToggle_e.swStopDebuggingOnSpecialException, False)

‘ 処理対象のファイルパスリスト(実際にはファイルシステムやDBから取得)
Dim fileList() As String
ReDim fileList(2)
fileList(0) = “C:\Data\Assembly_A.sldasm”
fileList(1) = “C:\Data\Assembly_B.sldasm”
fileList(2) = “C:\Data\Assembly_C.sldasm”

Dim frmProgress As CProgressForm
Set frmProgress = New CProgressForm
frmProgress.Show vbModeless ‘ モードレス表示でバックグラウンド感を演出

On Error GoTo ErrorHandler

Dim i As Long
Dim totalFiles As Long
totalFiles = UBound(fileList) – LBound(fileList) + 1

For i = LBound(fileList) To UBound(fileList)
‘ 進捗更新とキャンセルチェック
frmProgress.UpdateProgress i, totalFiles, “処理中: ” & Dir(fileList(i))

‘ 個別アセンブリの処理を実行(スコープを分離してメモリを強制回収)
ProcessSingleAssembly swApp, fileList(i)

Next i

frmProgress.UpdateProgress totalFiles, totalFiles, “すべての処理が完了しました。”
Sleep 1000

Unload frmProgress
Set frmProgress = Nothing
MsgBox “一括エクスポートが正常に完了しました。”, vbInformation, “完了”
Exit Sub

ErrorHandler:
If Not frmProgress Is Nothing Then
Unload frmProgress
Set frmProgress = Nothing
End If

If Err.Number = 9999 Then
MsgBox Err.Description, vbExclamation, “処理中断”
Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End If
End Sub

Private Sub ProcessSingleAssembly(ByRef swApp As SldWorks.SldWorks, ByVal filePath As String)
Dim swModel As SldWorks.ModelDoc2
Dim openError As Long
Dim openWarning As Long

‘ 1. ドキュントのサイレントオープン(メモリ効率化のため非表示、または軽量化を検討)
‘ ※大規模アセンブリでは swOpenDocOptions_Silent を必ず使用する
Set swModel = swApp.OpenDoc6(filePath, swDocumentTypes_e.swDocASSEMBLY, swOpenDocOptions_e.swOpenDocOptions_Silent, “”, openError, openWarning)

If swModel Is Nothing Then
Exit Sub ‘ 開けなかった場合はスキップ
End If

‘ 2. エクスポート処理(例:STEP 214)
Dim exportPath As String
exportPath = Left(filePath, InStrRev(filePath, “.”) – 1) & “.step”

Dim saveError As Long
Dim saveWarning As Long

‘ 拡張子に応じたエクスポートオプションの適用
‘ ※SolidWorksのバージョンによるAPI仕様の変化に注意
Dim boolstatus As Boolean
boolstatus = swModel.Extension.SaveAs(exportPath, 0, swSaveAsVersion_e.swSaveAsCurrentVersion, Nothing, saveError, saveWarning)

‘ 3. 極めて重要:ドキュメントの明示的な閉鎖とメモリ解放
‘ SolidWorksセッションからモデルを完全にアンロードする
swApp.CloseDoc swModel.GetTitle

‘ 4. COMオブジェクトの参照を完全に断つ(GCへのリクエスト)
Set swModel = Nothing

‘ 5. 強制的なメモリ断片化の抑制(Windowsヒープへの配慮)
DoEvents
End Sub

—

4. チーフアーキテクトが教える「現場の急所」とアンチパターン

長年のレガシーシステム保守や大規模案件の現場において、VBAによるSolidWorks連携システムが陥る「罠」は決まっている。以下の鉄則を遵守せよ。

① `On Error Resume Next` の乱用は死を意味する

エラーを隠蔽すると、開いたアセンブリがメモリ上に残ったまま次のループに進むため、メモリリークの連鎖を引き起こし、やがてSolidWorksプロセスがクラッシュする。エラーは必ずキャッチし、異常系であっても確実に `CloseDoc` を通る構造にしなければならない。

② モードレスフォーム(`vbModeless`)の罠

プログレスバーを表示するために `vbModeless` でフォームを表示する際、ユーザーがSolidWorksのメインウィンドウや他のアプリケーションをクリックしてアクティブにすると、VBAの実行コンテキストとの間でフォーカス競合が発生し、予期せぬ挙動を示すことがある。
これを防ぐため、処理中はメインウィンドウを無効化(Disable)するAPI(`EnableWindow` 等)を併用するか、ユーザーの介入を完全に遮断する設計が望ましい。

③ 複数バージョン混在環境への配慮

社内システム管理者が直面する最大の悪夢は「SolidWorks 2022と2024が混在するPC環境」である。
早期リバインディング(事前バインディング: `Dim swApp As SldWorks.SldWorks`)はパフォーマンス上有利だが、バージョン間のタイプライブラリの差異でコンパイルエラーを起こすリスクがある。
完全な互換性を担保する必要がある極限の現場では、あえて遅延バインディング(`CreateObject(“SldWorks.Application”)`)を採用し、実行時動的解決を図るアーキテクチャも視野に入れよ。

—

5. 結びにかえて

VBAは、しばしば「おもちゃの言語」と揶揄される。しかし、その背後にあるWindows APIのメカニズムを理解し、COMオブジェクトのライフサイクルを完全に支配する者にとって、VBAは巨大なCADエンジンを意のままに操るための鋭利なメスへと変貌する。

プログレスバーが滑らかに動き、背後で巨大なアセンブリ群が狂いなく変換されていく――。その静寂で確実な自動化の達成感こそが、真のエンジニアリングの醍醐味である。

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