SolidWorks APIの「限界」を突破せよ:大規模アセンブリ処理におけるUIフリーズを撲滅する進捗管理術
SolidWorksのアセンブリ処理において、最も避けるべきは「SolidWorksが固まり、OSから『応答なし』と判定される時間」だ。大規模アセンブリをループ処理でエクスポートする際、何も考えずに`DoEvents`を連打するコードは、エンジニアの恥と心得よ。
今日は、マルチスレッドが使えないVBAという制約の中で、Windows APIとタイマーを駆使して「UIのレスポンスを維持する」ための、プロダクションレベルの設計手法を伝授する。
—
1. なぜ「`DoEvents`」だけでは不十分なのか
多くの初学者は、重い処理の中に`DoEvents`を置けばUIが固まらないと信じている。しかし、これは誤りだ。`DoEvents`はOSへのキューを解放するだけ。SolidWorksのCOMインターフェースはシングルスレッドであり、重い計算処理(再構築やエクスポート)が走っている間、SolidWorksは「計算」というタスクに全リソースを奪われ、ユーザー入力への応答性を完全に失う。
真のプロは、「タイマーによる非同期に近い擬似的な分割処理」を実装する。
2. 設計思想:UI駆動型バッチ処理
今回の実装の肝は以下の3点だ。
1. 処理の細分化(ステートマシン化): 一気に全ファイルを処理するのではなく、1パーツごとに処理を切り分ける。
2. Windows APIによるタイマー制御: `SetTimer`を用いて、メインスレッドの占有時間を強制的に解放する。
3. 進捗の状態保持: 現在どのコンポーネントを処理中か、クラスモジュールで状態を管理する。
—
3. 実装コード:堅牢なプログレス・コントローラー
まずは、ユーザーフォームにプログレスバーを配置し、以下のコードを標準モジュールとフォームに記述する。
標準モジュール:タイマー制御の核
‘ Windows APIの宣言
Public Declare PtrSafe Function SetTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Public Declare PtrSafe Function KillTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
‘ 処理状態を保持するグローバル変数(クラス化を推奨)
Public G_IsProcessing As Boolean
Public G_CurrentIndex As Long
‘ タイマーコールバック用プロシージャ
Public Sub TimerProc(ByVal hwnd As LongPtr, ByVal uMsg As LongPtr, ByVal nIDEvent As LongPtr, ByVal dwTime As LongPtr)
‘ 処理中ならメインの処理ループを呼び出す
If G_IsProcessing Then
Call MainExportLoop
End If
End Sub
フォームモジュール:UIのフリーズを防ぐロジック
Private Sub btnStart_Click()
G_CurrentIndex = 0
G_IsProcessing = True
‘ 100ミリ秒ごとに処理を実行することでメインスレッドに隙間を作る
SetTimer Application.hWndAccessApp, 1, 100, AddressOf TimerProc
End Sub
Private Sub MainExportLoop()
Dim swApp As SldWorks.SldWorks: Set swApp = Application.SldWorks
Dim swModel As ModelDoc2: Set swModel = swApp.ActiveDoc
‘ 終了条件
If G_CurrentIndex >= MaxComponents Then
KillTimer Application.hWndAccessApp, 1
G_IsProcessing = False
MsgBox “エクスポート完了”
Exit Sub
End If
‘ ここで1つだけ処理を実行
ProcessComponent G_CurrentIndex
‘ プログレスバー更新
Me.ProgressBar1.Value = (G_CurrentIndex / MaxComponents) 100
G_CurrentIndex = G_CurrentIndex + 1
End Sub
—
4. プロダクション環境における「禁忌」
この設計を採用するにあたり、現場でよくある失敗を警告しておく。
- データベース(SQL Server等)との頻繁な接続:
APIでファイル名を取得するたびにDBを叩くのは愚の骨頂だ。データは処理開始前に`Scripting.Dictionary`等にすべてロードし、オンメモリで完結させろ。I/O待ちがUIのレスポンスを殺す。
- SolidWorksの再構築(Rebuild)の罠:
`swModel.ForceRebuild3`は強力だが、大規模アセンブリではフリーズの主原因になる。必要なパーツ以外は`suppress`(抑制)してからエクスポートする戦略をとれ。
- エラーハンドリング:
タイマー処理内で例外が発生するとSolidWorksごと落ちる。`On Error GoTo`は全ループの入り口に必ず配置し、異常時には`KillTimer`を確実に実行してタイマーを死なせる設計を徹底せよ。
結論
大規模アセンブリの自動化は、単なるスクリプトの羅列ではない。「SolidWorksという巨大なモンスターをいかに制御下におくか」というアーキテクチャの戦いだ。
今回紹介した「タイマーを用いた細分化処理」は、UIフリーズを回避するだけでなく、処理の途中でユーザーが「キャンセル」ボタンを押せるようにするための唯一の現実的な解である。
コードを書く前に、まずは「この処理はメインスレッドを何ミリ秒占有するか」を考えろ。その意識を持つ者だけが、真の業務自動化エンジニアとして評価される。健闘を祈る。
