「読み取り専用」の悲劇を回避せよ:作業データを死守するための「救済型セーブ」設計思想
業務自動化ツールを開発するエンジニアにとって、最も避けるべき事態は何か?
それは、バグでプログラムが止まることではない。「ユーザーが積み上げた数時間の作業を、エラーと共に消失させること」だ。
AutoCADの運用現場では、ネットワークドライブの権限設定や、他ユーザーによるファイルのロック、あるいは属性設定のミスにより、意図せず「読み取り専用(Read-Only)」として図面を開いてしまうケースが頻発する。
ユーザーがそれに気づかず、熱心に修正を加えた後、いざ「保存」を押した瞬間に発生する「書き込み権限がありません」という無機質なエラー。これに対し、単に `MsgBox` でエラーを表示して処理を終了させるコードを書いたなら、君の設計は「未熟」と言わざるを得ない。
プロフェッショナルが実装すべきは、「エラーを検知し、ユーザーの作業を安全な場所へ自動的に退避させる救済ロジック」である。本稿では、その極限の設計手法を伝授する。
1. なぜ、標準的なエラーハンドリングでは不十分なのか
一般的な開発者が書くコードは、以下のパターンに陥りがちだ。
' --- 避けるべき、典型的な「素人」のコード ---
Sub BadSaveRoutine()
On Error Resume Next ' これが諸悪の根源だ
ThisDrawing.Save
If Err.Number <> 0 Then
MsgBox "保存に失敗しました。"
End If
On Error GoTo 0
End Sub
このコードの致命的な欠陥は2点ある。
1. 原因の特定ができていない: 権限不足なのか、ディスク容量不足なのか、ファイルがロックされているのか。原因によって取るべき行動は異なる。
2. 救済措置がない: エラーを通知するだけで、ユーザーの編集内容はメモリ上に漂ったまま、図面を閉じればすべて消える。
設計思想として重要なのは、「エラーが発生した事実を、次のアクションへのトリガー(引き金)に変える」ことだ。
2. 救済型セーブのアーキテクチャ設計
堅牢なツールを構築するためには、以下の「フェーズ」を実装する必要がある。
1. Detection(検知): `Save` メソッドの失敗を、特定の実行時エラー番号(特に `Err.Number = 70`:書き込み拒否)で捕捉する。
2. Redirection(転送): 保存先を、権限の制約を受けにくい「ユーザーのデスクトップ」または「Tempフォルダ」へ動的に切り替える。
3. Sanitization(命名規則): 元のファイル名と衝突しないよう、タイムスタンプを付与した一意のファイル名を生成する。
4. Notification(通知): 救済が成功したことを明確に伝え、ユーザーに「どこにファイルがあるか」を指し示す。
3. プロダクション・グレードのコード実装
以下に、実務でそのまま組み込めるレベルの、堅牢なセーブ制御モジュールを示す。ここでは、`FileSystemObject` を活用してパス操作の確実性を高めている。
Option Explicit
''' <summary>
''' 図面を安全に保存するための高度なプロシージャ。
''' 読み取り専用エラーを検知した場合、デスクトップへ別名保存を試みる。
''' </summary>
Public Sub ProfessionalSafeSave()
Dim doc As AcadDocument
Set doc = ThisDrawing
Dim originalPath As String
originalPath = doc.FullName
' 1. 通常の保存を試行
On Error GoTo ErrorHandler
doc.Save
MsgBox "保存が完了しました。", vbInformation, "Success"
Exit Sub
ErrorHandler:
' エラー番号 70: Permission Denied (書き込み権限なし/読み取り専用)
' エラー番号 75: Path/Disk error (パスが存在しない/ディスクフル)
If Err.Number = 70 Or Err.Number = 75 Then
Debug.Print "Standard Save Failed. Attempting Rescue Mode..."
Call ExecuteRescueSave(doc, originalPath)
Else
' その他の予期せぬエラーは、通常のハンドリングへ
MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical, "Critical Error"
End If
On Error GoTo 0
End Sub
''' <summary>
''' エラー発生時に実行される救済ロジック。
''' </summary>
Private Sub ExecuteRescueSave(ByRef doc As AcadDocument, ByVal oldPath As String)
On Error GoTo RescueError
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim shell As Object
Set shell = CreateObject("WScript.Shell")
' 2. 保存先の設定(ユーザーのデスクトップ)
Dim desktopPath As String
desktopPath = shell.SpecialFolders("Desktop")
' 3. 一意のファイル名を生成 (例: Original_RESCUED_20231027_143005.dwg)
Dim timestamp As String
timestamp = Format(Now, "yyyymmdd_hhmmss")
Dim baseName As String
baseName = fso.GetBaseName(oldPath)
Dim extension As String
extension = fso.GetExtensionName(oldPath)
Dim rescuePath As String
rescuePath = desktopPath & "\" & baseName & "_RESCUED_" & timestamp & "." & extension
' 4. 救済保存の実行
doc.SaveAs rescuePath
' 5. ユーザーへの通知(非常に重要)
MsgBox "【警告】元のファイルは読み取り専用のため、保存できませんでした。" & vbCrLf & vbCrLf & _
"作業データを以下の場所に救済保存しました:" & vbCrLf & _
rescuePath, vbExclamation, "Rescue Mode Activated"
Exit Sub
RescueError:
MsgBox "【致命的エラー】救済保存にも失敗しました。作業内容が失われる可能性があります。" & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical, "Rescue Failed"
End Sub
コードの技術的ポイント
- `WScript.Shell` の活用: `Environ(“USERPROFILE”)` を使う方法もあるが、`SpecialFolders(“Desktop”)` を使う方が、OSの言語設定やユーザー環境の差異に強く、よりプロフェッショナルなアプローチだ。
- `FileSystemObject` (FSO): 文字列操作(パスの結合や拡張子の抽出)を自前で行うと、ドットの数やスラッシュの向きでバグを生む。FSOに任せるのが、保守性を高める鉄則だ。
- タイムスタンプの精度: 秒単位 (`hhmmss`) まで含めることで、連続して保存を試みた場合でもファイル名の衝突を防いでいる。
4. さらに高度な運用に向けたアドバイス
このコードは「救済」の第一歩に過ぎない。大規模な開発プロジェクトにおいては、以下の観点も検討すべきだ。
ファイルロックの監視
もし、特定のサーバー上の図面を常に扱うツールであれば、`SaveAs` を試みる前に、そのファイルが他者によってロックされているかを事前にチェックする仕組み(`FileLock` 検知)を導入することを推奨する。
データベースとの整合性
もし、図面の属性情報を外部データベース(SQL Server等)と同期させている場合、`SaveAs` でファイル名が変わってしまうと、DB側との紐付けが切れるリスクがある。救済保存を行った際は、「ファイル名が変わった事実」をログとしてDBに記録するか、ユーザーにDB更新の再試行を促す設計が必要だ。
ログの永続化
救済保存が行われた際、その履歴をローカルのテキストファイルやイベントログに書き出しておけば、後日「なぜファイルが分かれているのか?」というユーザーからの問い合わせに対して、ロジカルな回答が可能になる。
結論
優れたエンジニアは、コードが「動くこと」をゴールにしない。「壊れたときに、いかに優雅に、いかに被害を最小限に抑えて復帰できるか」をゴールに据える。
今回紹介した「救済型セーブ」のパターンを、君のライブラリの標準実装として組み込んでほしい。それが、ユーザーからの信頼を勝ち取り、真の「業務自動化のスペシャリスト」と呼ばれるための条件だ。
