【上級】AcadApplication.Preferences.Files.SupportPathの動的制御:プロジェクトごとに異なる「フォント・ハッチング・パターン」を自動で追加・優先順位付けする
AutoCAD VBAの真のポテンシャルを引き出す時、我々は常にひとつの残酷な現実と向き合うことになる。それは、「AutoCADは本質的にドキュメント中心ではなく、セッション中心のアーキテクチャである」という事実だ。
`AcadApplication.Preferences.Files.SupportPath`(以下、SupportPath)は、AutoCADがフォント(.shx, .ttf)、ハッチングパターン(.pat)、カスタマイズファイル(.cuix)、そしてLISPプログラムを探索するための生命線である。しかし、このプロパティはアプリケーションレベル、すなわちレジストリ(`HKEY_CURRENT_USER`)に直結している。
プロジェクトAの専用フォントと、プロジェクトBの特殊なハッチングパターンがコンフリクトを起こす。あるいは、外部協力会社から受領した図面を開いた途端、フォントの置き換えダイアログの嵐に見舞われる——。この地獄絵図を、単なるマニュアル運用の徹底で乗り切ろうとするのは、エンジニアの怠慢でしかない。
今回は、図面(`AcadDocument`)のオープンイベントをフックし、そのプロジェクトに特化したサポートパスを動的に、かつ「最優先(最上位)」の順序でレジストリを汚染せずにインジェクションする極限の環境自動制御術を解説する。
—
1. SupportPath制御における致命的な罠とアーキテクチャの理解
まず、敵を知ることから始めよう。`SupportPath`は、セミコロン(`;`)区切りの文字列の集合体だ。
VBAからこれを操作する場合、以下の罠が待ち受けている。
1. 文字列処理のコスト: パスの追加・削除を単純な文字列結合で行うと、重複パスの発生や、セミコロンの末尾・二重付与によるAutoCADのパス解決エンジンのクラッシュを招く。
2. 永続化のジレンマ: アプリケーション終了時や別の図面を開いた際にも動的に追加したパスが残り続け、環境が汚染される。
3. イベントの非同期性: `AcadDocument`のオープン直後は、AutoCADの内部初期化シーケンスが完全に完了していない場合があり、早すぎるタイミングでのプロパティ書き換えは無視されるかエラーを吐く。
これらを克服するためには、アプリケーションのライフサイクルを完全に掌握し、厳密な配列操作アルゴリズムを構築する必要がある。
—
2. 実装コード:動的サポートパス制御エンジン
以下のコードは、特定のプロジェクトフォルダ構造(例: 図面と同じ階層、あるいは特定の共有サーバー上)を検知し、それを`SupportPath`の最先頭に割り込ませるためのクラスモジュールおよびイベントハンドラーの実装である。
クラスモジュール: `CPathManager.cls`
パスの重複排除、順序入れ替え、セミコロンの正規化をミリ秒単位の効率で処理するコアロジック。
Option Explicit
Private m_App As AcadApplication
Private Const TARGET_FOLDER_NAME As String = “ProjectSupport”
‘ コンストラクタでApplicationを安全にキャプチャ
Public Sub Initialize(ByVal appObj As AcadApplication)
Set m_App = appObj
End Sub
‘ 指定された図面のパスを解析し、プロジェクト固有のサポートパスを最優先で追加する
Public Sub InjectProjectSupportPath(ByVal doc As AcadDocument)
On Error GoTo ErrorHandler
‘ 新規図面(まだ保存されていない)の場合はスキップ
If doc.Path = “” Then Exit Sub
Dim targetPath As String
targetPath = doc.Path & “\” & TARGET_FOLDER_NAME
‘ フォルダが存在するかどうかをVBAのFileSystemObjectで確認
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(targetPath) Then
‘ プロジェクト専用パスが存在しない場合は何もしない
Exit Sub
End If
Dim currentPaths As String
currentPaths = m_App.Preferences.Files.SupportPath
‘ パスを最適化して再構築
Dim newPaths As String
newPaths = MergePathToTop(currentPaths, targetPath)
‘ 変更がある場合のみ書き込み(無駄なレジストリ書き込みを抑制)
If currentPaths <> newPaths Then
m_App.Preferences.Files.SupportPath = newPaths
Debug.Print “【SupportPath Updated】: ” & targetPath
End If
CleanUp:
Set fso = Nothing
Exit Sub
ErrorHandler:
MsgBox “SupportPathの動的制御中にエラーが発生しました: ” & Err.Description, vbCritical, “API Error”
Resume CleanUp
End Sub
‘ 既存のパスリストの最先頭にターゲットパスを挿入し、重複を排除する関数
Private Function MergePathToTop(ByVal originalPaths As String, ByVal topPath As String) As String
Dim pathArray() As String
pathArray = Split(originalPaths, “;”)
Dim i As Long
Dim cleanedList As String
cleanedList = “”
‘ 1. ターゲットパスと完全一致する既存の要素を排除しつつ、リストを再構築
For i = LBound(pathArray) To UBound(pathArray)
Dim p As String
p = Trim$(pathArray(i))
If p <> “” Then
‘ 大文字小文字を区別しない比較で重複チェック
If StrComp(p, topPath, vbTextCompare) <> 0 Then
If cleanedList = “” Then
cleanedList = p
Else
cleanedList = cleanedList & “;” & p
End If
End If
End If
Next i
‘ 2. 最先頭にターゲットパスを挿入
If cleanedList = “” Then
MergePathToTop = topPath
Else
MergePathToTop = topPath & “;” & cleanedList
End If
‘ 末尾の余分なセミコロンを排除する正規化
If Right$(MergePathToTop, 1) = “;” Then
MergePathToTop = Left$(MergePathToTop, Len(MergePathToTop) – 1)
End If
End Function
—
グローバルイベントハンドラー: `AcadAppEvent.cls` (またはThisDocument)
図面が開かれた瞬間(`AcadApplication_DocumentOpen`)をキャッチし、上記のマネージャーを駆動する。
Option Explicit
‘ WithEventsを使用してAutoCADアプリケーションのイベントを監視
Public WithEvents AppEvents As AcadApplication
Private m_PathMgr As CPathManager
Public Sub HookApplication()
Set AppEvents = ThisDrawing.Application ‘ または GetObject(, “AutoCAD.Application”)
Set m_PathMgr = New CPathManager
m_PathMgr.Initialize AppEvents
End Sub
‘ 図面オープン時のイベント
Private Sub AppEvents_DocumentOpen(ByVal Document As AcadDocument)
‘ 遅延実行が必要な場合はここでOnTime等を使うが、
‘ 単純なパス追加であれば直接呼び出しで十分機能する
m_PathMgr.InjectProjectSupportPath Document
End Sub
—
3. シニアエンジニアが押さえるべきパフォーマンスとメモリ管理の極意
ここまでのコードでも実用レベルに達しているが、真のチーフアーキテクトであれば、以下の「見えないコスト」への配慮を忘れない。
1. レジストリ書込み回数の最小化
`AcadPreferencesFiles.SupportPath`への代入は、内部でWindowsレジストリ(`HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\[Version]\[Profile]\Profiles\[ProfileName]\General`)への書き込みを引き起こす。
不要な代入(すでに最先頭にパスが存在する場合の書き込みなど)は、AutoCADのパフォーマンス低下やレジストリのフラグメンテーションを招くため、必ず `currentPaths <> newPaths` による差分検知(ガード節)を挟まなければならない。
2. COMオブジェクトの参照リーク対策
VBAにおける `CreateObject(“Scripting.FileSystemObject”)` は、明示的に変数スコープを抜けさせるか `Set fso = Nothing` を行わないと、VBAのランタイム環境にCOM参照が残り続ける原因となる。
特にイベントドリブンで頻繁に呼び出されるコードでは、わずかなメモリリークが長時間のバッチ処理や常時起動環境で致命的なメモリ肥大化を引き起こす。上記のコードで `CleanUp` ラベルを設け、確実に参照を破棄しているのはそのためだ。
3. マルチドキュメント環境(SDI / MDI)における競合
AutoCADはMDI(マルチドキュメントインターフェイス)環境において、複数の図面が同時に開かれる。
しかし、`SupportPath`はアプリケーション(セッション)全体でただ一つしか存在しない。
つまり、図面Aから図面Bへアクティブタブを切り替えた瞬間にパスが上書きされるような設計にすると、ユーザーが混乱する。
そのため、本稿で示した通り「図面オープン時(`DocumentOpen`)」にその図面のコンテキストに合わせたパスを“上書きではなく最優先挿入”するアプローチが、最も副作用が少なく堅牢な設計となる。
—
4. 運用への組み込みとデプロイメント
この仕組みを組織全体に展開する場合、個別の図面にVBAマクロを埋め込む(`acad.dvb`の自動ロードなど)アプローチはセキュリティ上のリスク(マクロウィルス対策としての無効化)や保守性の観点から推奨されない。
推奨アーキテクチャ:
1. 上記のコードをコンパイル済みのCOMアドイン(VB.NETで作成したDllImport / AutoCAD .NET APIのManagedPlugin)、あるいは全社共通の暗号化されたAutoCAD VBAプロジェクト(`acaddoc.lsp` から `NetLoad` または `VBALOAD` でサイレントロード)として配置する。
2. ネットワークドライブ上のプロジェクトフォルダ構造を標準化し、すべての図面群が決まった命名規則(例: `…\ProjectRoot\ProjectSupport\`)を持つように強制する。
文字化けやハッチングの消失という、長年設計現場を悩ませてきた「ヒューマンエラーと環境依存の呪縛」は、コードの力によって完全に根絶することができる。
プロパティの背後にあるアーキテクチャを理解し、システムを意のままに調教することこそが、我々オートメーション・エンジニアの存在意義である。
