図面自動生成の極限:イベント駆動型監視と例外安全なリトライ機構の実装
大規模アセンブリをソースとする図面(Drawing)の自動生成プロセスにおいて、多くのエンジニアが直面する「壁」がある。それは、単なるロジックの誤りではない。「ネットワークの瞬断」「COMオブジェクトのデッドロック」「メモリリークによるリソース枯渇」といった、制御不能な外部要因によるシステムの崩壊だ。
標準的な `On Error GoTo` によるエラーハンドリングでは、これらに対抗することは不可能である。一度COM例外(`0x80010108` 等)が発生すれば、SolidWorksのプロセス自体が不安定になり、後続の処理はすべて泥沼に沈む。
本稿では、単なる「自動化」を超え、ミッションクリティカルな環境に耐えうる「防衛的設計(Defensive Design)」に基づいた、イベント駆動型の監視と指数バックオフを用いたリトライ機構の極意を伝授する。
1. なぜ標準的なエラーハンドリングは無力なのか
大規模な図面生成では、数千個の部品を含むアセンブリへの参照、重いビューの配置、そしてネットワークストレージへの書き込みが連続する。このプロセスにおいて、エラーは「発生するもの」ではなく、「必ず発生するもの」として設計しなければならない。
COMの脆弱性とゾンビプロセス
VBAからSolidWorks APIを叩く際、ネットワーク遅延によって応答が遅れると、VBA側はタイムアウトを検知できず、COMインターフェースの待機状態(Deadlock)に陥ることがある。この状態で無理にリトライを繰り返すと、メモリ上に解放されない「ゾンビオブジェクト」が蓄積し、最終的にシステム全体がクラッシュする。
解決策:ステートフルな監視と指数バックオフ
我々が実装すべきは、単なるリトライループではない。
1. State Machine(状態遷移)による工程管理: どの工程(ファイルオープン、ビュー配置、保存)で失敗したかを厳密に記録する。
2. Exponential Backoff(指数バックオフ): 失敗直後に即座にリトライせず、待機時間を $2^n$ で増加させることで、ネットワークやI/Oの回復を待つ。
3. Watchdog(監視)概念: プロセスの生存確認を外部(または疑似的なイベントフック)から行う。
2. アーキテクチャの実装:堅牢なリトライエンジンの構築
以下に、堅牢な図面生成を実現するためのクラスモジュール構造の設計例を示す。ここでは、単一のプロシージャに書くのではなく、「実行コンテキスト」をカプセル化したクラスとして実装することが、メモリ管理の観点から極めて重要である。
クラスモジュール: `clsResilientAutomation`
'---------------------------------------------------------------------------
' Class: clsResilientAutomation
' Purpose: 例外安全な図面生成プロセスを管理する
'---------------------------------------------------------------------------
Option Explicit
Private swApp As SldWorks.SldWorks
Private currentStep As String
Private maxRetries As Integer
Private baseDelay As Double ' 秒単位
' 定数の定義
Private Const ERR_NETWORK_TIMEOUT As Long = 1001
Private Const ERR_COM_EXCEPTION As Long = 1002
' プロパティ
Public Property Let MaxRetries(Value As Integer): maxRetries = Value: End Property
Public Property Let BaseDelay(Value As Double): baseDelay = Value: End Property
' ---------------------------------------------------------------------------
' メイン実行メソッド: 指数バックオフを用いたリトライ制御
' ---------------------------------------------------------------------------
Public Function ExecuteDrawingProcess(ByVal templatePath As String, ByVal modelPath As String) As Boolean
Dim retryCount As Integer
Dim success As Boolean
Dim waitTime As Double
retryCount = 0
success = False
Do While retryCount <= maxRetries And Not success
On Error GoTo ErrorHandler
' ステップの記録(状態遷移)
currentStep = "INITIALIZING_DRAWING"
' --- プロセスの実行 ---
' 実際の実装では、ここに図面生成のロジックを分割して呼び出す
Call PerformDrawingGeneration(templatePath, modelPath)
' ----------------------
success = True
Debug.Print "Process completed successfully at " & Now
GoTo ExitPoint
ErrorHandler:
retryCount = retryCount + 1
' エラーの種類を判定(本来はHRESULT等で詳細に判定すべき)
Debug.Print "Error in [" & currentStep & "] at " & Now & ": " & Err.Description
' 致命的なエラー(メモリ不足や論理エラー)の場合は即時終了
If IsFatalError(Err.Number) Then
Debug.Print "Fatal error detected. Aborting."
GoTo ExitPoint
End If
' 指数バックオフの計算: baseDelay * 2^(retryCount-1)
waitTime = baseDelay * (2 ^ (retryCount - 1))
Debug.Print "Retry #" & retryCount & " in " & waitTime & " seconds..."
' Windows APIを使用して正確な待機を行う(Sleepはミリ秒単位)
SleepAPI CStr(waitTime * 1000)
' リソースの強制解放(重要:COMの連鎖崩壊を防ぐ)
Call CleanupResources
If retryCount > maxRetries Then
Debug.Print "Max retries exceeded."
End If
Resume ErrorHandler
ExitPoint:
Call CleanupResources
ExecuteDrawingProcess = success
End Function
' ---------------------------------------------------------------------------
' プライベートメソッド: リソースの明示的解放
' ---------------------------------------------------------------------------
Private Sub CleanupResources()
' COMオブジェクトの参照を完全に断ち切る
' VBAの Set obj = Nothing は、参照カウントを減らすだけで
' 内部的な解放が遅れることがあるため、慎重に行う
On Error Resume Next
' ここで保持しているすべてのSolidWorksオブジェクトをNothingにする
' 例: Set swDoc = Nothing, Set swView = Nothing
DoEvents ' OSに制御を戻し、メモリ解放の猶予を与える
End Sub
' 致命的なエラーかどうかの判定ロジック
Private Function IsFatalError(ByVal errNum As Long) As Boolean
Select Case errNum
Case 7 ' Out of memory
IsFatalError = True
Case Else
IsFatalError = False
End Select
End Function
' Windows APIの宣言 (標準モジュールではなく、クラス内で使う場合は注意)
If VBA7 Then
Private Declare PtrSafe Sub SleepAPI Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Else
Private Declare Sub SleepAPI Lib "kernel32" (ByVal dwMilliseconds As Long)
End If
3. メモリ最適化とWindows APIの極限知見
プロフェッショナルな自動化エンジニアが、シニア層とそれ以外を分けるポイントは「メモリのライフサイクル」への理解だ。
明示的解放の真実
VBAにおいて `Set obj = Nothing` は、そのオブジェクトの参照カウントを1つ減らすに過ぎない。SolidWorksのような巨大なアプリケーションを操作している場合、参照が循環(Circular Reference)してしまうと、`Nothing` を代入してもメモリ上には居座り続ける。
これを防ぐために、以下の戦略を取る:
1. スコープの最小化: オブジェクトをクラスのメンバ変数(Private変数)として長く保持せず、可能な限りプロシージャ内のローカル変数として定義する。
2. DoEvents の戦略的配置: 重いループの合間に `DoEvents` を挟むことで、Windowsのメッセージキューを処理させ、OSレベルでのリソース管理を促す。
3. APIによるプロセス監視: ネットワーク切断を検知する場合、`GetFileAttributes` などのWindows APIを使用して、ファイルシステムへのアクセスが物理的に可能かを、SolidWorksに命令を投げる前に「予兆」として検知する。
指数バックオフの数学的妥当性
なぜ指数バックオフなのか? それは、リトライの頻度がネットワークの輻輳(Congestion)を悪化させるのを防ぐためだ。
- 失敗直後のリトライ:ネットワークが混雑している場合、さらに負荷をかける。
- 指数的増加:負荷を段階的に下げることで、ネットワーク機器の再起動や、ファイルサーバーのI/O待ちが解消される確率を最大化する。
4. 結論:レジリエンスのあるシステムへ
図面自動生成の自動化は、単に「ボタンを押して図面が出る」ことではない。「どのような不測の事態が起きても、システムが自律的に回復を試み、最終的に整合性を保った状態で終了する」ことこそが、真の自動化である。
今回紹介した「状態管理」「指数バックオフ」「明示的なリソース解放」の3本柱を組み合わせることで、あなたの作成するVBAシステムは、単なるスクリプトから、企業のインフラを支える「堅牢なエンジン」へと昇華する。
技術の深淵へようこそ。次は、マルチスレッド的な挙動を模倣するための、別プロセスによるWatchdogの実装について語ろう。
