【実務・中級編】【初心者向け】ThisDrawing.CloseとDocuments.Closeの違い:単一図面と複数図面管理における正しい使い分け – AutoCAD VBA解析バイブル

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

AutoCAD VBAを掌握する極限の知見:`ThisDrawing.Close` と `Documents.Close` の決定的な境界線

いいか、よく聞け。
「とりあえず動いた」というコードを量産するな。それは、将来の自分、あるいはチームメンバーが深夜にバグの修正に追われるための「時限爆弾」を仕込んでいるのと同じだ。

AutoCAD VBAの世界において、図面(Document)の管理は、単なる「窓を閉じる操作」ではない。それはオブジェクトのライフサイクルを制御し、メモリ空間を整理し、アプリケーションの安定性を担保する極めてクリティカルな設計判断だ。

今回、多くの初心者が陥る罠——`ThisDrawing.Close` と `Documents.Close` の混同——について、その設計思想の深淵を徹底的に解説する。この記事を読み終える頃には、君のコードから「なんとなく」という不確実性は排除されているはずだ。

1. 概念の整理:ローカルな「自分」か、グローバルな「管理」か

まず、この二つのメソッドが何を指しているのか、その「視点」の違いを脳に刻み込め。

`ThisDrawing.Close` —— 「自己完結型」の命令

`ThisDrawing` は、現在実行中のマクロが紐付いている、まさに「その図面」を指すショートカットだ。
`ThisDrawing.Close` と書いたとき、コードは「今、自分が立っているこの場所を閉じる」という極めてローカルな命令を下している。

`Documents.Close` —— 「管理者型」の命令

一方で `Documents` は、AutoCADアプリケーションが開いているすべての図面を保持している「コレクション(集合体)」だ。
`Documents.Close(Index)` は、「管理下にあるリストの中から、特定の図面を指名して閉じる」という、俯瞰的な管理命令である。

2. なぜ初心者はバグを連発するのか?(致命的な設計ミス)

なぜ、この違いを知らないとプロジェクトが破綻するのか。理由は大きく分けて2つある。

① 「自殺マクロ」の恐怖

これが最も初歩的かつ致命的なミスだ。
`ThisDrawing` 内に記述されたマクロの中で `ThisDrawing.Close` を実行してみろ。マクロを実行している「器」そのものを破壊することになる。
実行中のスタック(メモリ上の処理手順)が、図面のクローズに伴って強制的に破棄されるため、その後に続く「ファイルのバックアップ」や「ログ出力」といった重要な後処理が一切行われず、プログラムが異常終了したり、ファイルが破損したりする原因になる。

② コレクションの「インデックス・シフト」問題

複数の図面をループで回して閉じようとする際、初心者は必ずこう書く。
`For Each doc In Documents … doc.Close … Next`

これはプロのコードではない。即座に修正しろ。

図面を閉じると、`Documents` コレクションの要素数が減り、残りの図面のインデックス(番号)が繰り上がる。`For Each` や「正方向へのループ」でこれをやると、「閉じた次の図面をスキップしてしまう」というバグが発生する。これは、自動化ツールが「一部の図面を処理し忘れる」という、業務ツールとして致命的な欠陥に直結する。

3. プロダクション・グレードの設計指針

堅牢なツールを作るなら、以下の原則を徹底せよ。

1. 「自分自身」を閉じる処理は、必ず「呼び出し元」に委ねる。 マクロ自身が自分を殺してはいけない。
2. コレクション操作は必ず「逆順(Step -1)」で行う。 集合体から要素を削除・クローズする場合は、後ろから数えるのが鉄則だ。
3. 保存の成否を制御する。 `Close` メソッドの引数(`SaveChanges`)を適切に使い、ユーザーの意図しない上書きや、未保存によるダイアログの停止を防げ。

4. 実装例:堅牢な複数図面管理エンジン

では、これらを踏まえた「実戦で使えるコード」を提示する。
このコードは、指定した条件に合致する図面を安全にクローズするための、プロフェッショナルな設計に基づいている。

'' ==============================================================================
'' Module: DocumentManager
'' Description: AutoCAD図面管理のベストプラクティスを実装したモジュール
'' ==============================================================================

Option Explicit

''' <summary>
''' 指定したキーワードを含む図面を、保存の確認なしで安全にすべて閉じる。
''' </summary>
''' <param name="searchKeyword">図面名に含まれる検索文字列</param>
Public Sub CloseDocumentsByKeyword(ByVal searchKeyword As String)
    Dim i As Long
    Dim docCount As Long
    Dim targetDoc As AcadDocument
    
    ' エラーハンドリング:予期せぬエラーでAutoCADが落ちるのを防ぐ
    On Error GoTo ErrorHandler

    ' 1. 現在開いている図面の総数を取得
    docCount = ThisDrawing.Application.Documents.Count
    
    Debug.Print "--- クローズ処理開始: " & Now & " ---"

    ' 2. 【重要】コレクション操作は必ず「逆順(Step -1)」で行う
    ' これにより、図面を閉じた際のインデックス・シフトによるスキップを回避する
    For i = docCount To 1 Step -1
        Set targetDoc = ThisDrawing.Application.Documents.Item(i)
        
        ' 3. 検索キーワードが図面名に含まれているか判定
        If InStr(1, targetDoc.Name, searchKeyword, vbTextCompare) > 0 Then
            
            ' 4. 【設計判断】自分自身(実行中の図面)を閉じようとしていないかチェック
            If targetDoc.Handle = ThisDrawing.Handle Then
                Debug.Print "警告: 実行中の図面 '" & targetDoc.Name & "' はスキップしました。"
            Else
                ' 5. 保存の確認ダイアログを出さずにクローズ (SaveChanges := False)
                ' プロダクションコードでは、意図しない対話型ダイアログは極力排除する
                Debug.Print "クローズ実行: " & targetDoc.Name
                targetDoc.Close False
            End If
            
        End If
    Next i

    Debug.Print "--- クローズ処理完了 ---"
    Exit Sub

ErrorHandler:
    ' エラーが発生しても、アプリケーション全体のクラッシュは防ぐ
    MsgBox "図面クローズ中に致命的なエラーが発生しました。" & vbCrLf & _
           "Error No: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical, "Runtime Error"
    Debug.Print "Error: " & Err.Description
End Sub

''' <summary>
''' 全ての図面を閉じるが、現在のアクティブな図面だけは残す(安全策)
''' </summary>
Public Sub SafeCloseAllExceptActive()
    Dim i As Long
    Dim docCount As Long
    
    On Error Resume Next ' 意図的なエラー無視(図面が既に閉じている場合など)
    
    docCount = ThisDrawing.Application.Documents.Count
    
    ' 逆順ループ
    For i = docCount To 1 Step -1
        ' アクティブな図面(現在マクロが動いている図面)以外を閉じる
        If ThisDrawing.Application.Documents.Item(i).Handle <> ThisDrawing.Handle Then
            ' 保存して閉じる場合は True、破棄する場合は False
            ThisDrawing.Application.Documents.Item(i).Close True
        End If
    Next i
    
    On Error GoTo 0
End Sub

コードの解説(アーキテクトの視点)

  • `Handle` による比較: `Name`(図面名)での比較は、ユーザーが図面名を変更した場合に脆弱になる。`Handle` はAutoCAD内部で一意に割り当てられる不変のIDだ。オブジェクトの同一性を判定するなら、`Handle` を使え。
  • `Step -1` の徹底: 前述の通り、コレクションの整合性を保つための必須テクニックだ。
  • `On Error GoTo ErrorHandler`: 自動化ツールにおいて、エラーは「起きるもの」だ。エラーを無視して進むのではなく、適切にキャッチし、ログを残し、ユーザーに状況を伝える。これが「信頼されるツール」の条件だ。
  • `SaveChanges` 引数の明示: `Close` メソッドに `False` を渡すことで、ユーザーの「保存しますか?」というポップアップを抑制している。バッチ処理において、このダイアログは「処理の停止」を意味する。

結論

`ThisDrawing.Close` は、その場の場当たり的な処理には便利かもしれない。だが、大規模な、あるいは堅牢なシステムを構築する際、君が扱うべきは常に `Documents` コレクションを通じた「統治(Governance)」である。

オブジェクトのライフサイクルを理解し、コレクションの挙動を予測し、例外的な事態を設計に組み込む。この積み重ねが、単なる「スクリプト」を「業務基盤(プロダクション・ツール)」へと昇華させるのだ。

次にコードを書くときは、自分に問いかけろ。
「そのオブジェクトは、いつ、どのように、誰の手によって消滅するのか?」

以上だ。作業に戻れ。

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