【実務・中級編】【上級プロフェッショナル向け】大量図面の一括変換における「メモリリーク」対策:一定数処理ごとのAcadApplication再起動とセッション復元 – AutoCAD VBA解析バイブル

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

大量図面一括変換における「メモリリーク」の呪縛を解く:セッション・リフレッシュ戦略

「数千枚の図面を、最新のDWG形式に一括変換するツールを作りました。しかし、300枚を超えたあたりでAutoCADが重くなり、最後にはクラッシュして止まってしまいます。原因は何でしょうか?」

もしあなたが、このような相談をエンジニアから受けたなら、私は即座にこう答えるだろう。
「君は、オブジェクトのライフサイクルではなく、プロセスの寿命を設計していない」と。

AutoCAD VBAの世界において、単なる `For Each` ループによるファイル処理は、未熟なエンジニアが陥る最大の罠だ。AutoCADという巨大なアプリケーション、その内部に蓄積される膨大なキャッシュ、COMオブジェクトの参照残存、そしてUndoスタック……これらが複雑に絡み合い、メモリは雪だるま式に膨れ上がっていく。

本稿では、数千枚規模のバッチ処理を「完遂」させるために必要な、「Orchestrator(オーケストレーター)パターン」による設計思想と、その実装コードを伝授する。

1. なぜ、あなたのバッチ処理は「死ぬ」のか

なぜ、単純なループ処理ではメモリ枯渇(Memory Exhaustion)が起きるのか。その理由は、AutoCADのアーキテクチャの本質にある。

COMオブジェクトの「残留」

VBAから図面(Document)やエンティティ(Entity)を操作する際、それらはすべてCOM(Component Object Model)を介して制御される。コード上で `Set obj = Nothing` と記述したとしても、AutoCAD内部のメモリ管理や、他の非表示の参照、あるいはエラー発生時のスタックによって、オブジェクトが完全に解放されないケースが多々ある。

内部キャッシュとUndoスタックの肥大化

図面を開き、保存し、閉じる。この一連の動作を繰り返すだけで、AutoCADの内部メモリには「以前のセッションの残骸」が蓄積される。特に、図面変換(Version Conversion)を行う場合、データベースの再構築が頻繁に発生し、メモリの断片化(Fragmentation)が加速する。

結論:単一のAutoCADプロセスで長時間稼働させることは、物理的に不可能である。

2. 解決策:Orchestrator(指揮者)によるセッション制御

堅牢な設計とは、エラーが起きないように祈ることではない。「エラーが起きても、システム全体が崩壊しないように設計すること」だ。

我々が採用すべきは、「AutoCAD本体」と「制御プログラム(Orchestrator)」を分離する設計である。

アーキテクチャの分離

1. Orchestrator (Excel VBA または VBScript):

  • 処理対象のリスト(CSV/DB)を管理する。
  • AutoCADプロセスを起動・終了する権限を持つ。
  • 「どのファイルまで完了したか」というステータス(チェックポイント)を保持する。

2. Worker (AutoCAD VBA):

  • 単一の図面に対して、指定された変換処理を実行する「職人」に徹する。
  • 副作用を最小限にし、処理が終われば即座に制御をOrchestratorに返す。

これにより、一定数(例えば50枚ごと)の処理が終わるたびに、AutoCADプロセス自体を完全に終了(Terminate)させ、メモリをOSに完全に返却することが可能になる。

3. 実装への道:プロダクション・グレードのコード例

ここでは、Excel VBAをOrchestratorとし、AutoCADをWorkerとして制御する、実戦的な実装例を示す。

A. Worker側:AutoCAD内の標準モジュール

このコードは、AutoCAD内で実行される。役割は「一つの図面を確実に処理すること」に限定する。

' [AutoCAD VBA側]
' 引数: targetPath (変換対象ファイル), savePath (保存先), targetVer (保存形式)
Public Sub BatchConvertWorker(targetPath As String, savePath As String, targetVer As Integer)
    On Error GoTo ErrorHandler
    
    Dim dwgDoc As AcadDocument
    Set dwgDoc = ThisDrawing ' 現在開いている図面
    
    ' 1. 図面の整合性チェック(Audit)
    dwgDoc.Audit True
    
    ' 2. 不要な要素のパージ(メモリ節約のため)
    ' ※ 必要に応じて、特定のレイヤーやブロックを削除するロジックを追加
    
    ' 3. 名前を付けて保存 (SaveAs)
    ' AutoCADのバージョン定数を使用して保存
    dwgDoc.SaveAs savePath, targetVer
    
    ' 4. 明示的なクリーンアップ
    ' オブジェクト参照を切り離す
    Set dwgDoc = Nothing
    
    Exit Sub

ErrorHandler:
    ' エラー情報を呼び出し元(Orchestrator)へ伝えるため、
    ' 必要に応じてログファイルや特定のセルへ書き出す設計にする
    Err.Raise Err.Number, "BatchConvertWorker", Err.Description
End Sub

B. Orchestrator側:Excel VBA

ここが「脳」となる。AutoCADのライフサイクルを管理する。

' [Excel VBA側]
Option Explicit

Sub RunMassiveBatchProcess()
    Dim acadApp As Object
    Dim targetFiles As Collection
    Dim i As Long
    Dim batchSize As Integer: batchSize = 50 ' 50枚ごとにAutoCADを再起動
    Dim currentBatchCount As Integer: currentBatchCount = 0
    Dim filePath As String
    Dim savePath As String
    
    ' 1. 処理リストの構築(本来はDBやCSVから読み込む)
    Set targetFiles = LoadFileList("C:\Work\InputFiles\")
    
    On Error Resume Next
    
    For i = 1 To targetFiles.Count
        filePath = targetFiles(i)
        savePath = "C:\Work\OutputFiles\" & Mid(filePath, InStrRev(filePath, "\") + 1)
        
        ' --- AutoCADセッションの開始 ---
        Set acadApp = CreateObject("AutoCAD.Application")
        acadApp.Visible = True ' デバッグ時はTrue、本番はFalse推奨
        
        ' 2. 図面を開いてWorkerを呼び出す
        ' ※ ここではCOM経由でAutoCAD内のマクロを叩く
        On Error Resume Next
        Dim doc As Object
        Set doc = acadApp.Documents.Open(filePath)
        
        If Not doc Is Nothing Then
            ' AutoCAD内のマクロを実行 (コマンドライン経由またはVBScript経由)
            ' 実際の実装では、acadApp.Run を使用するか、
            ' 別のスクリプトファイルとして実行させる
            Call ExecuteWorkerMacro(acadApp, filePath, savePath)
            
            doc.Close False ' 保存せずに閉じる(Worker側でSaveAs済みのため)
            Set doc = Nothing
        End If
        
        ' 3. AutoCADの完全終了(メモリ解放の要)
        acadApp.Quit
        Set acadApp = Nothing
        
        ' --- ステータス管理 ---
        currentBatchCount = currentBatchCount + 1
        Debug.Print "Processed: " & i & "/" & targetFiles.Count
        
        ' 進捗をExcelシートに記録(チェックポイント機能)
        RecordProgress i, filePath, "SUCCESS"
        
        ' 4. 定期的なリフレッシュ(バッチサイズに達したら明示的に待機/ログ出力)
        If currentBatchCount >= batchSize Then
            ' OSにメモリを整理する時間をわずかに与える
            DoEvents
            currentBatchCount = 0
            Debug.Print "--- Batch Refresh Completed ---"
        End If
    Next i
    
    MsgBox "全工程が完了しました。", vbInformation
End Sub

' ※ 実装の詳細は環境に依存するため、概念的なラッパーとして記述
Private Sub ExecuteWorkerMacro(app As Object, fPath As String, sPath As String)
    ' ここで、AutoCAD内のVBA関数をCOM経由で呼び出す
    ' 例: app.Run "BatchConvertWorker", fPath, sPath, 2018
End Sub

4. プロフェッショナルとしての「詰め」:設計の注意点

このアーキテクチャを実戦投入する際、以下の3点を必ず考慮せよ。

① チェックポイント(再開機能)の実装

数千枚の処理中、PCの再起動やネットワークの瞬断は必ず起きる。
`RecordProgress` 関数において、「どのファイルまで完了したか」を外部ファイル(CSVやSQLite等)に逐次書き出すことが不可欠だ。Orchestratorは起動時にそのファイルを読み込み、未処理のインデックスからループを開始しなければならない。

② エラーハンドリングの階層化

  • Workerレベルのエラー: 特定の図面が壊れている場合、その図面を「スキップ」してOrchestratorに通知し、次の図面へ進む。
  • Orchestratorレベルのエラー: AutoCADが応答なし(Not Responding)になった場合、`taskkill` コマンド等を用いてプロセスを強制終了し、プロセスを再生成する「自己修復」ロジックを持つべきだ。

③ ファイルI/Oのオーバーヘッド

大量のファイルを開閉する際、ネットワークドライブへのアクセスはボトルネックになる。可能な限り、「ローカルSSDへのコピー → 処理 → 完了後にネットワークへ戻す」というワークフローを組むことで、スループットは劇的に向上する。

結論

AutoCAD VBAにおける大量処理の極意は、「AutoCADを信じすぎないこと」にある。

一つのプロセスにすべてを委ねるのではなく、プロセスを使い捨て可能な「部品」へと格下げし、それを外部のOrchestratorが統治する。この「分離と制御」の思想こそが、バグの起きない、堅牢な業務自動化ツールを生む唯一の道である。

設計に魂を込めろ。メモリリークは、君の設計の甘さを突いてくる。

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