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)」である。
オブジェクトのライフサイクルを理解し、コレクションの挙動を予測し、例外的な事態を設計に組み込む。この積み重ねが、単なる「スクリプト」を「業務基盤(プロダクション・ツール)」へと昇華させるのだ。
次にコードを書くときは、自分に問いかけろ。
「そのオブジェクトは、いつ、どのように、誰の手によって消滅するのか?」
以上だ。作業に戻れ。
