【上級プロフェッショナル向け】AutoCAD VBA図面操作の極意:トランザクション管理とロールバック設計
開発プロジェクトの現場で、こんな悪夢を見たことはないだろうか。
「数百枚のDWGファイルを一括バッチ処理している最中、中盤のファイルで予期せぬエラー(メモリ不足や不正な図面データ)が発生。処理が異常終了した結果、半端にデータが書き換わった『ゾンビファイル』が生成され、元のバックアップも上書きされて消滅した」
AutoCAD VBAにおける図面オープン(`Documents.Open`)とセーブ(`SaveAs` / `Close`)の制御は、一歩間違えると業務インフラを破壊する諸刃の剣となる。特に標準のVBAには、データベースのような「華麗なロールバック機構(`ROLLBACK TRANSACTION`)」はネイティブでは用意されていない。
今回は、AutoCADのドキュメントライフサイクルとファイルI/Oの特性を完全に理解し、「失敗した時は、何事もなかったかのように元の状態へ完全復帰する」極限まで堅牢なトランザクション管理・ロールバック機構の設計思想と実装コードを伝授する。
—
1. なぜ標準のAutoCAD VBAは「危うい」のか?
多くの初級〜中級プログラマブルが陥る罠がこれだ。
‘ 【アンチパターン】絶対にやってはいけない実装
Sub DangerousProcess()
Dim doc As Document
Set doc = Documents.Open(“C:\Data\Target.dwg”)
‘ 何らかの危険な図面改変処理
Call DoSomethingDangerous(doc)
‘ エラーが起きようが起きまいが強制保存して閉じる
doc.Save
doc.Close
End Sub
このコードの何が致命的か?
1. 例外時の状態不整合: `DoSomethingDangerous` 内でエラーが発生した場合、`doc` は変更された「ダーティ(Dirty)」な状態でメモリ上に残り、最悪の場合はそのまま上書き保存される。
2. プロセスロック: ネットワークドライブ上で実行した場合、ファイルハンドルが中途半端に保持され、他のユーザーのアクセスを阻害する。
3. 不可逆性: 一度 `Save` を叩いたが最後、過去の状態には二度と戻せない。
プロのエンジニアが目指すべきは、「原子性(Atomicity)の担保」である。すなわち、「すべての処理が成功するか、一切の変更が加えられない(All-or-Nothing)」の原則をVBAのスコープ内で泥臭く、かつエレガントに実現することだ。
—
2. 堅牢なトランザクション設計の4原則
AutoCAD VBAで安全なファイル操作システムを構築するためには、以下の4つのアーキテクチャを設計に組み込む必要がある。
1. 事前スナップショット(物理ファイルの退避)
メモリ上のトランザクションに頼らず、OSレベルでの安全な一時コピー(バックアップ)をセキュアな領域に確保する。
2. 非表示(Invisible)ドキュメント操作
画面描画や余計なイベント発火を防ぐため、可能な限りバックグラウンドで処理を完結させる。
3. 厳格なエラーハンドリング(`On Error Goto` のスコープ管理)
どのフェーズでエラーが起きたかを正確に捕捉し、対応するロールバック関数へルーティングする。
4. 確実なリソース解放(ファイナライザ的発想)
正常終了時・異常終了時を問わず、開いたドキュメントや一時ファイルを必ずクリーンアップする。
—
3. 【プロダクションコード】安全なファイルオープン・セーブ・ロールバックの実装
以下のコードは、実務の現場でそのまま組み込める、堅牢なトランザクション管理クラスの核心部である。
特定の図面を開き、何らかの処理を施し、成功すれば上書き保存、失敗すれば一連の変更を完全に破棄して元のファイルを死守する。
Option Explicit
‘ ==============================================================================
トランザクション制御付き図面処理モジュール
‘ ==============================================================================
Public Sub SafeExecuteDrawingTransaction(ByVal targetFilePath As String)
Dim fso As Object
Dim backupPath As String
Dim targetDoc As Document
Dim isOpenedByMe As Boolean
Dim transactionSuccess As Boolean
Set fso = CreateObject(“Scripting.FileSystemObject”)
isOpenedByMe = False
transactionSuccess = False
‘ ————————————————————————–
‘ Phase 1: プリチェック & 物理バックアップ(ロールバックの担保)
‘ ————————————————————————–
If Not fso.FileExists(targetFilePath) Then
MsgBox “対象ファイルが存在しません: ” & targetFilePath, vbCritical
Exit Sub
End If
‘ 一時的なバックアップパスの生成(同一ディレクトリに .bak.tmp を作成)
backupPath = targetFilePath & “.bak.tmp”
On Error GoTo ErrorHandler
‘ 既存の古い一時ファイルがあれば削除
If fso.FileExists(backupPath) Then fso.DeleteFile backupPath, True
‘ 物理ファイルを複製(これが究極のロールバック保険となる)
fso.CopyFile targetFilePath, backupPath, True
‘ ————————————————————————–
‘ Phase 2: ドキュメントのオープン(既に開かれている場合のハンドリング含む)
‘ ————————————————————————–
Set targetDoc = GetOrOpenDocument(targetFilePath, isOpenedByMe)
If targetDoc Is Nothing Then
Err.Raise vbObjectError + 1000, “Transaction”, “図面を開くことができませんでした。”
End If
‘ ————————————————————————–
‘ Phase 3: ビジネスロジック(図面改変処理)の実行
‘ ————————————————————————–
‘ ※ここで例外が発生する可能性がある
Call ExecuteBusinessLogic(targetDoc)
‘ ————————————————————————–
‘ Phase 4: コミット(変更の確定)
‘ ————————————————————————–
‘ 自身で開いた場合のみ保存を実行(外部から既に開かれていた場合は呼び出し元に委譲するかポリシーによる)
targetDoc.Save
transactionSuccess = True
GoTo CleanUp
ErrorHandler:
‘ ————————————————————————–
‘ Rollback Phase: 異常系処理
‘ ————————————————————————–
transactionSuccess = False
Dim errDesc As String
errDesc = Err.Description
‘ メモリ上のドキュメントに変更が波及している場合、変更を破棄して閉じる
If Not targetDoc Is Nothing Then
On Error Resume Next
‘ 変更を保存せずに閉じる(SavedプロパティをTrueに偽装して強制閉鎖も有効)
targetDoc.Saved = True ‘ プロンプトを出さないためのテクニック
If isOpenedByMe Then
targetDoc.Close False
End If
On Error GoTo 0
End If
‘ 物理ファイルの復元(ロールバック実行)
If fso.FileExists(backupPath) Then
On Error Resume Next
fso.CopyFile backupPath, targetFilePath, True
On Error GoTo 0
End If
MsgBox “トランザクションエラーが発生しました。処理をロールバックしました。” & vbCrLf & _
“詳細: ” & errDesc, vbCritical
CleanUp:
‘ ————————————————————————–
‘ Finalizer: リソースの解放
‘ ————————————————————————–
On Error Resume Next
‘ 一時バックアップファイルの削除
If fso.FileExists(backupPath) Then fso.DeleteFile backupPath, True
‘ 自分が開いたもので、かつ正常終了した場合のクローズ処理
If isOpenedByMe And transactionSuccess And Not targetDoc Is Nothing Then
targetDoc.Close True
End If
Set fso = Nothing
Set targetDoc = Nothing
If transactionSuccess Then
‘ 正常終了ログなど
End If
End Sub
‘ — ヘルパー関数: 既に開かれているかを考慮した安全なドキュメント取得 —
Private Function GetOrOpenDocument(ByVal filePath As String, ByRef isOpenedByMe As Boolean) As Document
Dim doc As Document
Dim normalizedPath As String
normalizedPath = LCase(filePath)
‘ すでにAutoCADのセッション上で開かれているかチェック
For Each doc In Documents
If LCase(doc.FullName) = normalizedPath Then
Set GetOrOpenDocument = doc
isOpenedByMe = False
Exit Function
End If
Next doc
‘ 開かれていない場合は新規に開く
Set GetOrOpenDocument = Documents.Open(filePath)
isOpenedByMe = True
End Function
‘ — サンプルとしてのビジネスロジック —
Private Sub ExecuteBusinessLogic(ByRef doc As Document)
‘ 例として画層を追加する処理(ここで意図的にエラーを起こすテストも可能)
Dim lyr As AcadLayer
Set lyr = doc.Layers.Add(“PRO_AUTO_LAYER”)
lyr.Color = acRed
‘ 【テスト用】意図的にエラーを発生させる場合は以下のコメントを外す
‘ Err.Raise vbObjectError + 999, “BusinessLogic”, “意図的なシステムエラー”
End Sub
—
4. コードの解説とプロフェッショナルな知見
① 物理ファイルコピー(`.bak.tmp`)によるフォールバック
データベースの世界ではトランログやUNDOログを用いるが、ファイルベースのCADシステムにおいて最も確実で信頼性が高いのは、処理開始直前の「完全なバイナリ複製」を別名で保持することである。
VBAの `Scripting.FileSystemObject` を使い、同階層に一時退避させることで、AutoCADが異常終了してプロセスがクラッシュした後でも、OSレベルで元の状態へ100%復元できる。
② `targetDoc.Saved = True` のハック
AutoCAD VBAでドキュメントを `Close False`(変更を破棄して閉じる)しようとした際、図面に変更が加わっていると、まれにユーザーインタラクション(本当に保存しますか?のダイアログ)を要求されてバッチ処理がフリーズすることがある。
ロールバック時に `targetDoc.Saved = True` を明示的に挟むことで、AutoCAD側へ「この図面は変更されていない(保存済みである)」と認識させ、完全なサイレントクローズを実現している。このテクニックは無人バッチ処理において生命線となる。
③ 2重オープン防止の排他制御
`GetOrOpenDocument` 関数では、すでにユーザーがAutoCAD上で該当図面を開いているかどうかをコレクションから走査している。
もし既に開かれている場合、そのドキュメントインスタンスを借用し(`isOpenedByMe = False`)、処理終了後も閉じずにそのまま残す。この配慮がないと、ユーザーが手作業で開いていた図面をプログラムが勝手に閉じてしまい、現場から大クレームに発展する。
—
5. データベースや外部システム連携時の注意点
もしこのトランザクション処理の内部で、外部のSQLデータベース(Access, SQL Server等)への書き込みや、Excel集計表との同期を行っている場合、「AutoCADのロールバック」と「外部DBのロールバック」のスコープを一致させる必要がある。
- 2相コミットの原則: AutoCADファイルのロールバックに成功しても、外部DBのトランザクションがコミットされてしまえばデータ不整合が起きる。
- 外部連携を含む場合は、VBA内であってもADO (`ADODB.Connection`) の `BeginTrans` / `CommitTrans` / `RollbackTrans` を組み合わせ、「DBトランザクション開始 ➔ AutoCADファイル処理 ➔ 両方成功で一括コミット」という厳格な順序を組むこと。
—
6. まとめ:プロたる者、失敗を前提にコードを書け
「コードは絶対にエラーを起こさない」という性善説に基づいた設計は、プログラマの怠慢でしかない。ネットワークの切断、突然のメモリ不足、巨大すぎるDWGデータの破損――現場ではあらゆる異常が起こり得る。
今回紹介した「物理スナップショットによるロールバック機構」と「確実なリソースクリーンアップ」をあなたのツールキットに組み込むことで、どんな悪条件のバッチ処理であっても、社内のインフラとデータを絶対に守り抜く最強の業務自動化システムが完成する。
妥協のない堅牢な設計で、真のプロフェッショナルとしての開発をやり遂げてほしい。
