SolidWorks VBAを掌握する極限の知見:クラッシュを前提とした「不屈のバッチ処理フレームワーク」の設計
現場のエンジニア諸君、よく聞いてほしい。
「`On Error GoTo` を書いているから大丈夫です」
「エラーが出たら手動で再実行すればいいだけです」
もし君たちがそう考えているなら、君たちのコードは「ツール」であっても「システム」ではない。プロフェッショナルが構築すべきは、ユーザーの作業時間を奪わない、「失敗を前提とした堅牢な自動化エンジン」だ。
特に図面(Drawing)の自動生成やビュー配置を伴うバッチ処理は、メモリ消費が激しく、APIの呼び出しタイミングによってはSolidWorks自体が「Access Violation」を起こして強制終了(クラッシュ)することが珍しくない。VBAの標準的なエラーハンドリングは、メモリレベルのクラッシュに対しては無力だ。プロセスが死ねば、VBAの制御権も共に消滅するからだ。
本稿では、レガシーなSolidWorks APIを現代的な設計思想でラップし、「クラッシュしても、再起動後に『どこまで終わったか』を自動判別して続行する」リカバリーシステムの構築術を伝授する。
1. なぜ君のコードは「脆弱」なのか:エラーハンドリングの誤解
多くの開発者が陥る罠は、「エラー処理(Error Handling)」と「リカバリー設計(Recovery Design)」を混同していることだ。
- エラーハンドリング (`On Error`): APIが返したエラーコード(`swErrApiError`など)を捕捉し、プログラムの進行を制御するもの。これは「予期された例外」に対処するためのものだ。
- リカバリー設計: SolidWorksという「制御不能な巨大なプロセス」が、突然消滅することを前提とした設計。これは「予期せぬ破滅」に対処するためのものだ。
図面生成バッチにおいて、メモリ不足や複雑なジオメトリの計算でSolidWorksが落ちた場合、`On Error` は一瞬で意味をなさなくなる。だからこそ、「状態(State)を外部に切り出す」という設計思想が不可欠なのだ。
2. アーキテクチャ設計:State Machine(状態遷移)モデル
堅牢なバッチ処理を実現するには、以下の3要素を分離して設計せよ。
1. Task List (タスクリスト): 処理すべきファイルパスのリスト。
2. Checkpoint (チェックポイント): 現在どのファイルまで完了したかを記録する外部ファイル(JSON, CSV, またはテキスト)。
3. Worker (ワーカー): SolidWorks APIを叩き、実際の図面操作を行う主体。
設計のキモ:Atomicity(原子性)の確保
「1つのファイルに対する処理」を一つの原子(Atom)として扱う。
「ファイルを開く」→「ビューを配置する」→「保存する」→「完了フラグを外部に書き込む」
この「完了フラグの書き込み」が、次のファイル処理の前に確実に実行されることが、リカバリーの生命線となる。
3. 実装:プロダクション・グレードのリカバリー・フレームワーク
以下に、実務でそのまま転用可能な、チェックポイント機能を備えたバッチ処理のテンプレートを示す。ここでは、進捗管理を外部のテキストファイルで行う設計を採用する。
Module: `modBatchEngine`
Option Explicit
' --------------------------------------------------------------------------------
' プロフェッショナル向けバッチ処理エンジン
' 設計思想: 外部チェックポイントによるクラッシュからの自動復旧
' --------------------------------------------------------------------------------
Private Const CHECKPOINT_FILE As String = "C:\Temp\sw_batch_checkpoint.txt"
Private Const LOG_FILE As String = "C:\Temp\sw_batch_error.log"
Public Sub ExecuteRobustBatch()
Dim swApp As SldWorks.SldWorks
Dim targetFiles As Collection
Dim filePath As Variant
Dim currentIdx As Long
Dim lastCompletedIdx As Long
' 1. 初期化
Set swApp = Application.SldWorks
Set targetFiles = GetFileList("C:\Projects\Drawings\*.drwdot") ' 対象リスト取得
' 2. 前回終了時のチェックポイント読み込み
lastCompletedIdx = LoadCheckpoint()
Debug.Print "Resume Mode: Last completed index was " & lastCompletedIdx
' 3. バッチループ
currentIdx = 1
For Each filePath In targetFiles
' すでに完了しているインデックスはスキップ
If currentIdx > lastCompletedIdx Then
Debug.Print "Processing [" & currentIdx & "/" & targetFiles.Count & "]: " & filePath
' --- 処理の実行 (ここがクリティカルセクション) ---
Dim success As Boolean
success = ProcessDrawingTask(swApp, CStr(filePath))
' ----------------------------------------------
If success Then
' 成功時のみチェックポイントを更新(外部ファイルへ書き出し)
SaveCheckpoint currentIdx
Else
' 致命的なエラー(APIエラー等)が発生した場合はログを残して中断
LogError "Task Failed", filePath
MsgBox "Critical error encountered. Check log: " & LOG_FILE
Exit Sub
End If
End If
currentIdx = currentIdx + 1
Next filePath
MsgBox "Batch Process Completed Successfully!", vbInformation
' 全完了したらチェックポイントをクリア
Kill CHECKPOINT_FILE
End Sub
' --------------------------------------------------------------------------------
' 個別タスク実行ユニット
' --------------------------------------------------------------------------------
Private Function ProcessDrawingTask(swApp As SldWorks.SldWorks, filePath As String) As Boolean
On Error GoTo ErrHandler
Dim swDoc As SldWorks.DrawingDoc
Dim swModel As SldWorks.ModelDoc2
' ファイルオープン
Set swDoc = swApp.OpenDoc6(filePath, swDocDRAWING, swOpenDocOptions_Silent, "", 0, 0)
If swDoc Is Nothing Then
Err.Raise vbObjectError + 513, "ProcessDrawingTask", "Failed to open file."
End If
Set swModel = swDoc
' --- 【実務的ロジックの例】ビュー配置・シート制御 ---
' ここに実際のAPI操作を記述する
' 例: swDoc.CreateDrawViewFromModelView3(...)
' --------------------------------------------------
' 変更の保存
Dim errors As Long, warnings As Long
swModel.Save3 swSaveAsOptions_Silent, errors, warnings
' クローズ
swApp.CloseDoc swModel.GetTitle
ProcessDrawingTask = True
Exit Function
ErrHandler:
LogError Err.Description, filePath
ProcessDrawingTask = False
' クラッシュを防ぐため、エラー時はドキュメントを閉じる試行を行う
If Not swModel Is Nothing Then swApp.CloseDoc swModel.GetTitle
End Function
' --------------------------------------------------------------------------------
' 永続化レイヤー (Persistence Layer)
' --------------------------------------------------------------------------------
Private Sub SaveCheckpoint(idx As Long)
Dim fnum As Integer
fnum = FreeFile
Open CHECKPOINT_FILE For Output As #fnum
Print #fnum, idx
Close #fnum
End Sub
Private Function LoadCheckpoint() As Long
Dim fnum As Integer
Dim content As String
If Dir(CHECKPOINT_FILE) = "" Then
LoadCheckpoint = 0
Exit Function
End If
fnum = FreeFile
Open CHECKPOINT_FILE For Input As #fnum
Line Input #fnum, content
Close #fnum
If IsNumeric(content) Then
LoadCheckpoint = CLng(content)
Else
LoadCheckpoint = 0
End If
End Function
Private Sub LogError(errDesc As String, context As String)
Dim fnum As Integer
fnum = FreeFile
Open LOG_FILE For Append As #fnum
Print #fnum, Now & " | ERROR: " & errDesc & " | CONTEXT: " & context
Close #fnum
End Sub
Private Function GetFileList(pattern As String) As Collection
' 本来はFileSystemObject等を用いて実装
Dim col As New Collection
' ダミーデータ
col.Add "C:\Test\Drawing1.drwdot"
col.Add "C:\Test\Drawing2.drwdot"
col.Add "C:\Test\Drawing3.drwdot"
Set GetFileList = col
End Function
4. 現場で差がつく「プロの作法」
このコードをベースに、君たちが実装を肉付けする際に遵守すべき鉄則を伝える。
① API呼び出しの「重み」を意識せよ
図面のビュー配置(`CreateDrawViewFromModelView3`)は、モデルの再構築を伴う。ループ内でこれを乱発すると、メモリの断片化が進み、SolidWorksが不安定になる。
- 対策: 一定件数(例:50件)ごとに、一度SolidWorksを再起動する、あるいは明示的に `swApp.Quit` して別プロセスとして立ち上げ直す「マルチプロセス・バッチ」への昇華を検討せよ。
② 外部ログへの「コンテキスト」の記録
単に `Err.Description` を書くだけでは、デバッグには不十分だ。
「どのファイルで」「どのメソッドを呼び出した時に」「どのようなパラメータで」失敗したのか。このコンテキストこそが、数時間後の君自身の命を救う。
③ ファイルI/Oの原子性を守る
`SaveCheckpoint` を行う際、ファイルが書き込み中(ロック中)にクラッシュするとチェックポイントファイル自体が破損するリスクがある。
より高度な設計では、`checkpoint.tmp` に一度書き込み、完了後に `checkpoint.txt` へリネームする手法を用いる。これが「データベースのコミット」の考え方だ。
結論:エンジニアの価値は「例外」に宿る
正常なフローを記述するのは、誰にでもできる。
真に価値のあるエンジニアは、「システムが壊れたときに、いかに最小のコストで、いかに自動的に復旧させるか」を設計できる者だ。
SolidWorks APIという、時に気まぐれで巨大な獣を御するには、単なるコマンドの羅列ではなく、このような「堅牢なフレームワーク」という檻が必要なのだ。
この設計思想を、君たちの次のプロジェクトに組み込んでほしい。健闘を祈る。
