【実務・中級編】【上級】AcadDocument.Database.CopyObjectsの戻り値「IdPair」を解析し、コピー前後でのオブジェクトIDの対応関係を厳密に管理する – AutoCAD VBA解析バイブル

スポンサーリンク

【上級】AcadDocument.Database.CopyObjectsの戻り値「IdPair」を解析し、オブジェクトIDの対応関係を完全掌握する

開発プロジェクトの現場で、こんな壁にぶつかったことはないだろうか。

「図面間でエンティティをコピーしたが、アタッチされていた拡張データ(Xdata)や外部データベースのキー情報とのリンクが切れた」
「`CopyObjects`メソッドを実行したはいいが、コピー先のオブジェクトがどのオブジェクトID(ObjectId)を持つようになったのか分からず、後続の関連付け処理が破綻した」

AutoCAD VBAにおいて、図面間(あるいは同一図面内)のオブジェクト複製は日常茶飯事だ。しかし、それを「なんとなく動く」レベルのコードで実装しているうちは、プロの自動化エンジニアとは言えない。

今回は、`CopyObjects`の真の挙動と、戻り値である`IdPair`配列を完全網羅し、実務の現場で絶対に破綻しない堅牢なオブジェクトID管理の仕組みを伝授する。

1. なぜ「なんとなくコピー」では業務ツールとして失格なのか?

AutoCADのオブジェクトモデルにおいて、`AcadEntity`やその他のデータベースオブジェクトは、すべて固有の`ObjectID`(VBAではLongPtr、あるいはLong型として扱われる)を持っている。

`Database.CopyObjects`メソッドは、ソースとなるオブジェクトの配列を、宛先のデータベース(またはブロックテーブルレコード)へ一括転送するための最強の武器だ。しかし、このメソッドは「コピーした結果、新しいオブジェクトのIDがどうなったか」を自動的には元のオブジェクトに書き戻してくれない。

‘ 【アンチパターン】これではコピー後のIDが追跡できない
Dim sourceObjs(0) As AcadEntity
‘ … (オブジェクトの取得処理) …
destDb.CopyObjects sourceObjs, destOwner, idPairs ‘ ← idPairsを無視、あるいは未解析

これの何が問題か。
例えば、親図形とそれに紐付く子図形(属性やハッチング、カスタムXRecordなど)を同時にコピーした場合、コピー先での「親と子の関連性(Owner/Ownedの関係やポインタ)」を再構築しなければならない。IDの対応関係を見失えば、データはただの「ゴミの集まり」と化す。

ここで登場するのが、`CopyObjects`の第3引数である`IdPairs`(IdPairオブジェクトの配列)だ。これを制する者が、AutoCAD VBAのデータベース操作を制する。

2. IdPairオブジェクトの構造とライフサイクル

`CopyObjects`を実行すると、引数に渡したバリアント変数に`IdPair`の配列が格納される。
ひとつの`IdPair`は、以下の極めてシンプルかつ重要なプロパティを持っている。

  • `Key` (LongPtr / Long): コピー(ソース)のオブジェクトID
  • `Value` (LongPtr / Long): コピー(ディスティネーション)のオブジェクトID
  • `Cloned` (Boolean): 実際にクローン(複製)されたか、あるいは既存のものが参照されたかを示すフラグ
  • `Primary` (Boolean): ユーザーが直接指定してコピー対象にした主オブジェクトか、依存関係によって連鎖的にコピーされた従属オブジェクトかを示すフラグ

この `IdPair` をハッシュマップ(VBAであれば `Scripting.Dictionary`)にインデックス化することで、「どの旧IDが、どの新IDに生まれ変わったか」を$O(1)$のオーダーで瞬時に引き当てることができる。

3. 【プロダクションコード】堅牢なID追跡・同期コピーエンジン

以下のコードは、別図面(あるいは同一図面内)へオブジェクト群を安全にコピーし、`IdPair`を解析して辞書化、さらにカスタムデータの関連付けまでを完結させる実用的なモジュールだ。

Option Explicit

‘ ==============================================================================
リティカル・トランスファー・エンジン
用途: 図間オブジェクトコピーとIdPair解析による完全なIDマッピングの実現
==============================================================================
Public Sub ExecuteRobustCopy()
Dim srcDoc As AcadDocument
Dim destDoc As AcadDocument

‘ 稼働中のドキュメントを想定(適宜パスやオブジェクトに書き換えてください)
Set srcDoc = ThisDrawing
Set destDoc = Documents.Add() ‘ 新規図面へコピーする例

On Error GoTo ErrorHandler

‘ 1. コピー対象のオブジェクト配列を準備
Dim entitiesToCopy() As AcadEntity
ReDim entitiesToCopy(0) ‘ 例として1つの線分を対象とする

Dim pt1(2) As Double, pt2(2) As Double
pt1(0) = 0: pt1(1) = 0: pt1(2) = 0
pt2(0) = 100: pt2(1) = 100: pt2(2) = 0

‘ ソース図面に適当な図形を作成
Set entitiesToCopy(0) = srcDoc.ModelSpace.AddLine(pt1, pt2)

‘ 2. コピー先オーナー(通常はModelSpace)の取得
Dim destOwner As AcadBlock
Set destOwner = destDoc.ModelSpace

‘ 3. オブジェクト配列をVariantへバインド
Dim varSourceObjs As Variant
varSourceObjs = entitiesToCopy

‘ 4. CopyObjectsの実行とIdPairsの取得
Dim varIdPairs As Variant
srcDoc.Database.CopyObjects varSourceObjs, destOwner, varIdPairs

‘ 5. IdPairの解析とDictionaryへのマッピング(★ここが最重要)
Dim idMap As Object
Set idMap = CreateObject(“Scripting.Dictionary”)

Dim i As Long
Dim currentPair As AcadIdPair

If Not IsEmpty(varIdPairs) Then
For i = LBound(varIdPairs) To UBound(varIdPairs)
Set currentPair = varIdPairs(i)

‘ Key:元ID, Value:新ID を登録
‘ ※AutoCADのビットネス(32bit/64bit)による型安全性を確保するためCStrでキー化を推奨
If currentPair.Cloned Then
idMap.Add CStr(currentPair.Key), currentPair.Value

‘ デバッグ出力(イミディエイトウィンドウで確認)
Debug.Print “Source ID: ” & currentPair.Key & ” –> Dest ID: ” & currentPair.Value & _
” [Primary: ” & currentPair.Primary & “]”
End If
Next i
End If

‘ 6. コピー後のオブジェクトに対する後続処理(例:新IDを使ったプロパティ操作)
Dim originalKeyStr As String
originalKeyStr = CStr(entitiesToCopy(0).ObjectID)

If idMap.Exists(originalKeyStr) Then
Dim newObjId As LongPtr ‘ 64bit環境を考慮する場合はLongPtr(VBA7以降)
newObjId = idMap(originalKeyStr)

‘ ObjectIDからオブジェクト本体を再バインドして操作
Dim clonedEntity As AcadEntity
Set clonedEntity = destDoc.ObjectIdToObject(newObjId)

‘ 例として色を赤に変更
clonedEntity.Color = acRed
MsgBox “オブジェクトのコピーとIDマッピングが正常に完了しました。”, vbInformation
End If

Exit Sub

ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
‘ 必要に応じたトランザクションロールバック等の後処理
End Sub

4. プロジェクトアーキテクトからの実践的アドバイス

実務でこのパターンを実装する際、以下の「罠」にハマりがちだ。プロの視点から回避策を提示する。

① 64bit環境におけるオブジェクトIDの型問題

AutoCADの近代バージョン(64bit版)では、オブジェクトIDはポインタサイズ(64bit整数)として扱われる。古いコードにあるような `Long` 型で受け渡していると、オーバーフローを起こす危険性がある。
VBA 7.0以降(Office 2010以降)環境であれば、IDを扱う変数は可能な限り `LongPtr` を採用し、Dictionaryのキーとして文字列化(`CStr()`)する手法が最も安全かつ確実である。

② 外部データベース(SQLite / Access等)との連携における留意点

図面間コピーを行うツール(例えば、定型アセンブリのパーツをマスター図面からプロジェクト図面に流し込む自動化など)では、コピー前の「元のID(Source ID)」を外部DBの外部キーとして保持しているケースが多い。
`IdPair` を解析して「旧ID $\rightarrow$ 新ID」の対応表さえ作れれば、外部DB側のリンク情報も一括でUPDATE文を発行して書き換えることができる。この設計を取り入れるだけで、外部連携ツールの堅牢性は桁違いに跳ね上がる。

③ トランザクションとエラーハンドリング

複数の画層やブロック定義が複雑に絡み合うアセンブリをコピーする場合、途中でメモリ不足やロック競合が発生するリスクがある。可能であれば、処理の前後でエラー監視を厳重に行い、部分的なゴミオブジェクトが図面に残らないクリーンな実装を心がけてほしい。

総括

`AcadDocument.Database.CopyObjects` とその戻り値 `IdPair` の解析は、単なる「図形のコピペ」を「高度なエンジニアリング・データマイグレーション」へと昇華させるための必須教養である。

コードをコピペして動かすだけではなく、その裏で何が行われているのか(データベース間のポインタ解決)を意識し、保守性が高く拡張性のある自動化システムを構築してほしい。あなたの開発するツールが、現場のエンジニアたちを退屈な手作業から解放する強力な武器になることを確信している。

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