AutoCAD VBAを掌握する極限の知見:WBLOCKのVBA完全再現とメモリ最適化の極意
AutoCAD VBAの領域において、図面全体のオープン・セーブ制御(`SaveAs`や`Open`)は基本のルーチンに過ぎない。しかし、実務の現場――それも数千枚規模の図面群を統合・再編するような巨大な自動化パイプラインにおいて真に求められるのは、「図面全体ではなく、特定のエンティティ(オブジェクト)群のみを切り出し、任意のバージョンで新規DWGとして射出する」という高度な制御だ。
標準の `WBLOCK` コマンドをユーザーインターフェース経由で手動実行するなど論外である。我々は、COMの背後にあるC++コアのライフサイクルを完全に支配し、メモリリークの脅威を封じ込めながら、高速かつ堅牢にオブジェクトを別ファイルへと書き出すコードを書かねばならない。
本稿では、AutoCAD VBAにおける `WockBlock` メソッドの深淵と、実務で直面するバージョン互換・メモリ管理の罠を突破するチーフアーキテクト級の知見を公開する。
—
1. WBLOCKメカニズムの核心とVBAにおける実装アプローチ
AutoCADのオブジェクトモデルにおいて、特定の図形を別ファイルとして切り出す手段は主に2つ存在する。
1. `Document.WBlock` メソッドの利用
2. `CopyObjects` メソッドによる別ドキュメントへの転送 + `SaveAs`
一見、`WBlock` メソッドが最も直感的だが、ここにはCOM特有のトラップが潜んでいる。`WBlock` は引数として「ファイルパス」と「対象オブジェクトの配列(Variant)」を取るが、この処理の裏ではAutoCADのセッション内テンポラリデータベースが生成・破棄される。
実務上、選択されたオブジェクトが他の画層、線種、あるいはブロック定義に依存している場合、単純な配列の渡航だけでは依存関係が欠落した「破損に近いDWG」が生成される。この問題をクリアし、完全な独立性を保ったDWGを生成するための実装パターンを以下に示す。
—
2. 実装コード:堅牢なるオブジェクト抽出・別名保存ルーチン
以下のコードは、モデル空間内で選択された(あるいはプログラムでフィルタリングされた)オブジェクト群を、依存関係を維持したまま指定バージョンの新DWGとして書き出す実用プロシージャである。
Option Explicit
‘ =================================================================================
‘ módulo: ModWBlockManager
‘ 概要 : 選択されたエンティティ群を安全に別DWGとして切り出すプロフェッショナル実装
‘ =================================================================================
Public Sub ExportSelectedObjectsToDWG(ByVal targetFilePath As String, ByVal fileVersion As AcSaveAsType)
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing
Dim sset As AcadSelectionSet
Dim ssetName As String
ssetName = “SSET_WBLOCK_EX_” & Format(Now, “hhmmss”)
On Error GoTo ErrorHandler
‘ 1. 選択セットの安全な生成と取得
‘ 既に同名のセットが存在する場合のクラッシュを防ぐ防衛的コード
Set sset = CreateSelectionSetSafely(acadDoc, ssetName)
‘ 例として、ユーザーに画面上でオブジェクトを選択させる(実務ではフィルタ処理に置き換え可能)
MsgBox “書き出すオブジェクトを選択してください。”, vbInformation, “WBLOCK自動化”
sset.SelectOnScreen
If sset.Count = 0 Then
MsgBox “オブジェクトが選択されませんでした。処理を中断します。”, vbExclamation
GoTo Cleanup
End If
‘ 2. オブジェクトID配列への変換
Dim entCount As Long
entCount = sset.Count
Dim objIDs() As Long
ReDim objIDs(0 To entCount – 1)
Dim i As Long
For i = 0 To entCount – 1
objIDs(i) = sset.Item(i).ObjectID
Next i
‘ 3. WBLOCKの実行(COMの境界を越えたメモリ・リソースの操作)
‘ ※注意: 同一パスのファイルが既に存在する場合は事前に削除または上書き許可が必要
If Dir(targetFilePath) <> “” Then
Kill targetFilePath ‘ 既存ファイルの強制排除(実務ではバックアップポリシーに依存)
End If
‘ AutoCAD内部データベースへの委譲
acadDoc.WBlock targetFilePath, objIDs
‘ 4. バージョン互換の強制適用(必要に応じて保存形式を再制御)
‘ WBlock直後のファイルは現在のセッション形式で保存されるため、
‘ 古いバージョン(例: AutoCAD 2004形式など)への厳密なダウングレードが必要な場合は
‘ 生成されたファイルを一度サイレントオープンしてSaveAsし直すアプローチをとる。
If fileVersion <> acR2018Drawing Then
Call ConvertDWGVersion(targetFilePath, fileVersion)
End If
MsgBox “ファイルの書き出しが正常に完了しました。” & vbCrLf & targetFilePath, vbInformation, “完了”
Cleanup:
‘ 5. メモリの明示的解放(オブジェクト参照の断絶とガベージコレクションの誘導)
Call SafeDeleteSelectionSet(acadDoc, ssetName)
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume Cleanup
End Sub
‘ — ヘルパー関数群 —
Private Function CreateSelectionSetSafely(doc As AcadDocument, setName As String) As AcadSelectionSet
Dim ss As AcadSelectionSet
On Error Resume Next
Set ss = doc.SelectionSets.Item(setName)
If Err.Number <> 0 Then
Set ss = doc.SelectionSets.Add(setName)
Else
ss.Clear
End If
On Error GoTo 0
Set CreateSelectionSetSafely = ss
End Function
Private Sub SafeDeleteSelectionSet(doc As AcadDocument, setName As String)
On Error Resume Next
doc.SelectionSets.Item(setName).Delete
On Error GoTo 0
End Sub
Private Sub ConvertDWGVersion(filePath As String, targetVer As AcSaveAsType)
‘ 外部から生成されたDWGを指定バージョンへコンバートするサイレントルーチン
Dim app As AcadApplication
Set app = ThisDrawing.Application
Dim targetDoc As AcadDocument
‘ ウィンドウを表示させずにバックグラウンドに近い状態で開く(Visible制御)
Set targetDoc = app.Documents.Open(filePath)
targetDoc.SaveAs filePath, targetVer
targetDoc.Close False
Set targetDoc = Nothing
Set app = Nothing
End Sub
—
3. チーフアーキテクトが警鐘を鳴らす「メモリ最適化」と「COMの罠」
AutoCAD VBAにおいて、`WBlock` や多数のオブジェクトを操作するプログラムをループさせると、決まって「メモリリーク」や「Automationエラー(致命的なエラーによる強制終了)」に直面する。
その原因は、VBAの背後で動作しているCOMオブジェクト(C++で書かれたARX/ObjectARXのラッパー)の参照カウントの不整合にある。
1. 選択セットとオブジェクト配列のゾンビ化を防ぐ
VBAで `SelectionSet` を作成・破棄する際、`Delete` メソッドを呼び出してもAutoCADの内部メモリ(ドキュメントデータベース)に参照が残存することが多々ある。これを放置して数千回ループさせると、確実にAutoCAD本体が沈黙する。
- 対策: ループ内あるいは処理の終了時には、必ず `Set sset = Nothing` および明示的な `Delete` をセットで行い、VBAのローカルスコープから外れた段階でCOMラッパーの参照を完全に断ち切ること。
2. `ObjectID` の配列処理における境界制約
`WBlock` に渡す配列は `Variant` 型である必要があるが、内部のデータ型は `Long`(32bit)または `LongPtr`(64bit AutoCAD環境におけるVBA7のポインタサイズ)に厳密に一致していなければならない。
- 64bit環境(AutoCAD 2014以降)では、ポインタサイズの変化に伴う型の不一致がクラッシュを引き起こすリスクがあるため、配列の型定義には細心の注意を払う必要がある。
—
4. レガシー環境とバージョン互換の統御
社内システムや外部ベンダーとのやり取りにおいて、「最新のAutoCADで作成した図面を、下位互換性のある古いCAD(例:AutoCAD 2004や2007形式)で読ませたい」という要求は根強い。
`WBlock` メソッド単体では、実行しているAutoCADのネイティブバージョンでDWGが生成されてしまう。そのため、本稿のコードでも示した通り、「一度WBlockで切り出した後、ターゲットファイルをサイレントで再オープンし、目的の `AcSaveAsType` で上書き保存し直す」という2段階のフェーズを踏むのが、最も確実で破綻のないアプローチとなる。
利用可能な主なバージョン定数(`AcSaveAsType`):
- `acR14Drawing` (AutoCAD R14)
- `ac2000Drawing` (AutoCAD 2000)
- `ac2004Drawing` (AutoCAD 2004)
- `ac2007Drawing` (AutoCAD 2007)
- `ac2010Drawing` (AutoCAD 2010)
- `ac2018Drawing` (AutoCAD 2018以降)
—
5. 総括
AutoCAD VBAは、単なるマクロの記録ツールではない。AutoCADの深層アーキテクチャ(データベース、オブジェクトモデル、COMライフサイクル)を正確に理解した者にとって、それは巨大なCADインフラを意のままに操るための最強の武器となる。
今回解説した `WBLOCK` のコードベースとメモリ管理の哲学をあなたのシステムに組み込むことで、手作業による図面切り出し作業から解放され、完全無欠の自動化パイプラインが構築されることを確信している。妥協なきコードを書け。
