【実務・中級編】【上級プロ向け】図面ファイルの完全バッチ処理エンジン:破損ファイル・エラーの例外安全な自動リトライ機構 – SolidWorks VBA解析バイブル

スポンサーリンク
スポンサーリンク

【上級プロ向け】図面ファイルの完全バッチ処理エンジン:破損ファイル・エラーの例外安全な自動リトライ機構

「スクリプトは動いた。でも、3時間後に確認したら途中で止まっていた。」

もし君が、深夜に数千枚の図面を自動生成・変換するバッチジョブを走らせて、翌朝にこのような報告を受けているとしたら、君の設計は「自動化」ではなく、単なる「祈り」だ。

多くの開発者が陥る罠がある。それは「Happy Path(正常系)」のみを想定したコードを書くことだ。SolidWorks APIの世界は、不確実性の塊である。参照ファイルが欠落している、ファイルが他者にロックされている、あるいは予期せぬダイアログが背後で「保存しますか?」と問いかけている……。これら一つひとつが、君のバッチ処理を無慈悲にハングアップさせる。

本稿では、単なる自動化スクリプトではなく、「止まらない、壊さない、確実に記録する」ための、プロダクションレベルのバッチ処理エンジンの設計思想を伝授する。

1. 設計思想:例外安全(Exception Safety)へのパラダイムシフト

プロフェッショナルなエンジニアは、コードを書く前に「何が失敗するか」を定義する。バッチ処理において、以下の3原則は絶対である。

1. Isolation(隔離): 一つのファイルの失敗が、プロセス全体を停止させてはならない。
2. Atomicity(原子性): 操作が中途半端な状態で残ることを防ぐ。失敗したなら、元の状態に戻すか、明確に「失敗」としてマークする。
3. Observability(観測可能性): 「なぜ止まったのか」を、後からログだけで完全に特定できること。`Debug.Print` などは、本番環境ではゴミに等しい。

なぜ `On Error Resume Next` は「毒」なのか

初心者はエラーを無視するために `On Error Resume Next` を多用する。これは最悪の選択だ。エラーを無視するということは、「オブジェクトのメモリ空間が不安定な状態で、次の命令を強行する」ことを意味する。これが原因で、SolidWorksがメモリリークを起こしたり、最悪の場合、OSごとフリーズしたりするのだ。

2. アーキテクチャ:Controller / Worker モデル

堅牢なエンジンを構築するために、処理を「管理層(Controller)」と「実行層(Worker)」に分離する。

  • Controller: ファイルリストを走査し、全体の進捗を管理し、エラー発生時にリトライ戦略を決定する。
  • Worker: 個別のファイルに対してSolidWorks APIを叩く。エラーが発生した場合は、それを「構造化されたエラー情報」としてControllerに返却する。

3. 実装例:堅牢なバッチ処理エンジンのプロトタイプ

以下に、VBAで実装可能な、リトライ機構と詳細なロギングを備えた設計例を示す。これをベースに、君の業務要件を組み込んでほしい。

A. ロガー・クラス (clsLogger)

まず、エラーを構造的に記録するためのクラスを定義する。

' Class Module: clsLogger
Option Explicit

Private fso As Object
Private logFile As Object

Private Sub Class_Initialize()
    Set fso = CreateObject("Scripting.FileSystemObject")
    ' ログファイルを作成(タイムスタンプ付き)
    Set logFile = fso.CreateTextFile(ThisWorkbook.Path & "\BatchLog_" & Format(Now, "yyyymmdd_hhmmss") & ".csv", True)
    ' ヘッダーの書き込み
    logFile.WriteLine "Timestamp,Level,FileName,ErrorCode,ErrorMessage,RetryCount"
End Sub

Public Sub Log(level As String, fileName As String, errCode As Long, errMsg As String, retryCount As Integer)
    Dim line As String
    line = Format(Now, "yyyy-mm-dd hh:mm:ss") & "," & _
           level & "," & _
           """" & fileName & """," & _
           errCode & "," & _
           """" & errMsg & """," & _
           retryCount
    logFile.WriteLine line
    logFile.Flush ' 即時書き出し(クラッシュ対策)
End Sub

Private Sub Class_Terminate()
    logFile.Close
End Sub

B. メイン・エンジン (modEngine)

ここが心臓部だ。リトライロジックと、SolidWorksの挙動制御を実装する。

' Standard Module: modEngine
Option Explicit

' 構成定数
Private Const MAX_RETRIES As Integer = 3        ' 最大リトライ回数
Private Const RETRY_DELAY_SEC As Double = 2     ' リトライ間の待機時間

Public Sub ExecuteBatchProcess()
    Dim swApp As SldWorks.SldWorks
    Dim logger As New clsLogger
    Dim fileList As Collection
    Dim targetFile As Variant
    Dim successCount As Long: successCount = 0
    Dim failCount As Long: failCount = 0
    
    On Error GoTo FatalError
    
    Set swApp = Application.SldWorks
    
    ' --- 1. ファイルリストの取得 (本来はフォルダ走査等で行う) ---
    Set fileList = GetFileList("C:\Work\Drawings\") 
    
    ' --- 2. 重要:ユーザー操作の抑制 ---
    ' ダイアログを表示させないための設定。これが無いと、深夜に止まる。
    swApp.UserControl = False 
    
    ' --- 3. メインループ ---
    For Each targetFile In fileList
        Dim retryAttempt As Integer
        Dim isSuccess As Boolean: isSuccess = False
        isSuccess = False
        retryAttempt = 0
        
        ' リトライループ
        Do While retryAttempt <= MAX_RETRIES And isSuccess = False
            On Error GoTo TaskError
            
            ' 個別のタスク実行
            PerformDrawingTask swApp, CStr(targetFile)
            
            ' 成功した場合
            isSuccess = True
            successCount = successCount + 1
            
        Loop While retryAttempt < MAX_RETRIES ' (実際にはDo While内で制御)
        
        ' 失敗した場合の処理
        If Not isSuccess Then
            failCount = failCount + 1
            ' 失敗はログに記録して次へ(ここで止まらないのが重要)
        End If
        
        ' エラーハンドリング用のラベルへジャンプするための準備
TaskError:
        If Err.Number <> 0 Then
            retryAttempt = retryAttempt + 1
            logger.Log "ERROR", CStr(targetFile), Err.Number, Err.Description, retryAttempt
            Err.Clear
            
            ' 指数バックオフ(リトライ間隔を徐々に伸ばす)
            If retryAttempt <= MAX_RETRIES Then
                Sleep (RETRY_DELAY_SEC * retryAttempt * 1000) 
            End If
        End If
        On Error GoTo FatalError ' 致命的エラーに戻す
    Next targetFile

    MsgBox "処理完了" & vbCrLf & "成功: " & successCount & vbCrLf & "失敗: " & failCount

    Exit Sub

FatalError:
    MsgBox "致命的なエラーが発生しました。処理を中断します。" & vbCrLf & Err.Description
    ' ここでログに致命的エラーを記録
End Sub

' 個別の図面操作ロジック
Private Sub PerformDrawingTask(swApp As SldWorks.SldWorks, filePath As String)
    Dim swModel As SldWorks.ModelDoc2
    
    ' ファイルを開く(ここで破損ファイルによるエラーが発生しやすい)
    ' OpenDoc6 を使用し、エラーコードを厳密にチェックする
    Dim errors As Long
    Dim warnings As Long
    Set swModel = swApp.OpenDoc6(filePath, swDocDRAWING, swOpenDocOptions_Silent, "", errors, warnings)
    
    If swModel Is Nothing Then
        Err.Raise 1001, , "Failed to open file. Error Code: " & errors
    End If
    
    ' --- ここに図面生成・ビュー配置のメインロジックを記述 ---
    ' 例: swModel.Save3 swSaveAsOptions_Silent, 0, 0
    
    ' 後処理:モデルを閉じる(メモリ解放のため必須)
    swApp.CloseDoc swModel.GetTitle
End Sub

' --- 補助関数 (API宣言等) ---

If VBA7 Then

Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)

Else

Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

End If

Private Function GetFileList(folderPath As String) As Collection ' 簡易的な実装(実際にはFSO等で実装) Dim col As New Collection col.Add folderPath & "drawing1.SLDDRW" col.Add folderPath & "corrupt_file.SLDDRW" ' テスト用破損ファイル col.Add folderPath & "drawing2.SLDDRW" Set GetFileList = col End Function

4. プロフェッショナルのための、さらなる「極限の知見」

コードを書くだけでは足りない。実務で運用するなら、以下の3点にまで意識を研ぎ澄ませてほしい。

① `swApp.UserControl = False` の真価

これは単なる「ダイアログ抑制」ではない。SolidWorksに対して「今、人間は操作していない。すべての意思決定はAPIで行う」と宣言する儀式だ。これを行わずにバッチを回すと、ファイル破損時に「修復しますか?」というダイアログが出た瞬間、プロセスは永久に待機状態(ゾンビプロセス)となる。

② ファイルの生存確認(Pre-flight Check)

`OpenDoc6` を呼ぶ前に、`FileSystemObject` を使ってファイルが存在するか、また「読み取り専用属性」になっていないかを確認せよ。ファイルロック(他者が開いている状態)は、APIエラーよりも先に、OSレベルの整合性として扱うべきだ。

③ メモリの「掃除」を怠るな

バッチ処理の最大の敵は、ループが進むにつれて増大するメモリ消費だ。
`Set swModel = Nothing` を明示的に行うだけでなく、`swApp.CloseDoc` を確実に行い、SolidWorksの内部メモリからドキュメントを完全に解放すること。これを行わないと、数千枚の処理中にSolidWorksがメモリ不足で自壊する。

結論

堅牢な自動化エンジンとは、「エラーが起きないコード」ではなく、「エラーが起きた時に、いかに優雅に、かつ迅速に復旧し、次の任務へ移れるか」を設計したコードのことだ。

君が書いたコードが、深夜のサーバー室で、誰にも知られず、しかし確実に、完璧なログを残しながら任務を遂行する。それこそが、真のアーキテクトの仕事である。

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