AutoCAD VBAを掌握する極限の知見:ネットワーク切断時の「緊急ローカル退避保存」
開発プロジェクトのリーダーである私たちが、現場の設計者から最も恐れられる瞬間を知っているか?
それは、終業間際、数時間に及ぶ渾身のモデリングの末に、「ネットワークエラー:ファイルを保存できませんでした」という冷酷なダイアログが表示された瞬間だ。
共有サーバーの瞬断、VPNの切断、あるいはNASのトラブル。
CADオペレーターがいくら神業的なスピードで図面を描こうとも、インフラの魔神が微笑んだ瞬間に、未保存のデータは揮発する。AutoCADの自動保存(AUTOSAVE)機能もあるが、あれはタイムラグがあり、かつ指定パスがサーバー上に設定されている場合、ネットワーク障害時には無力と化す。
今回は、AutoCAD VBAのイベントとエラーハンドリングの深淵を突き詰め、「サーバー保存が失敗した瞬間に、自動でローカルのTemp領域へ緊急退避させ、作業ロスを絶対にゼロにする」ための堅牢なプロダクションコードを伝授する。
—
なぜ「通常のSaveAs」では現場を救えないのか?
素人プログラマーは、次のようなコードを書く。
‘ 【アンチパターン】これではネットワーク切断時に沈黙する
Sub BadSave()
On Error Resume Next
ThisDrawing.Save
If Err.Number <> 0 Then
MsgBox “保存に失敗しました!”
End If
On Error GoTo 0
End Sub
このアプローチがなぜゴミなのか?理由は3つある。
1. `ThisDrawing.Save` の非力さ:
すでに一度も保存されていない新規図面や、リモートパスへのアクセス権限・タイムアウト制御において、単なる `On Error` ではAutoCAD内部のCOM例外を完全に捕捉しきれないケースがある。
2. パスの動的書き換えの欠如:
保存先がダメなら、どこに・どのような命名規則で退避させるべきかという「フォールバック戦略」がコードに組み込まれていない。
3. ユーザーへの認知負荷:
エラーが出て止まるだけのプログラムは、パニックに陥った設計者を助けない。「どこに救出されたのか」を明確に示し、かつ図面オブジェクトのデータベース(Database)を汚染しない設計が不可欠だ。
プロフェッショナルは、「保存プロセスそのものを監視し、例外をトランザクションのように巻き戻してローカルへ強制退避させる」仕組みを構築する。
—
堅牢な緊急退避システムのアーキテクチャ
今回構築するシステムの要件は以下の通りである。
1. トランザクション的思考: サーバーへの上書き保存(`SaveAs` または `Save`)を実行し、エラーが発生した場合は即座にトラップする。
2. 安全なローカルパスの生成: 実行ユーザー名、元のファイル名、タイムスタンプを結合し、ファイル名の衝突(Collision)を防いだ状態で `Environ(“TEMP”)` へ退避させる。
3. 図面タイトルの維持: ローカルへ緊急退避させた後も、ドキュメントのポインタやステータスを適切に管理し、作業を継続できるようにする。
—
プロダクションコード:緊急ローカル退避モジュール
以下のコードを、AutoCADのVBAプロジェクト(`ThisDrawing` または標準モジュール)に実装せよ。実務の現場でそのままコピー&ペーストし、即座に稼働させることが可能なレベルまで磨き上げている。
Option Explicit
‘ ==============================================================================
‘ 処理名: ネットワーク切断対策・緊急ローカル退避保存ルーチン
‘ 概要 : サーバーへの保存失敗を検知し、ローカルTempへタイムスタンプ付きで強制退避
‘ ==============================================================================
Public Sub EmergencySaveToLocal()
Dim targetDoc As Document
Set targetDoc = ThisDrawing
Dim originalFullName As String
Dim fileNameOnly As String
Dim fileExtension As String
Dim localTempPath As String
Dim emergencyFileName As String
Dim timeStamp As String
‘ 1. 現在のドキュメントのパスと名前を取得
On Error GoTo PathError
originalFullName = targetDoc.FullName
‘ まだ一度も保存されていない(未命名)図面の場合のハンドリング
If originalFullName = “” Then
fileNameOnly = “UnsavedDrawing”
fileExtension = “.dwg”
Else
fileNameOnly = GetFileNameWithoutExtension(targetDoc.Name)
fileExtension = GetExtension(targetDoc.Name)
End If
On Error GoTo 0
‘ 2. タイムスタンプの生成 (YYYYMMDD_HHMMSS形式)
timeStamp = Format(Now, “yyyymmdd_HHMMSS”)
‘ 3. ローカルのTempフォルダパスを取得し、退避ファイル名を構築
localTempPath = Environ(“TEMP”) & “\”
emergencyFileName = localTempPath & fileNameOnly & “_EMERGENCY_” & timeStamp & fileExtension
‘ 4. 保存の試行(メイン処理)
‘ まずは通常通りに保存を試みる
On Error GoTo ServerSaveFailed
If originalFullName = “” Then
‘ 新規図面の場合は名前をつけて保存
targetDoc.SaveAs emergencyFileName
MsgBox “図面は未保存だったため、ローカルTempに緊急保存しました。” & vbCrLf & emergencyFileName, vbExclamation, “緊急退避完了”
Else
‘ 上書き保存を試行
targetDoc.Save
‘ ここに到達できればサーバー保存は成功
Exit Sub
End If
Exit Sub
ServerSaveFailed:
‘ ==========================================================================
‘ 【例外処理】サーバー保存時のエラー(ネットワーク切断等)をここでキャッチ
‘ ==========================================================================
Dim errDesc As String
errDesc = Err.Description
On Error GoTo LocalSaveFatal
‘ サーバーがダメなら、強制的にローカルTempへ「名前をつけて保存 (SaveAs)」を実行
‘ これにより、メモリ上の最新データを確実にハードディスクに掴み取る
targetDoc.SaveAs emergencyFileName
‘ 成功時の警告とリカバリ案内
MsgBox “【警告】ネットワーク接続のエラーを検知しました!” & vbCrLf & _
“サーバーへの保存に失敗したため、データを以下のローカルパスに緊急退避しました。” & vbCrLf & vbCrLf & _
emergencyFileName & vbCrLf & vbCrLf & _
“エラー詳細: ” & errDesc, _
vbCritical + vbOKOnly, “【緊急】データ保護発動”
Exit Sub
LocalSaveFatal:
‘ ネットワークもローカルディスクすら書き込み不可という極限状態
MsgBox “【致命的エラー】ローカルストレージへの退避にも失敗しました。” & vbCrLf & _
“CADを終了せず、外部ストレージやクリップボードへの退避を手動で行ってください。” & vbCrLf & _
“エラー: ” & Err.Description, _
vbCritical, “完全保存失敗”
Exit Sub
PathError:
MsgBox “図面情報の取得に失敗しました。”, vbCritical, “エラー”
End Sub
‘ — ヘルパー関数: 拡張子なしのファイル名を取得 —
Private Function GetFileNameWithoutExtension(ByVal fullName As String) As String
Dim p As Long
p = InStrRev(fullName, “.”)
If p > 0 Then
GetFileNameWithoutExtension = Left(fullName, p – 1)
Else
GetFileNameWithoutExtension = fullName
End If
End Function
‘ — ヘルパー関数: 拡張子を取得 —
Private Function GetExtension(ByVal fullName As String) As String
Dim p As Long
p = InStrRev(fullName, “.”)
If p > 0 Then
GetExtension = Mid(fullName, p)
Else
GetExtension = “.dwg”
End If
End Function
—
コードの設計思想とエンジニアリング的解説
このコードが「なぜプロフェッショナル仕様なのか」、その裏側にある設計思想を解説する。
1. 二段構えの例外トラップ(フォールバック・パターン)
通常の `ThisDrawing.Save` でエラーが発生した際、処理は `ServerSaveFailed` ラベルへジャンプする。
ここではエラーメッセージを保持したまま、即座に `targetDoc.SaveAs emergencyFileName` を実行する。
これにより、「ネットワークパスへの書き込み試行で発生したタイムアウトやI/Oエラーをバイパスし、確実にI/Oの向き先をローカルへ切り替える」という離れ業をやってのけている。
2. 衝突を防ぐ一意の命名規則(Collision Avoidance)
緊急退避の際に単に `backup.dwg` などという名前をつけてはならない。複数回エラーが起きた場合や、複数の図面を同時に扱っている場合に上書き事故が起きる。
`Format(Now, “yyyymmdd_HHMMSS”)` を付与することで、タイムスタンプベースの完全な一意性を担保し、データのロストを防いでいる。
3. 最悪のシナリオ(LocalSaveFatal)への備え
ネットワークもダメ、もしやローカルの空き容量もない、あるいは権限がないという「最悪の極限状態」すら想定している。
万が一ローカル保存すら失敗した場合でも、プログラムがクラッシュしてAutoCADごと落ちるのを防ぎ、ユーザーに手動退避を促すメッセージボックスをポップさせる。プログラマブルな傲慢さを捨て、最後の砦としてメッセージでユーザーを導く設計だ。
—
現場への導入と運用のアドバイス
このマクロを現場に展開するにあたり、以下のポイントをリーダーとして徹底してほしい。
- リボンメニューやCUIへの統合:
設計者に `Alt + F8` でマクロを開かせてはならない。クイックアクセスツールバーやカスタムリボンにこの `EmergencySaveToLocal` を割り当て、「通常の保存ボタンの代わりにこれを押させる」、あるいはアプリケーションイベント(`DocumentBeforeSave` など)のフックと組み合わせることで、意識させずにこの安全網を機能させるのが理想だ。
- Tempフォルダの定期清掃:
このスクリプトはローカルの `Temp` にファイルを量産するため、Windowsのタスクスケジューラなどで古いTempファイルを掃除するポリシーをIT部門と連携して組んでおくこと。
インフラストラクチャはいつか必ず裏切る。だが、コードを書く私たちは、その裏切りからユーザーの労力を守り抜くことができる。
この知見をあなたの開発環境にインストールし、真に堅牢なAutoCAD自動化環境を構築せよ。
