【実務・中級編】【実務中級者向け】保存完了を検知する待機ループ:非同期書き込みによるエラーを防ぐVBA実装 – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:保存完了を検知する待機ループ――非同期書き込みの罠を断つ堅牢な設計

AutoCAD VBAによる図面自動処理の開発において、最も頻発し、かつ開発者を絶望の淵に追い込むバグの一つが「ファイル保存に起因するI/O競合とデータ破損」だ。

`ThisDrawing.SaveAs` や `Save` メソッドをコードに記述した瞬間、私たちは「処理が完了した」と錯覚する。しかし、OSやAutoCADの内部アーキテクチャ、ネットワークドライブのレイテンシという現実世界において、その認識は致命的な甘えである。

本稿では、AutoCADの非同期書き込み(あるいはバックグラウンド保存)の闇を暴き、実務の現場で絶対に破綻しない「保存完了検知待機ループ」の極限実装を伝授する。

—

1. なぜ `SaveAs` の直後の処理はクラッシュするのか?

多くの初中級プログラマブルなコードは、以下のように書かれている。

‘ 【アンチパターン】これでは確実に破綻する
ThisDrawing.SaveAs “C:\Projects\Output.dwg”, ac2018_DWG
‘ すぐに次の処理、またはファイルを閉じる処理へ移行
ThisDrawing.Close False

このコードがなぜ地雷なのか。理由は明確だ。
AutoCADの `SaveAs` メソッドは、ファイルI/Oの完了を待たずに制御をVBAに戻すケース(あるいはバックグラウンドでのシリアライズ処理)が存在する。特に以下の条件下では確率で致命傷を負う。

1. 巨大な図面データ:膨大なブロック定義、ハッチング、レジストリ辞書を持つDWGの書き込みには数秒〜数十秒を要する。
2. ネットワーク環境(NAS/共有サーバー):パケットの遅延やファイルロックの解放タイミングのズレ。
3. 連続バッチ処理:保存直後に外部プロセス(PDF変換、別システムへの連携、ファイルの移動・削除)がファイルを叩きに行くケース。

結果として、「ファイルがロックされたままアクセス拒否(エラー 70: 書き込みできません)」が発生するか、最悪の場合は0バイトの破損ファイルが生成される。

—

2. 堅牢な設計:ファイルサイズ監視と排他制御ループ

では、どう設計すべきか。
答えはシンプルだ。「ファイルシステム側から保存の完結を監視する(ポーリング)」こと。

AutoCADの内部状態を信じるな。ファイルが実際にディスク上に書き出され、サイズが安定し、かつ排他ロックが完全に外れた瞬間をコードで捉えるのだ。

堅牢な待機ループの要件

  • タイムアウト機構の搭載:無限ループによるプロセスのフリーズを絶対に防ぐ。
  • ファイルサイズの安定検知:書き込み中はファイルサイズが変動する。一定時間サイズの変化がないことを確認する。
  • 排他制御(File Locked Check):ファイルを開ける(`Open … For Binary Access Read Write`)かテストし、OSから完全に解放されたことを確認する。

—

3. 【プロダクションコード】実務で使える完璧な保存制御モジュール

以下のコードは、実務の現場でそのまま利用できる堅牢な保存・待機サブルーチンである。エラーハンドリングとリトライロジックを極限まで高めている。

Option Explicit

‘ ==============================================================================
‘ 担当者必携:非同期書き込み・I/O遅延を完全に吸収する安全な図面保存ルーチン
‘ ==============================================================================
Public Sub SaveDrawingSafely(ByVal targetDoc As Document, ByVal filePath As String, ByVal dwgVer As AutoCAD.AcSaveAsType)

Const MAX_TIMEOUT_SEC As Long = 30 ‘ タイムアウト制限(秒)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 既存ファイルがある場合は念のため事前に削除または上書き準備
On Error Resume Next
If fso.FileExists(filePath) Then
fso.DeleteFile filePath, True
End If
On Error GoTo 0

‘ 1. 保存実行(非同期トリガー)
targetDoc.SaveAs filePath, dwgVer

‘ 2. 監視ループの初期化
Dim startTime As Double
startTime = Timer

Dim fileSizeBefore As Long
Dim fileSizeAfter As Long
fileSizeBefore = -1

Do
‘ タイムアウト判定(無限ループ防止)
If (Timer – startTime) > MAX_TIMEOUT_SEC Then
Err.Raise vbObjectError + 1000, “SaveDrawingSafely”, “保存処理がタイムアウトしました。ファイルI/Oが遅延しているか、ロックされています: ” & filePath
End If

‘ わずかにCPU負荷を逃がしつつOSに処理を譲る(DoEventsの適切な利用)
DoEvents
Application.Update

‘ ファイルが生成されたかチェック
If fso.FileExists(filePath) Then
Dim targetFile As Object
Set targetFile = fso.GetFile(filePath)
fileSizeAfter = targetFile.Size

‘ ファイルサイズが前回チェック時と同じ、かつ 0バイトではない場合
If fileSizeAfter > 0 And fileSizeAfter = fileSizeBefore Then
‘ さらに「実際に排他ロックが外れているか(読み書き可能か)」をテスト
If TestFileAccess(filePath) Then
‘ 完全な書き込み完了とみなしてループ脱出
Exit Do
End If
End If

fileSizeBefore = fileSizeAfter
End If

‘ ポーリング間隔の調整(0.5秒ウェイト)
SleepByTimer 0.5

Loop

Set fso = Nothing
‘ 正常終了ログ等の記録をここに置く
End Sub

‘ ==============================================================================
‘ ヘルパー関数:ファイルがOSから完全に解放されている(排他ロック解除)かテスト
‘ ==============================================================================
Private Function TestFileAccess(ByVal filePath As String) As Boolean
Dim fileNum As Integer
On Error GoTo ErrorHandler

fileNum = FreeFile
‘ 排他制御モードでバイナリオープンを試みる
Open filePath For Binary Access Read Write Lock Read Write As #fileNum
Close #fileNum

TestFileAccess = True
Exit Function

ErrorHandler:
‘ ロックされている場合はエラーになるため False を返す
TestFileAccess = False
On Error GoTo 0
End Function

‘ ==============================================================================
‘ ヘルパープロシージャ:精度の高いタイマーウェイト
‘ ==============================================================================
Private Sub SleepByTimer(ByVal durationSec As Double)
Dim start As Double
start = Timer
Do While Timer < start + durationSec DoEvents ' 深夜跨ぎ(0時直前)のタイマーリセット対策 If Timer < start Then start = Timer End If Loop End Sub ---

4. コードのアーキテクチャ解説:プロの視点

この実装には、業務システムを支えるエンジニアとしてのこだわりが詰まっている。

1. `DoEvents` とタイマーの組み合わせ
VBAの `Sleep` APIを使うとAutoCADのUIスレッドごと完全にフリーズし、Windowsから「応答なし」と判定されるリスクがある。自作の `SleepByTimer` により、UIの描画やイベントキューを維持しつつ、安全にウェイトを入れている。
2. 二重の完了判定(サイズ安定 + 排他テスト)
単に「ファイルが存在する」「サイズが変わらない」だけでは不十分だ。OSやウイルス対策ソフトがファイルを掴んでいる瞬間を `TestFileAccess` 関数(`Lock Read Write` によるオープン試行)で正確に検知し、「外部システムが安全にそのファイルを触れる状態」を担保している。
3. 深夜跨ぎ(Midnight RollOver)への配慮
VBAの `Timer` 関数は、日付が変わると `0` にリセットされる。長時間のバッチ処理でこの罠を踏むと無限ループに陥るが、`If Timer < start Then` の補正を入れることで、24時間稼働する自動化サーバーでも耐えうる設計にしている。 ---

5. まとめ:自動化の信頼性は「細部」に宿る

AutoCAD VBAによる図面管理の自動化は、コードを書いて動かすだけなら初級者でもできる。しかし、ネットワークの負荷、大容量図面、複数プロセスの競合といった「現実のハードル」をいかにロジカルに超えるかが、プロフェッショナルとアマチュアを分ける境界線だ。

今回紹介した「保存完了検知待機ループ」をあなたの開発環境に組み込めば、ファイルI/Oに起因する謎のエラーとは永久に決別できるはずだ。

現場の信頼を勝ち取る、強靭なコードを書き続けよう。

タイトルとURLをコピーしました