AutoCAD VBA:なぜ「SaveAs」でエラーが起きるのか? 非同期書き込みを制する「真の待機ループ」実装術
AutoCADの自動化において、最も初歩的でありながら、多くのエンジニアが「運任せのコード」で放置している領域がある。それが「図面の保存待機」だ。
`ThisDrawing.SaveAs` を実行した直後、次の処理へ移ろうとして「ファイルが使用中」や「アクセス拒否」でクラッシュする。そんな経験はないだろうか?
多くの初心者はここで `Sleep` 関数を使って「3秒待つ」といった愚かな実装をする。だが、大容量図面やネットワークストレージ環境では、その3秒すら裏切られる。本稿では、AutoCADの内部ライフサイクルを理解した上で、確実に保存完了を検知する堅牢な実装を伝授する。
—
なぜ `SaveAs` は「即時完了」しないのか
AutoCADの `SaveAs` メソッドは、呼び出された時点で「保存を開始せよ」というコマンドをOS側に投げるが、それが物理的なディスク書き込み完了を意味するわけではない。
特に以下の条件下では、同期ズレが致命的なバグを誘発する。
- ネットワークドライブへの保存: パケットの遅延や排他制御が絡む。
- 大容量図面(重い外部参照など): 保存処理中のプロセスがロックを保持し続ける。
- バックグラウンド保存機能: AutoCADのオプション設定により、保存が非同期で進む場合がある。
`DoEvents` を挟むだけのコードは、もはや「祈り」に近い。我々はエンジニアとして、「状態の監視」を行うべきだ。
—
堅牢な実装:FileSystemObjectを用いた「排他監視」
もっとも確実な方法は、保存されたファイルに対して「排他ロックが解除されるまで待つ」というロジックだ。VBAの `Scripting.FileSystemObject` を使い、ファイルが開けるようになるまでループを回す。
実装コード
このコードを標準モジュールに配置し、`SafeSaveAs` を呼び出すように設計せよ。
Option Explicit
‘ 保存待機のための安全なラッパー関数
Public Sub SafeSaveAs(doc As AcadDocument, filePath As String)
On Error Resume Next
‘ 保存実行
doc.SaveAs filePath
‘ 保存が完了するまで最大10秒間、排他ロックの解除を監視する
If Not WaitForFileReady(filePath, 10) Then
Err.Raise vbObjectError + 1001, “SafeSaveAs”, “保存完了を検知できませんでした。ファイルがロックされています。”
End If
On Error GoTo 0
End Sub
‘ ファイルが書き込み可能になるまで待機する関数
Private Function WaitForFileReady(ByVal path As String, ByVal timeoutSeconds As Long) As Boolean
Dim fso As Object
Dim startTime As Double
Dim fileNum As Integer
Set fso = CreateObject(“Scripting.FileSystemObject”)
startTime = Timer
Do
‘ ファイルが存在するか確認
If fso.FileExists(path) Then
‘ 排他アクセス権を試行(ファイルを開こうとしてみる)
On Error Resume Next
fileNum = FreeFile
Open path For Binary Access Read Write Lock Read Write As #fileNum
If Err.Number = 0 Then
‘ エラーなし=ロック解除成功
Close #fileNum
WaitForFileReady = True
Exit Function
End If
Err.Clear
On Error GoTo 0
End If
‘ 0.5秒のインターバルを置く(CPU負荷軽減)
DoEvents
Sleep 500 ‘ 要Windows API宣言
Loop While (Timer – startTime) < timeoutSeconds WaitForFileReady = False End Function ---
プロダクション環境での重要設計指針
このコードをあなたのツールに組み込む際、以下の3点を忘れてはならない。
1. Sleep関数の宣言: VBAには標準でSleepがないため、Windows APIの `kernel32` を宣言すること。
#If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
#Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
#End If
2. タイムアウトの設計: タイムアウト時間は、図面のサイズに応じて可変にするのが賢い。数MBなら2秒、数百MBなら15秒といった具合だ。定数で固定してはいけない。
3. エラーハンドリングの徹底: `SafeSaveAs` が失敗した際、作成中のファイルが破損していないかをチェックする処理(存在チェックとサイズ確認)を後続に置くこと。
結論:自動化は「待機」に宿る
多くのスクリプトが、処理の「実行」部分に注力し、「状態の確認」を怠る。しかし、AutoCADのような重いアプリケーションを操作する場合、「次に進むための条件」をいかに厳密に定義できるかこそが、真の自動化エンジニアの腕の見せ所だ。
この「排他監視ループ」は、保存だけでなく、外部データベースへの書き込みや、出力したCSVの読み込みにも応用できる。コードをコピペして満足するのではなく、なぜこのループが有効なのか、その裏側にあるOSのファイルシステム制御を理解してほしい。
自動化の安定性は、美しいコードよりも「泥臭い確認」から生まれるのだ。
