AutoCAD VBAにおける「情報の完全抹消」:機密レイヤー排除による配布用DWG生成の極限設計
設計業務の自動化において、我々エンジニアが直面する最大の壁は「機能の実装」ではない。実装した機能が「意図しない副作用」を招いたとき、あるいは「守るべき情報を漏洩させた」とき、そのシステムの価値はゼロどころか、負の遺産へと転じる。
特に、社外への図面配布プロセスにおいて、特定の機密情報(コスト、部材詳細、設計者情報など)が含まれるレイヤーを「なんとなく」削除する程度のスクリプトでは、プロフェッショナルの現場では通用しない。オブジェクトの依存関係、ブロック定義への残留、そしてメモリ上のキャッシュ――。これらを完全に制御し、「数学的にクリーンな配布用DWG」を生成するアーキテクチャが求められる。
本稿では、単なるレイヤー削除を超えた、実務に耐えうる「セキュリティ・マクロ」の設計思想とその実装について、深淵なる知見を共有する。
1. 破壊的変更のジレンマ:現在のドキュメントを汚染させない設計
多くの初級者が陥る罠がある。それは、「現在開いている図面(ActiveDocument)に対して直接削除操作を行い、その後にSaveAsを実行する」という手法だ。
これは極めて危険な設計である。万が一、削除プロセス中にエラーが発生した場合、ユーザーが作業中の「マスターデータ」が破壊されるリスクがあるからだ。
アーキテクチャの定石:Copy-on-Write 戦略
プロフェッショナルな自動化においては、「作業ドキュメントは一切触らず、複製した一時ファイルに対して外科手術を行う」という戦略をとる。
1. Master DWG: ユーザーが作業中の原本。
2. Temp DWG: `SaveAs` により作成された、一時的な作業用コピー。
3. Distribution DWG: 全ての機密要素を抹消し、`Purge` を完了させた最終成果物。
このフローを遵守することで、エンジニアリングにおける「非破壊性」を担保する。
2. 実装:機密レイヤー抹消エンジン
以下に、Windows Script Host (FileSystemObject) を活用してファイル操作を堅牢にし、オブジェクトのライフサイクルを意識した実装例を示す。
Option Explicit
' ==============================================================================
' Module: SecurityPurgeModule
' Description: 特定のレイヤーを完全に排除し、配布用DWGを生成する
' Author: Legendary Chief Architect
' ==============================================================================
Sub GenerateDistributionDWG()
Dim acadApp As Object
Dim doc As Object
Dim tempDoc As Object
Dim fso As Object
Dim masterPath As String
Dim distFolder As String
Dim tempPath As String
Dim distPath As String
Dim targetLayers As Variant
Dim i As Integer
' --- 設定領域 ---
' 削除対象とする機密レイヤー名(配列で指定)
targetLayers = Array("CONFIDENTIAL_COST", "INTERNAL_REVISION", "SECRET_DATA")
' 配布用ファイルの保存先ディレクトリ
distFolder = "C:\AutoCAD_Output\Distribution\"
' ----------------
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application
Set doc = ThisDrawing ' 現在のマスター図面
Set fso = CreateObject("Scripting.FileSystemObject")
masterPath = doc.FullName
' 1. 保存先フォルダの存在確認と作成
If Not fso.FolderExists(distFolder) Then
fso.CreateFolder (distFolder)
End If
' 2. ファイルパスの生成
' 拡張子を除いたファイル名を取得
Dim fileName As String
fileName = fso.GetBaseName(masterPath)
tempPath = distFolder & fileName & "_TEMP.dwg"
distPath = distFolder & fileName & "_DIST.dwg"
' 3. [重要] マスター図面を一旦保存し、コピーを作成
' 直接SaveAsすると現在のドキュメントが切り替わってしまうため、
' 一旦別名で保存したものを操作するか、ファイルを物理コピーする。
' ここでは安全のため、物理コピーを推奨する。
If fso.FileExists(tempPath) Then fso.DeleteFile tempPath
fso.CopyFile masterPath, tempPath
' 4. コピーした図面をバックグラウンド(あるいは別ドキュメント)として開く
' 注意: VBAでは既存のインスタンス内で開く必要があるため、
' 一旦TempをActiveにするか、別のAutoCADプロセスを制御する手法があるが、
' ここでは「現在の図面をTempとして扱い、作業後に閉じる」堅実な手法をとる。
' 一旦現在の作業を保存
doc.Save
' TempファイルとしてSaveAsを実行(現在のドキュメントがTempに切り替わる)
doc.SaveAs tempPath
Set doc = ThisDrawing ' 再定義
' --- 外科手術フェーズ ---
Debug.Print "Starting surgical removal of confidential layers..."
' 5. レイヤー内のオブジェクトを物理的に削除
For i = LBound(targetLayers) To UBound(targetLayers)
Call EraseLayerContents(doc, CStr(targetLayers(i)))
Next i
' 6. レイヤー自体の削除
For i = LBound(targetLayers) To UBound(targetLayers)
On Error Resume Next ' レイヤーが存在しない場合のエラー回避
doc.Layers.Item(CStr(targetLayers(i))).Delete
On Error GoTo ErrorHandler
Next i
' 7. [極限の知見] Databaseの徹底清掃 (PURGE)
' レイヤーを消しても、ブロック定義やスタイルに「残骸」が残る可能性がある。
' これを放置すると、ファイルサイズが肥大化し、機密情報の断片が残る。
doc.SendCommand "-PURGE" & vbCr & "All" & vbCr & vbCr & vbCr
doc.SendCommand "-PURGE" & vbCr & "Regapps" & vbCr & vbCr
' 8. 最終成果物の保存
doc.SaveAs distPath
Debug.Print "Distribution file created successfully: " & distPath
' 9. 後処理:Tempファイルの削除
' doc.Close は現在の作業を中断させるため、ユーザーへの通知が必要。
' 本来は、別のAutoCADプロセスを起動して処理するのが理想的。
MsgBox "配布用ファイルの生成が完了しました。" & vbCrLf & "保存先: " & distPath, vbInformation
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
"Error No: " & Err.Number & vbCrLf & _
"Description: " & Err.Description, vbCritical
' 異常終了時、Tempファイルが残っている場合は削除を検討
End Sub
' ------------------------------------------------------------------------------
' Sub: EraseLayerContents
' Description: 指定されたレイヤーに属するすべてのエンティティを削除する
' ------------------------------------------------------------------------------
Private Sub EraseLayerContents(ByRef targetDoc As Object, ByVal layerName As String)
Dim objEntity As Object
Dim i As Long
' ModelSpaceとPaperSpaceの両方を走査
Dim space As Variant
For Each space In Array(targetDoc.ModelSpace, targetDoc.PaperSpace)
' 後ろからループを回すのは、コレクションから要素を削除する際の定石
For i = space.Count - 1 To 0 Step -1
Set objEntity = space.Item(i)
If objEntity.Layer = layerName Then
objEntity.Delete
End If
Next i
Next space
End Sub
3. 技術的深掘り:なぜ `Purge` なのか?
コード内で `doc.SendCommand “-PURGE”` を実行している理由を理解してほしい。これは単なる「ファイル軽量化」ではない。
AutoCADのデータベース構造において、レイヤーを削除したとしても、そのレイヤーを参照していた「ブロック定義(Block Definition)」や「文字スタイル(Text Style)」、あるいは「ダイナミックブロックのパラメータ」がメモリおよびDB内に残留することがある。
これらは、高度な解析ツールやバイナリエディタを用いれば、機密情報の断片(例えば、削除したレイヤー名そのものや、そのレイヤー上にあった文字列の属性データ)として復元されるリスクがある。
`PURGE` コマンドを、`Regapps`(登録済みアプリケーション)を含めて実行することは、「データの痕跡を、データベースの論理構造レベルで抹消する」という、セキュリティ・エンジニアリングにおける必須プロセスである。
4. パフォーマンスとメモリの最適化
大量のオブジェクトを扱う場合、`For Each` ループは極めて低速になる可能性がある。
より高度な実装を求めるなら、`SelectionSets` を利用して、対象レイヤーのオブジェクトを一括で取得し、一括削除(`Erase`)を行うべきだ。
また、VBAにおけるオブジェクトの解放(`Set obj = Nothing`)は、大規模なバッチ処理を行う際には必須である。AutoCADのCOMインターフェースは、参照カウントが適切に管理されない限り、メモリリークを引き起こし、長時間稼働させるシステムにおいて「AutoCADの突然の強制終了」を招く主因となる。
5. 結論
図面配布の自動化は、単なる「作業の効率化」ではない。「情報の境界線を守る」という、企業の信頼性に直結する防衛策である。
我々アーキテクトが目指すべきは、「実行した瞬間に、その図面が何を包含し、何を排除したかが、論理的に証明できるコード」を書くことだ。このマクロが、貴殿のシステムの堅牢性を一段上のレベルへと引き上げることを切に願う。
