AutoCAD VBAの深淵へ:オブジェクト選択と情報抽出の真髄 ~レガシーシステム保守とAPI連携の極意~
長年、CADシステムの自動化という泥臭い戦場を渡り歩いてきた者ならば、AutoCAD VBAがもたらす静かなる革命の片鱗に触れたことがあるはずだ。特に、図面上のオブジェクトを自在に操り、その情報を抽出し、さらにそれを基にシステム連携を図るという行為は、単なるスクリプト作成の域を超え、システム全体のパフォーマンスと保守性に直結する、まさに「極限」とも呼べる領域である。
本稿では、AutoCAD VBAの基本中の基本である「オブジェクト選択と情報抽出」に焦点を当てる。しかし、その解説は、新人エンジニアが参考書をなぞるような甘っちょろいものではない。我々が長年培ってきた、Windows APIの呼び出し、メモリ最適化、レガシー環境の保守、そしてシステム間連携といった、血と汗で得た「極限の知見」を惜しみなく披露しよう。
1. オブジェクト選択の舞台裏:AcadApplicationとAcadDocumentの役割
まず、AutoCAD VBAにおけるオブジェクト選択の基本構造を理解するために、`AcadApplication` と `AcadDocument` オブジェクトのライフサイクルを把握することが肝要だ。
- `AcadApplication`: AutoCADアプリケーションそのものを指す。VBAからAutoCADを操作する際の「窓口」であり、起動、終了、ドキュメントの開閉など、アプリケーションレベルの操作を司る。
- `AcadDocument`: 現在アクティブな図面ファイル、すなわち「ドキュメント」を表す。図面上の全てのオブジェクト(線、円、文字、ブロックなど)は、この `AcadDocument` オブジェクトの配下にある。
我々が図面上のオブジェクトを選択し、その情報を取得するという行為は、`AcadDocument` オブジェクトの持つ `SelectionSets` コレクションを操作することから始まる。
1.1. SelectionSetsコレクション:選択の「場」
`SelectionSets` コレクションは、図面上でユーザーが選択したオブジェクトの集合体である。VBAでは、このコレクションを利用して、ユーザーにオブジェクトを選択させ、その結果を取得する。
‘AcadDocumentオブジェクトを取得
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing ‘ 現在アクティブな図面を取得
‘SelectionSetsコレクションに新しい選択セットを作成
Dim ss As AcadSelectionSet
Set ss = acadDoc.SelectionSets.Add(“MySelectionSet”) ‘ 一意の名前を付ける
‘ユーザーにオブジェクトを選択させる
‘acSelectionSetCrossing: 境界線で囲まれたオブジェクトと交差するオブジェクトを選択
‘acSelectionSetWindow: 境界線で囲まれたオブジェクトをすべて選択
‘acSelectionSetWindowPolygon: ポリゴンで囲まれたオブジェクトを選択
‘acSelectionSetCrossingPolygon: ポリゴンと交差するオブジェクトを選択
‘acSelectionSetFence: フェンス線と交差するオブジェクトを選択
ss.Select acSelectionSetCrossing, , , “Select Objects”
‘選択されたオブジェクトの情報を取得する処理は後述
ここで重要なのは、`Select` メソッドの引数である。`acSelectionSetCrossing` や `acSelectionSetWindow` などの定数は、選択方法を指定する。これらの引数を適切に設定することで、より効率的かつ意図したオブジェクトのみを選択させることが可能となる。
1.2. メモリ管理の極意:オブジェクトの明示的解放
AutoCAD VBA、特に大量のオブジェクトを扱う場合、メモリリークは避けて通れない課題である。`SelectionSets` コレクションも例外ではない。使い終わった選択セットは、明示的に解放することが、システム全体の安定稼働に不可欠である。
‘ 選択セットの使用後
If Not ss Is Nothing Then
ss.Delete ‘ 選択セットを削除
Set ss = Nothing ‘ オブジェクト参照を解放
End If
‘ AcadDocumentオブジェクトも同様に解放
Set acadDoc = Nothing
`ss.Delete` は、`SelectionSets` コレクションからその選択セットを削除する。`Set ss = Nothing` は、VBAのオブジェクト変数への参照を解放し、ガベージコレクションによるメモリ解放を促す。この「明示的解放」の習慣が、レガシー環境での保守性を劇的に向上させる鍵となる。
2. オブジェクト情報の抽出:AcadObjectのプロパティを深掘りする
ユーザーが選択したオブジェクトは、`AcadSelectionSet` オブジェクトの `objectArray` プロパティを通じて取得できる。この `objectArray` は、選択された `AcadObject` の配列である。
`AcadObject` は、AutoCAD図面上のあらゆるオブジェクトの基底クラスであり、そのプロパティを介して、オブジェクトの様々な情報を取得できる。
‘AcadSelectionSetからobjectArrayを取得
Dim obj As AcadObject
Dim objArray As Variant
objArray = ss.objectArray
‘選択されたオブジェクトをループ処理
For Each obj In objArray
‘オブジェクトのタイプを取得
Dim objType As String
objType = obj.ObjectName
‘画層名を取得
Dim layerName As String
layerName = obj.Layer
‘色を取得 (AciColorオブジェクト)
Dim objColor As AciColor
Set objColor = obj.Color
‘色番号を取得 (AutoCADの色番号: 1-255)
Dim colorIndex As Integer
colorIndex = objColor.ColorIndex
‘色名(RGB値)を取得
Dim colorName As String
colorName = “R:” & objColor.Red & “, G:” & objColor.Green & “, B:” & objColor.Blue
‘取得した情報をメッセージボックスに表示
MsgBox “オブジェクトタイプ: ” & objType & vbCrLf & _
“画層: ” & layerName & vbCrLf & _
“色番号: ” & colorIndex & vbCrLf & _
“色 (RGB): ” & colorName, vbInformation, “オブジェクト情報”
‘AciColorオブジェクトも解放
Set objColor = Nothing
Next obj
‘AcadObjectの参照も解放
Set obj = Nothing
2.1. `ObjectName` プロパティ:オブジェクトの「正体」を知る
`obj.ObjectName` は、オブジェクトの内部的な型名を文字列で返す。例えば、線分であれば `”AcDbLine”`、円であれば `”AcDbCircle”`、文字であれば `”AcDbText”` といった具合だ。この情報は、オブジェクトの種別によって処理を分岐させたい場合に非常に役立つ。
2.2. `Layer` プロパティ:管理の要、画層情報
`obj.Layer` は、オブジェクトが配置されている画層の名前を文字列で返す。図面管理においては、画層情報は極めて重要であり、このプロパティで容易に取得できる。
2.3. `Color` プロパティ:色の表現と `AciColor` オブジェクト
`obj.Color` は、オブジェクトの色情報を `AciColor` オブジェクトとして返す。`AciColor` オブジェクトは、以下のプロパティを持つ。
- `ColorIndex`: AutoCADの標準色番号(1~255)を整数で返す。
- `Red`, `Green`, `Blue`: RGB値をそれぞれ0~255の整数で返す。True Colorが設定されている場合に有効。
これにより、単なる色番号だけでなく、より詳細な色情報を取得することが可能となる。
2.4. パフォーマンスへの配慮:不要なオブジェクトの参照解放
ループ内で `obj` や `objColor` といったオブジェクト変数が生成される。これらのオブジェクトも、必要がなくなれば速やかに解放することが、メモリ使用量を抑える上で重要である。ループの最後に `Set obj = Nothing` を記述するのは、このためである。
3. Windows API連携の極意:より高度な選択と情報取得
AutoCAD VBA単体では限界がある場合、Windows APIの出番となる。特に、ユーザーインターフェースのカスタマイズや、より低レベルでのオブジェクト操作が必要な場面で、API連携は強力な武器となる。
例えば、ユーザーにオブジェクトを選択させる際に、標準の `Select` メソッドではなく、カスタムダイアログを表示させたい場合や、選択されたオブジェクトのハンドルを直接取得したい場合などが考えられる。
3.1. オブジェクトハンドル(`ObjectID`)の取得と利用
AutoCADの各オブジェクトは、一意の識別子である「オブジェクトハンドル」を持つ。これは、`AcadObject` インターフェースの `Handle` プロパティで取得できる。
‘ オブジェクトハンドルを取得
Dim objHandle As String
objHandle = obj.Handle
このハンドルは、別のVBAマクロから、あるいはVB.NETやC#などの.NET言語から、AutoCADのオブジェクトを特定し、操作するための強力なキーとなる。特に、図面を跨いだオブジェクトの参照や、外部アプリケーションとの連携において、このハンドルが不可欠となる場面は多い。
3.2. Windows APIによるカスタム選択ダイアログ
標準の選択方法では物足りない場合、`user32.dll` や `comdlg32.dll` などのWindows APIを駆使して、カスタムダイアログを作成し、ユーザーにオブジェクトを選択させることも不可能ではない。しかし、これはAutoCAD VBAの範疇を超え、高度なWindowsプログラミングの知識を要求される。
例えば、`PickFirst` APIのような低レベルな関数を直接呼び出すことで、より柔軟な選択肢を提供できる可能性がある。ただし、AutoCADのバージョンアップによるAPIの変更や、AutoCAD内部のデータ構造との非互換性リスクを常に考慮する必要がある。
3.3. レガシー環境でのAPI利用における注意点
長年稼働しているレガシーシステムにおいては、Windows APIの利用は慎重に行う必要がある。
- APIの互換性: 使用するAPIが、ターゲットとなるWindows OSおよびAutoCADのバージョンでサポートされているかを確認する。
- 32bit/64bit問題: AutoCADの32bit版と64bit版では、APIの呼び出し方やDLLのパスが異なる場合がある。
- パフォーマンス: API呼び出しは、VBAネイティブな処理に比べてオーバーヘッドが大きい場合がある。パフォーマンスが重要な箇所での安易なAPI利用は避けるべきである。
4. システム間連携の極限:VBAから.NET、そして外部システムへ
AutoCAD VBAで取得したオブジェクト情報を、他のシステムで活用したいというニーズは尽きない。
4.1. VB.NET / C# によるAutoCAD操作(ObjectARX / .NET API)
近年では、AutoCADの操作はVBAよりも、より強力で柔軟な.NET API(ObjectARXの.NETラッパー)を利用することが主流となっている。VBAで取得したオブジェクトハンドルを.NETアプリケーションに渡し、そのハンドルを基に.NET APIから対象オブジェクトにアクセスすることで、VBAの制約を超えた高度な連携が可能となる。
.net
‘ VB.NETでの例(VBAから渡されたハンドルを基にオブジェクトを取得)
‘ Imports Autodesk.AutoCAD.Runtime
‘ Imports Autodesk.AutoCAD.DatabaseServices
‘ Imports Autodesk.AutoCAD.ApplicationServices
Public Sub ProcessObjectFromHandle(handle As String)
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim db As Database = doc.Database
Using tr As Transaction = db.TransactionManager.StartTransaction()
Try
Dim oid As ObjectId = db.GetObjectIdFromHandle(handle)
If Not oid.IsNull Then
Dim obj As DBObject = tr.GetObject(oid, OpenMode.ForRead)
‘ objの情報を処理する
MessageBox.Show($”オブジェクトタイプ: {obj.GetType().Name}”)
Else
MessageBox.Show(“無効なハンドルです。”)
End If
tr.Commit()
Catch ex As Exception
MessageBox.Show($”エラー: {ex.Message}”)
tr.Abort()
End Try
End Using
End Sub
4.2. COM連携によるデータ交換
VBAが生成したデータを、COMインターフェースを通じて他のアプリケーション(Excel、Access、あるいはカスタムアプリケーション)に渡すことも一般的だ。`CreateObject` 関数や `GetObject` 関数を駆使して、異なるアプリケーション間のデータ連携を図る。
‘ Excelを操作してデータを書き込む例
Dim excelApp As Object
Dim excelWb As Object
Dim excelWs As Object
On Error Resume Next ‘ エラー発生時も続行
Set excelApp = GetObject(, “Excel.Application”) ‘ 起動中のExcelを取得
If excelApp Is Nothing Then
Set excelApp = CreateObject(“Excel.Application”) ‘ Excelを起動
End If
On Error GoTo 0
excelApp.Visible = True ‘ Excelを表示
Set excelWb = excelApp.Workbooks.Add
Set excelWs = excelWb.Sheets(1)
‘ ヘッダー行を書き込む
excelWs.Cells(1, 1).Value = “オブジェクトタイプ”
excelWs.Cells(1, 2).Value = “画層”
excelWs.Cells(1, 3).Value = “色番号”
excelWs.Cells(1, 4).Value = “色 (RGB)”
Dim rowNum As Integer
rowNum = 2
‘ VBAで取得したオブジェクト情報をExcelに書き込む (上記コードの続きとして)
For Each obj In objArray
‘ … (objType, layerName, colorIndex, colorName の取得処理) …
excelWs.Cells(rowNum, 1).Value = objType
excelWs.Cells(rowNum, 2).Value = layerName
excelWs.Cells(rowNum, 3).Value = colorIndex
excelWs.Cells(rowNum, 4).Value = colorName
rowNum = rowNum + 1
Next obj
‘ オブジェクト参照の解放
Set obj = Nothing
Set objArray = Nothing
Set ss = Nothing
Set acadDoc = Nothing
‘ Excelオブジェクトの解放 (注意: 起動したExcelを閉じずに参照を解放すると、Excelがバックグラウンドで残り続ける可能性がある)
‘ excelWs.Parent.Close ‘ ブックを保存せずに閉じる場合
‘ excelApp.Quit
Set excelWs = Nothing
Set excelWb = Nothing
‘ Set excelApp = Nothing ‘ 起動したExcelを閉じない場合は、この行はコメントアウトまたは削除
このExcel連携の例では、`GetObject` で起動中のExcelインスタンスを取得し、存在しない場合は `CreateObject` で新規に起動するという、レガシーシステムでもよく見られる「堅牢な」処理を実装している。また、`On Error Resume Next` と `On Error GoTo 0` を用いて、COMオブジェクトの生成や操作におけるエラーハンドリングを丁寧に行うことで、予期せぬクラッシュを防いでいる。
結論:VBAは「終わらない」
AutoCAD VBAは、一見すると古びた技術に見えるかもしれない。しかし、そのオブジェクトモデルの深淵、そしてWindows APIとの連携、さらには.NET APIや外部システムとの連携までを理解し、使いこなすことで、VBAは依然として強力な自動化ツールであり続ける。
特に、既存のレガシーシステムを保守・拡張する現場においては、VBAの知識は「必須」と言っても過言ではない。オブジェクトのライフサイクルを理解し、メモリ管理を徹底し、APIの特性を熟知すること。これこそが、伝説のチーフアーキテクトが長年培ってきた、技術の真髄である。
本稿で解説したオブジェクト選択と情報抽出は、その第一歩に過ぎない。しかし、この基礎を極めることで、あなたはAutoCAD VBAの世界で、さらに深遠な領域への扉を開くことができるだろう。さあ、君も「極限」への道を歩み始めよ。
