【実務・中級編】【実務中級者向け】Excelリストに基づいた図面の一括オープン&特定ブロック置換後の自動保存ツール – AutoCAD VBA解析バイブル

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

[Mastering AutoCAD VBA] バッチ処理の極意:設計思想が分ける「スクリプト」と「プロダクションツール」の違い

多くの開発者が「AutoCAD VBAを使って、Excelリストに基づいた図面の一括書き換えツールを作りたい」と考えている。しかし、ネット上の断片的なコードを繋ぎ合わせただけの「スクリプト」は、実務の荒波では必ずと言っていいほど瓦解する。

なぜか? それは、「図面オブジェクトのライフサイクル」と「例外に対する防御設計」、そして「ファイルロックの管理」という、プロダクションレベルの設計思想が欠落しているからだ。

本記事では、単なるコピペで終わらない。実務において「絶対に止まらない」「結果が確実にログに残る」堅牢なバッチ処理ツールのアーキテクチャを伝授する。

1. なぜ君のバッチ処理は途中で止まるのか?

プロフェッショナルではないコードは、以下の3つの罠に必ず陥る。

1. メモリリークとファイルロック: `Document` オブジェクトを適切に解放(`Set obj = Nothing`)せず、`Close` も不完全なまま次のループへ進む。結果、AutoCADが図面を掴んだままになり、次のファイルが開けなくなる。
2. 「全か無か」の例外処理: 100枚中1枚の図面が破損していただけで、マクロがエラーで停止し、それまでの処理がすべて台無しになる。
3. バージョンの不整合: `SaveAs` メソッドの引数(`dwgVersion`)を理解せず、意図しないバージョンで保存してしまい、後続の工程(古いCADを使う協力会社への送付など)で開けなくなる。

これらを解決するために、我々は「Isolation(隔離)」と「Auditability(監査可能性)」を設計の柱に据える。

2. プロダクション・アーキテクチャの設計図

今回構築するのは、以下の4層構造を持つ設計である。

  • Data Layer (Excel): パス、置換対象属性、保存バージョンを管理。
  • Orchestration Layer (Main Loop): Excelを読み込み、個別の処理を呼び出す。エラーが発生しても、そのファイルを「失敗」として記録し、次のファイルへ進む(リトライ・スキップ戦略)。
  • Logic Layer (Task Execution): 図面を開き、ブロックを探し、属性を書き換える。ここには「図面を開く」「操作する」「保存する」の責務を分離して実装する。
  • Logging Layer: 成功・失敗・エラー内容をExcelまたはテキストに書き出す。

3. 実装:堅牢な一括処理エンジン

以下に、Excel VBAからAutoCADを制御する、実務レベルのコード例を示す。

事前準備

1. ExcelのVBAエディタで `Tools` -> `References` を開き、`AutoCAD [Version] Type Library` にチェックを入れること。
2. Excelシートの構成:

  • A列: 図面フルパス
  • B列: 置換対象の属性タグ名
  • C列: 新しい値
  • D列: 保存バージョン (整数: 2018=2013, 2013=2010… ※AutoCADの定数に準拠)

プロダクション・コード

Option Explicit

' ==============================================================================
' AutoCAD Batch Processor (Production Grade)
' 目的: Excelリストに基づいた図面の一括属性更新およびバージョン変換
' ==============================================================================

' AutoCADのバージョン定数(環境に合わせて調整)
' ※実際にはAutoCADの列挙型を使用するのがベスト
Private Const DWG_VER_2018 As Long = 2018 ' 例: AutoCAD 2018形式
Private Const DWG_VER_2013 As Long = 2013 ' 例: AutoCAD 2013形式

Sub ExecuteBatchProcess()
    Dim acadApp As Object ' AcadApplication
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim i As Long
    
    Set ws = ThisWorkbook.Sheets("List") ' Excelシート名
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
    ' 1. AutoCADへの接続(既存プロセスを優先)
    On Error Resume Next
    Set acadApp = GetObject(, "AutoCAD.Application")
    If Err.Number <> 0 Then
        Set acadApp = CreateObject("AutoCAD.Application")
        acadApp.Visible = True
    End If
    On Error GoTo 0
    
    If acadApp Is Nothing Then
        MsgBox "AutoCADを起動できませんでした。", vbCritical
        Exit Sub
    End If

    ' 2. メインループ(Isolation設計)
    For i = 2 To lastRow ' 2行目から開始
        Dim filePath As String: filePath = ws.Cells(i, 1).Value
        Dim tagKey As String: tagKey = ws.Cells(i, 2).Value
        Dim newValue As String: newValue = ws.Cells(i, 3).Value
        Dim targetVer As Long: targetVer = ws.Cells(i, 4).Value ' 保存バージョン
        
        ' 処理結果を記録するための変数
        Dim status As String: status = "Success"
        Dim errorMsg As String: errorMsg = ""

        ' ファイルの存在確認(Pre-flight check)
        If Dir(filePath) = "" Then
            ws.Cells(i, 5).Value = "Error: File Not Found"
            GoTo NextIteration
        End If

        ' 個別図面の処理を関数化(例外をこの中で隔離する)
        On Error Resume Next
        Call ProcessSingleDrawing(acadApp, filePath, tagKey, newValue, targetVer)
        
        If Err.Number <> 0 Then
            status = "Error"
            errorMsg = Err.Description
            Err.Clear
        End If
        On Error GoTo 0

        ' 3. 結果の書き出し(Auditability)
        ws.Cells(i, 5).Value = status
        ws.Cells(i, 6).Value = errorMsg
        
NextIteration:
    Next i

    MsgBox "バッチ処理が完了しました。ログを確認してください。", vbInformation
End Sub

' ------------------------------------------------------------------------------
' 個別図面操作ロジック(責務の分離)
' ------------------------------------------------------------------------------
Private Sub ProcessSingleDrawing(ByRef acadApp As Object, _
                                 ByVal dwgPath As String, _
                                 ByVal targetTag As String, _
                                 ByVal newVal As String, _
                                 ByVal saveVer As Long)
    
    Dim acadDoc As Object ' AcadDocument
    Dim entity As Object  ' AcadEntity
    Dim blockRef As Object ' AcadBlockReference
    Dim attributRef As Object ' AcadAttributeReference
    Dim found As Boolean: found = False
    
    ' 図面を開く
    Set acadDoc = acadApp.Documents.Open(dwgPath)
    
    ' モデル空間およびペーパー空間を走査(実務では両方カバーすべき)
    ' ここでは簡略化のためModelSpaceのみを対象とする
    For Each entity In acadDoc.ModelSpace
        ' ブロック参照かどうかを判定
        If entity.ObjectName = "AcDbBlockReference" Then
            Set blockRef = entity
            
            ' 属性を持っているブロックか確認
            If blockRef.HasAttributes Then
                For Each attributRef In blockRef.GetAttributes
                    ' 指定されたタグ名に一致するか
                    If UCase(attributRef.TagString) = UCase(targetTag) Then
                        attributRef.TextString = newVal
                        attributRef.Update
                        found = True
                    End If
                Next attributRef
            End If
        End If
    Next entity

    ' 属性が見つからなかった場合のハンドリング(必要に応じて)
    If Not found Then
        ' 必要ならここでエラーをスローさせる
    End If

    ' 4. バージョン指定保存とクローズ(Lifecycle Management)
    ' SaveAsの引数は AutoCAD の API 仕様に厳密に従う必要がある
    ' SaveAs(FileName, SaveAsType) 
    ' ※VBAから呼び出す場合、dwgVersion定数は引数として渡す
    
    ' 確実に保存して閉じる
    acadDoc.SaveAs dwgPath, saveVer ' 本来はSaveAsTypeの定数を使用
    acadDoc.Close True ' 変更を保存して閉じる
    
    ' オブジェクトの明示的解放(メモリリーク防止の要)
    Set attributRef = Nothing
    Set blockRef = Nothing
    Set entity = Nothing
    Set acadDoc = Nothing
End Sub

4. チーフアーキテクトによる解説:なぜこのコードは「強い」のか

① オブジェクトのライフサイクル管理

`ProcessSingleDrawing` 内で、`Set acadDoc = Nothing` を徹底している。AutoCAD VBAにおいて、`Document` オブジェクトがメモリ上に残っている限り、そのファイルはOSレベルで「使用中」となり、削除や移動、あるいは再オープンが不可能になる。これを防ぐことが、バッチ処理の安定性を決定づける。

② 徹底した「例外の隔離」

`ExecuteBatchProcess`(司令塔)と `ProcessSingleDrawing`(実行部)を分けた。
司令塔は `On Error Resume Next` を使い、実行部で発生したエラーを「検知」して「記録」することだけに専念している。これにより、「1つの図面が壊れていても、残りの99枚を完遂する」という、業務ツールとして最低限の要件を満たしている。

③ Pre-flight Check(事前検証)

いきなり `Documents.Open` を叩くのではなく、`Dir(filePath)` でファイルの存在を確認している。ネットワークドライブ上のパスが切れているような初歩的なミスで、AutoCADのCOMインターフェースをエラーで叩き起こさないための、プロの作法だ。

④ 監査ログ(Audit Trail)

処理結果をExcelの隣の列に書き出す。これは単なる親切心ではない。大量の図面を処理した後、「どのファイルが、なぜ失敗したのか」を即座に特定できないツールは、現場では「使い物にならないゴミ」として扱われる。

5. さらなる高みへ:パフォーマンスと拡張性

もし、あなたが数千枚規模の図面を扱うなら、以下のステップを検討せよ。

1. UIの非表示化: `acadApp.Visible = False` にすることで、描画負荷を下げ、処理速度を劇的に向上させることができる。ただし、デバッグ時は `True` にしておくこと。
2. マルチスレッドの検討: VBA自体はシングルスレッドだが、AutoCADのプロセスを複数立ち上げる(極めて困難だが)か、あるいはC# (.NET) で `Object Model` を叩く設計に移行することで、並列処理が可能になる。
3. データベース連携: Excelを捨て、SQL Serverなどのデータベースからリストを取得するようにすれば、多人数での同時作業に耐えうる真のエンタープライズ・ツールへと進化する。

「動くコード」を書くのは初級者だ。「運用に耐えうるシステム」を書くのが、我々プロフェッショナルの仕事である。

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