1. ACIの限界と「AcCmColor」アーキテクチャの必然性
AutoCADの製図環境において、伝統的なACI(AutoCAD Color Index: 1〜255色)は、レイヤー(画層)によるペン割り当てやモノクロ出力を前提としたレガシーな運用において主役であり続けてきました。しかし、BIM/CIMへの移行、GISデータのインポート、コーポレートカラー(PANTONEやDIC)の厳密な再現、あるいはレンダリング精度が求められる現代の統合CADシステムにおいて、ACIの「256色」という制約は致命的なボトルネックとなります。
この限界を突破するために導入されたのが、24bit(約1,670万色)のフルカラーをサポートするTrueColor(RGB)およびカラーブック(ColorBook)システムです。
AutoCAD VBAにおいて、単にエンティティの `Color` プロパティにインデックス数値を代入するだけでは、この高度な色彩空間にアクセスすることはできません。これを完全制御するために用意されたCOMオブジェクトが `AcCmColor` です。
[AutoCAD Entity (e.g., AcadLine)]
│
└── .TrueColor ──> [ AcCmColor Object ]
├── .ColorMethod (RGB / ACI / ColorBook)
├── .SetRGB(R, G, B)
└── .SetColorBookColor(BookName, ColorName)
しかし、実務の最前線における最大の障壁は、「`AcCmColor` オブジェクトをどのようにして安全かつ、バージョン非依存にインスタンス化するか」という点にあります。
VBAの参照設定(Early Binding)で特定のAutoCADタイプライブラリ(`acaxXXdb.tlb` など)を直接参照して `New` 宣言を行う手法は、単一バージョンのクローズドな環境でしか機能しません。バージョンが異なるAutoCADが混在するエンタープライズ環境や、将来的なアップグレード(例:AutoCAD 2021から2026への移行)において、参照設定の破損(Missing Reference)はシステムを即座にクラッシュさせます。
これを回避し、完全に動的なバインド(Late Binding)と堅牢なライフサイクル管理を実現するための唯一無二の手段が、`AcadApplication.GetInterfaceObject` メソッドによるCOMインターフェース経由の動的生成です。
—
2. ProgIDの動的解決:バージョン互換性の極限設計
`GetInterfaceObject` を呼び出す際、引数に渡すプログラミングID(ProgID)は、AutoCADのバージョンに密接に紐付いています。
例えば、`AcCmColor` のProgIDは以下のように変遷しています。
- AutoCAD 2018/2019/2020: `AutoCAD.AcCmColor.22`
- AutoCAD 2021/2022: `AutoCAD.AcCmColor.24`
- AutoCAD 2025: `AutoCAD.AcCmColor.25`
バージョン非依存の ProgID として `”AutoCAD.AcCmColor”` も存在しますが、レジストリの破損や複数バージョンの共存環境(Side-by-Side)においては、意図しないバージョンのCOMサーバーが呼び出され、内部でバイナリ不整合による予期せぬエラー(エラーコード `429: Active Xコンポーネントはオブジェクトを作成できません`)を引き起こすリスクがあります。
したがって、プロフェッショナルな設計においては、稼働中のAutoCADインスタンスのメジャーバージョンを動的に取得し、適切なProgIDをその場で組み立ててインスタンス化する「レジリエントなファクトリパターン」の実装が必須です。
以下に、その動的解決のコアロジックを示します。
‘================================================================================
‘ AutoCADバージョンから適切なAcCmColorのProgIDを動的に生成するヘルパー関数
‘================================================================================
Public Function GetAcCmColorProgID(ByVal app As AcadApplication) As String
On Error GoTo Error_Handler
Dim verStr As String
Dim majorVer As Double
Dim suffix As String
‘ “24.1s (LMS Tech)” のような文字列からメジャーバージョンを抽出
verStr = app.Version
majorVer = Val(Left(verStr, InStr(verStr, “.”) + 1))
‘ メジャーバージョンに対応するProgIDのサフィックスを判定
Select Case majorVer
Case 25.0: suffix = “25” ‘ AutoCAD 2025
Case 24.3: suffix = “24.3” ‘ AutoCAD 2024
Case 24.2: suffix = “24.2” ‘ AutoCAD 2023
Case 24.1: suffix = “24.1” ‘ AutoCAD 2022
Case 24.0: suffix = “24” ‘ AutoCAD 2021
Case 23.1: suffix = “23.1” ‘ AutoCAD 2020
Case 23.0: suffix = “23” ‘ AutoCAD 2019
Case 22.0: suffix = “22” ‘ AutoCAD 2018
Case Else
‘ 未知の最新バージョンの場合は、バージョン非依存のProgIDにフォールバック
GetAcCmColorProgID = “AutoCAD.AcCmColor”
Exit Function
End Select
GetAcCmColorProgID = “AutoCAD.AcCmColor.” & suffix
Exit Function
Error_Handler:
GetAcCmColorProgID = “AutoCAD.AcCmColor”
End Function
—
3. 【実戦コード】TrueColor & ブックカラー完全制御クラス
以下に、上記の動的ProgID解決を組み込み、TrueColor(RGB)およびカラーブック(DIC / PANTONEなど)を安全にオブジェクトに適用する、極めて実用的で堅牢な標準モジュールの実装コードを示します。
このコードは、大量の図形処理(10万エンティティ超)におけるメモリリークを防止するための明示的なCOM解放ロジックと、Windows API(`QueryPerformanceCounter`)によるミリ秒単位のプロファイリングを意識した設計となっています。
Option Explicit
‘================================================================================
‘ Win32/Win64 API宣言:高精度タイマーによるパフォーマンス監視用
‘================================================================================
If VBA7 Then
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
Else
Private Declare Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
End If
‘================================================================================
‘ メインエントリーポイント:選択したオブジェクトのカラーをTrueColor / BookColorに書き換える
‘================================================================================
Public Sub ExecuteAdvancedColorControl()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim selectSet As AcadSelectionSet
Dim entity As AcadEntity
Dim colorObj As Object ‘ Late Binding (IAcadAcCmColor)
Dim progId As String
‘ 高精度タイマー初期化
Dim tStart As Currency, tEnd As Currency, tFreq As Currency
QueryPerformanceFrequency tFreq
QueryPerformanceCounter tStart
On Error GoTo Error_Handler
‘ AutoCADインスタンスの取得
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument
‘ 1. バージョン対応ProgIDの動的取得
progId = GetAcCmColorProgID(acadApp)
‘ 2. GetInterfaceObjectによるCOMインスタンス生成
Set colorObj = acadApp.GetInterfaceObject(progId)
If colorObj Is Nothing Then
Err.Raise vbObjectError + 513, “ExecuteAdvancedColorControl”, “AcCmColorインスタンスの生成に失敗しました。ProgID: ” & progId
End If
‘ 3. カラーパラメータの設定(例1:TrueColor RGB指定)
‘ RGB(14, 115, 186) – 鮮やかなシアンブルーを適用
colorObj.SetRGB 14, 115, 186
‘ 【重要】もしカラーブックを使用する場合は以下のように設定(環境にカラーブックファイル (.acb) が存在することが前提)
‘ colorObj.SetColorBookColor “PANTONE(R) solid coated”, “PANTONE Reflex Blue C”
‘ 4. 選択セットの作成とバッチ処理
On Error Resume Next
acadDoc.SelectionSets.Item(“TempColorSet”).Delete
On Error GoTo Error_Handler
Set selectSet = acadDoc.SelectionSets.Add(“TempColorSet”)
‘ ユーザーに図面上のオブジェクトを選択させる
AppActivate acadApp.Caption
selectSet.SelectOnScreen
If selectSet.Count = 0 Then
MsgBox “オブジェクトが選択されませんでした。”, vbExclamation, “処理中断”
GoTo CleanUp
End If
‘ 高速化のための画面更新抑止
acadDoc.StartUndoMark
acadApp.ScreenUpdating = False
‘ 5. イテレーションと適用
For Each entity In selectSet
‘ エンティティにAcCmColorオブジェクトをディープコピー(参照代入)
‘ AutoCAD内部のCOM境界を越えてオブジェクトが複製される
entity.TrueColor = colorObj
Next entity
‘ 画面の再描画と更新の再開
acadDoc.EndUndoMark
acadDoc.Regen acAllViewports
acadApp.ScreenUpdating = True
QueryPerformanceCounter tEnd
Dim elapsedSec As Double
elapsedSec = (tEnd – tStart) / tFreq
MsgBox “カラー適用を完了しました。” & vbCrLf & _
“適用件数: ” & selectSet.Count & ” 件” & vbCrLf & _
“処理時間: ” & Format(elapsedSec, “0.000”) & ” 秒”, vbInformation, “成功”
CleanUp:
‘============================================================================
‘ COMオブジェクトのライフサイクルとメモリマネジメント(最重要)
‘============================================================================
On Error Resume Next
If Not selectSet Is Nothing Then
selectSet.Clear
selectSet.Delete
Set selectSet = Nothing
End If
‘ GetInterfaceObjectで生成したCOMオブジェクトは明示的にNothingを代入して
‘ 参照カウントをデクリメントし、AutoCADのヒープ領域から解放する
If Not colorObj Is Nothing Then
Set colorObj = Nothing
End If
‘ 画面更新を確実に復帰させる
If Not acadApp Is Nothing Then
acadApp.ScreenUpdating = True
End If
Exit Sub
Error_Handler:
acadApp.ScreenUpdating = True
MsgBox “致命的エラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー原因: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub
—
4. COMのライフサイクルとメモリマネジメントの極意
AutoCAD VBAで `GetInterfaceObject` を使用する際、多くの開発者が陥るのが「暗黙のメモリリーク」です。
VBAは参照カウント方式(RC)のGC(ガベージコレクション)を採用していますが、`GetInterfaceObject` を介して生成されたオブジェクトは、VBAのプロセス空間ではなく、AutoCAD(`acad.exe`)のプロセス空間(アウトプロセス、またはインプロセスのCOMサーバー)に実体が生成されます。
なぜ `Set objColor = Nothing` が命綱なのか?
VBA内でプロシージャが終了すれば、ローカル変数は自動的に解放されるように見えます。しかし、AutoCADエンティティに `entity.TrueColor = colorObj` としてオブジェクトをアサインした際、AutoCADの内部C++コア(`AcDbDatabase`)側でそのカラーオブジェクトのコピー、または参照の保持が発生します。
VBA側の変数 `colorObj` を明示的に `Nothing` にしないままループを回したり、処理を終了したりすると、COMのスタブ(Stub)やプロキシ(Proxy)がメモリ内にゾンビのように残留し、以下のような深刻な不具合を引き起こします。
1. メモリ肥大化: 数千回、数万回のループ処理において、AutoCADのプライベートワーキングセットが数GBに達し、最終的に `E_OUTOFMEMORY` で強制終了する。
2. OLEアクションのロック: 図面を閉じようとした際に、COMの参照カウントが「0」に達していないため、AutoCADプロセスがバックグラウンドに残り続け(`acad.exe` がタスクマネージャーに残留)、次回起動時にファイル共有ロックエラーが発生する。
対策:
上記コードの `CleanUp:` セクションにある通り、処理が正常終了しようが、途中で異常終了(エラー発生)しようが、必ず実行経路が `CleanUp` を通過し、`Set colorObj = Nothing` を呼び出すことを保証する構造(構造化例外処理に準ずる設計)を徹底してください。
—
5. レガシー環境の保守:32bit / 64bit混在とバージョン混在の克服
大規模なエンタープライズ環境では、数世代前のAutoCAD(32bit版)が工場の端末や協力会社の環境で未だ現役で稼働しているケースがあります。また、VBAを実行するホスト環境(ExcelやAccessなど)が 32bit であるか 64bit であるかによっても、COM連携の挙動は大きく変化します。
1. 32bit / 64bitの境界(WOW64とCOMサロゲート)
VBAホスト(例: 64bit版 Excel)から 64bit版 AutoCAD を操作する場合、同一ビット幅(In-process)の高速な通信が行われます。しかし、32bit版 Excel から 64bit版 AutoCAD を操作する場合、Windowsの WOW64 (Windows on Windows 64-bit) および COMサロゲート (`dllhost.exe`) を介したプロセス間通信(IPC)が発生します。
この状況下では、COMのデータマーシャリング(データのシリアライズと転送)のオーバーヘッドが数倍から数十倍に膨れ上がります。
- 対策: `GetInterfaceObject` の呼び出し頻度を最小化するため、ループの「外側」で一度だけ `AcCmColor` を生成し、ループ内ではプロパティ(`SetRGB`等)の書き換えのみを行い、既存のオブジェクトを使い回す設計(Flyweightパターン)を徹底してください。
2. カラーブック(DIC / PANTONE)使用時の落とし穴
カラーブック名(例: `”DIC Color Guide”`)やカラー名(例: `”DIC 125″`)をAPI経由で指定する場合、実行環境のAutoCADに該当する `.acb` (AutoCAD Color Book) ファイル が正しくインストールされ、サポートパスに配置されている必要があります。
もし、クライアントPCに該当する `.acb` ファイルが存在しない場合、`SetColorBookColor` メソッドは実行時エラー(`Invalid Input`)をスローし、マクロはそこで停止します。
- 保守的なアプローチ: 実務用の配布ツールでは、カラーブックによる指定を行う前に、必ず `Dir` 関数等を用いて、AutoCADのサポートパス(`C:\Program Files\Autodesk\AutoCAD 20XX\Support\Color` 等)に対象の `.acb` が存在するかを確認するか、エラーハンドリングでACI(インデックスカラー)にフォールバックする二重の安全弁(Fail-Safe)を仕込んでおくのがシニアアーキテクトとしての作法です。
—
6. 結論:美しきコードが担保する、図面のライフサイクル
本稿で解説した `AcadApplication.GetInterfaceObject` による `AcCmColor` の動的制御は、単に「図面をカラフルにする」ための技術ではありません。
これは、「図面データという長期的な資産を、変化し続けるITインフラ(OS、Office、CADのバージョンアップ)から完全に隔離し、常に安定して稼働させるための防護壁」を構築する技術です。
レガシーコードへの敬意を払いつつも、動的バインド、厳密なエラー捕捉、そしてCOMライフサイクルの完璧な管理を組み合わせることで、あなたのVBAシステムは今後10年、何ら修正を加えることなく、次世代のAutoCAD環境でも静かに、そして高速に動き続けることでしょう。
美しき色彩の背後には、常に冷徹なまでに磨き上げられたアーキテクチャが存在するのです。
