【極限の堅牢性】SolidWorksバッチ処理における「死」からの復旧:Watchdogアーキテクチャの構築
大量の図面(Drawing)を自動生成し、ビューを配置し、シートフォーマットを制御する。一見、単純なループ処理に見えるこのタスクが、現場ではしばしば「悪夢」へと変貌する。
数千ファイルに及ぶバッチ処理の最中、突如として発生するSolidWorksの強制終了(Access Violation)。標準的な`On Error GoTo`では、プロセスそのものが消滅した瞬間に制御を失い、それまでの数時間の進捗は無に帰す。
シニアエンジニアとして、我々が目指すべきは「エラーを回避するコード」ではない。「エラー(クラッシュ)が発生することを前提とした、自己修復能力を持つシステム」の構築である。
本稿では、Windows APIを駆使し、プロセス監視と状態復旧(Checkpointing)を組み合わせた、プロフェッショナル向けの堅牢なフレームワークの設計思想を詳解する。
1. 敗北の教訓:なぜ `On Error` は無力なのか
VBAの標準的な例外処理は、あくまで「実行中のコード内の論理エラー」を捕捉するためのものだ。SolidWorksのAPI呼び出しにおいて、メモリの不整合やCOMのデッドロックによってプロセスがクラッシュした場合、VBAの実行コンテキストそのものが破壊される。
つまり、「エラーハンドラに遷移する前に、プログラム自体が死ぬ」のだ。
これを克服するには、SolidWorks(Worker)とは別のプロセス(Orchestrator)が、常にSolidWorksの生存を確認し、進捗を外部メモリ(ファイル)に書き出し続けるアーキテクチャが必要不可欠となる。
2. 設計思想:Orchestrator-Worker モデル
堅牢なシステムは、以下の二層構造で設計する。
1. Orchestrator (司令塔):
- Excel VBA または 独立した軽量プロセス。
- 処理対象リストの管理と「現在の進捗(Checkpoint)」の記録。
- Windows APIを用いたSolidWorksプロセスの監視。
- クラッシュ検知時のSolidWorks再起動と処理再開のトリガー。
2. Worker (実行体):
- SolidWorks API。
- 図面生成、ビュー配置などの実務。
- 「完了」のシグナルをOrchestratorへ送る。
3. 実装の真髄:Windows API によるプロセス監視とCheckpoint
ここでは、Orchestrator側に実装すべき、プロセスの生存確認と状態管理のコアロジックを示す。
Windows API の宣言
プロセスの生存確認には、`kernel32.dll` の関数を使用する。
' --- Windows API Declarations ---
If VBA7 Then
Declare PtrSafe Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongPtr
Declare PtrSafe Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As LongPtr, ByRef lpExitCode As Long) As Long
Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
Else
Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, ByRef lpExitCode As Long) As Long
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
End If
Private Const PROCESS_QUERY_INFORMATION As Long = &H400
Private Const STILL_ACTIVE As Long = &H103
プロセス生存監視と復旧ロジックの骨格
以下のコードは、処理対象のリストを回しながら、SolidWorksが生きているかを監視するOrchestratorの概念実装である。
Option Explicit
' 状態を保存するための定数
Private Const CHECKPOINT_FILE As String = "C:\Temp\sw_batch_checkpoint.txt"
''' <summary>
''' バッチ処理のメインルーチン(Orchestrator)
''' </summary>
Sub ExecuteRobustBatch()
Dim targetFiles As Variant
Dim i As Long
Dim startIndex As Long
Dim swApp As Object
' 1. 進捗の復旧 (Checkpoint Check)
startIndex = GetLastCheckpoint()
targetFiles = LoadFileList() ' ファイルリストをロードする関数(仮)
Debug.Print "Starting batch from index: " & startIndex
' 2. 処理ループ
For i = startIndex To UBound(targetFiles)
On Error GoTo ErrorHandler
' SolidWorksの接続確認
Set swApp = GetSolidWorksInstance()
If swApp Is Nothing Then
Debug.Print "SolidWorks not running. Starting new instance..."
Set swApp = StartSolidWorks()
End If
' --- クラッシュ監視の核 ---
' 実際の処理を行う前に、プロセスの健全性を確認
If Not IsProcessAlive(GetProcessID("SLDWORKS.exe")) Then
Debug.Print "Critical: SolidWorks crashed! Attempting recovery..."
' ここでリカバリ処理へ
GoTo RecoveryMode
End If
' 3. 実務処理の呼び出し (Workerへの委譲)
' この中で図面生成やビュー配置を行う
ProcessSingleDrawing swApp, targetFiles(i)
' 4. 成功したらチェックポイントを更新
UpdateCheckpoint i
Debug.Print "Successfully processed: " & targetFiles(i)
Next i
MsgBox "Batch Processing Completed Successfully.", vbInformation
Exit Sub
RecoveryMode:
' クラッシュ検知後の再起動とループ再開のロジック
Debug.Print "Recovering... Restarting SolidWorks."
' ここでプロセスをクリーンアップし、再度ループの開始位置に戻る
Resume ExecuteRobustBatch
ErrorHandler:
Debug.Print "Non-fatal error: " & Err.Description
Resume Next
End Sub
''' <summary>
''' プロセスが生存しているか確認する
''' </summary>
Function IsProcessAlive(pid As Long) As Boolean
If pid = 0 Then IsProcessAlive = False: Exit Function
Dim hProcess As LongPtr
Dim exitCode As Long
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, pid)
If hProcess <> 0 Then
GetExitCodeProcess hProcess, exitCode
IsProcessAlive = (exitCode = STILL_ACTIVE)
CloseHandle hProcess
Else
IsProcessAlive = False
End If
End Function
' --- 補助関数群 (実装のヒント) ---
Sub UpdateCheckpoint(index As Long)
' インデックスをファイルに書き出す(原子性を確保するため、一時ファイル経由での上書きを推奨)
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim ts As Object
Set ts = fso.CreateTextFile(CHECKPOINT_FILE, True)
ts.WriteLine index
ts.Close
End Sub
Function GetLastCheckpoint() As Long
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(CHECKPOINT_FILE) Then
GetLastCheckpoint = CLng(fso.OpenTextFile(CHECKPOINT_FILE).ReadAll)
Else
GetLastCheckpoint = 0
End If
End Function
' ※ GetProcessID, GetSolidWorksInstance, StartSolidWorks 等の
' 実装は環境に合わせて適切に記述すること。
4. メモリ管理の真髄:COMオブジェクトの「完全なる解放」
Worker(SolidWorks内での処理)において、クラッシュを誘発する最大の要因は、COMオブジェクトの参照カウントの不整合である。
図面生成のループ内で、`DrawingDoc`, `ModelDoc2`, `View`, `Sheet` といったオブジェクトを生成し続けると、メモリリークが発生し、最終的にSolidWorksは「リソース不足」でクラッシュする。
伝説的なエンジニアは、単に `Set obj = Nothing` と書くだけでは満足しない。以下の原則を徹底する。
1. スコープの最小化: オブジェクトの寿命を可能な限り短くする。
2. 明示的解放の連鎖: 親オブジェクトを解放する前に、必ず子オブジェクトを `Nothing` にする。
3. 再生成のタイミング: 数十ファイルごとに、一度SolidWorksのインスタンス自体を破棄し、再起動(Recycle)させる。これは、メモリの断片化を防ぐ最も確実な手段である。
Sub ProcessSingleDrawing(swApp As Object, filePath As String)
Dim swModel As Object ' ModelDoc2
Dim swDraw As Object ' DrawingDoc
Dim swView As Object ' View
On Error GoTo CleanUp
' 1. ファイルオープン
Set swModel = swApp.OpenDoc6(filePath, 3, 1, 0, 0, 0) ' swDocDRAWING = 3
If swModel Is Nothing Then Exit Sub
Set swDraw = swModel
' --- ここにビュー配置などの複雑なロジックを記述 ---
' 例: Set swView = swDraw.GetFirstView
' 2. 保存
swModel.Save3 1, 0 ' swSaveAsOptions_Silent
' 3. クローズ
swApp.CloseDoc swModel.GetTitle
CleanUp:
' 重要:逆順で解放する
Set swView = Nothing
Set swDraw = Nothing
Set swModel = Nothing
' 参照カウントを確実にゼロにする
End Sub
5. 結論:レガシーを支配する者の視点
SolidWorks VBAによる自動化は、単なる「操作の記録」ではない。それは、不安定な巨大アプリケーション(SolidWorks)を、外部から制御・監視し、その脆弱性をシステム設計で補完する「制御工学」である。
- Orchestratorによる監視
- Checkpointによる状態保持
- Windows APIによるプロセス検知
- 厳格なCOMオブジェクトのライフサイクル管理
これらを実装したとき、あなたのシステムは「止まらないシステム」へと昇華する。エラーを恐れるな。エラーが起きた後の挙動を設計せよ。それが、真のアーキテクトへの道である。
