【実務・中級編】【上級プロフェッショナル向け】Windows APIを用いた「ファイル使用中」の高度な判定と待機キューの実装 – AutoCAD VBA解析バイブル

スポンサーリンク

【AutoCAD VBA】共有ファイルロックの「その先」へ。Windows APIによる堅牢な排他制御と待機キューの実装

AutoCADのバッチ処理において、最も避けるべきは「ファイルが開けずにスクリプトが停止する」ことではない。「ファイルロックによる競合をハンドリングできず、異常終了したプロセスが図面をゾンビ化させること」だ。

多くの開発者が`Dir()`関数や単純なエラーハンドラで済ませているが、それは「プロ」の仕事ではない。ネットワーク経由でのアクセスが常態化する現代の設計現場において、ファイルオープンは常に不確定要素を孕んでいる。

今日は、AutoCAD VBAの限界をWindows APIで突破し、実務で絶対に壊れない「待機キュー付きファイルオープン」の実装論を伝授する。

—

1. なぜ「標準のVBA」では不十分なのか

VBAの`Workbooks.Open`や`Application.Documents.Open`は、ファイルがロックされていると即座にランタイムエラーを吐く。

多くの初心者はここで`On Error Resume Next`を多用するが、これは悪手だ。

  • リソースのリーク: エラーを無視してリトライを繰り返すと、AutoCADの内部オブジェクトが解放されずにメモリを圧迫し続ける。
  • 状態の不確実性: 「開けなかった」のか「権限がない」のか「ファイルが破損している」のか、エラーコードの判定を怠ると、無限ループの沼にハマる。

我々が求めるのは、Windows OSレベルでファイルハンドルを取得し、排他状態を正確に検知する「外科手術的なアプローチ」である。

—

2. 実装の核心:CreateFile APIによる「事前検知」

`CreateFile`関数を呼び出し、ファイルへのアクセス権を試行する。これにより、VBAがAutoCADに指示を出す前に「今、そのファイルに触れるか?」を判定できる。

プロダクションコード:RobustFileOpener

以下のモジュールは、指定したDWGに対して排他制御を行い、ロックが解除されるまで指数バックオフ(リトライ間隔を徐々に広げる手法)で待機する。

Option Explicit

‘ Windows API定義
Private Declare PtrSafe Function CreateFile Lib “kernel32” Alias “CreateFileA” ( _
ByVal lpFileName As String, ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long

Private Declare PtrSafe Function CloseHandle Lib “kernel32″ (ByVal hObject As Long) As Long

Private Const GENERIC_READ = &H80000000
Private Const FILE_SHARE_READ = &H1
Private Const OPEN_EXISTING = 3
Private Const INVALID_HANDLE_VALUE = -1

”’

”’ ファイルが他プロセスにロックされていないかを確認する
”’

Public Function IsFileAvailable(ByVal filePath As String) As Boolean
Dim hFile As Long
hFile = CreateFile(filePath, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)

If hFile <> INVALID_HANDLE_VALUE Then
CloseHandle hFile
IsFileAvailable = True
Else
IsFileAvailable = False
End If
End Function

”’

”’ リトライロジック付きのファイルオープン
”’

Public Function OpenDrawingRobust(ByVal filePath As String, ByVal maxRetries As Integer) As AcadDocument
Dim i As Integer
Dim delay As Double

For i = 1 To maxRetries
If IsFileAvailable(filePath) Then
Set OpenDrawingRobust = Application.Documents.Open(filePath)
Exit Function
End If

‘ 指数バックオフ待機 (1秒, 2秒, 4秒…)
delay = 2 ^ (i – 1)
Debug.Print “ファイルロック中: ” & filePath & ” 再試行まで ” & delay & ” 秒待機…”
Application.Wait Now + TimeSerial(0, 0, delay)
Next i

Err.Raise vbObjectError + 1001, “OpenDrawingRobust”, “最大試行回数を超過しました。ファイルが占有されています。”
End Function

—

3. 設計上の極意:なぜこの構成なのか

① 指数バックオフ(Exponential Backoff)の採用

単純な`Sleep 1000`のループは、サーバー負荷を不必要に高める。待機時間を倍々に増やすことで、他のバッチ処理やユーザー操作との競合を緩和し、システム全体の安定性を高めている。

② ファイルハンドルの即時解放

`CreateFile`で得たハンドルは、判定が終われば即座に`CloseHandle`で閉じる。これを忘れると、自分自身がロックの犯人になるという笑えない事態を招く。

③ データベース連携時の注意点

もしこのDWGオープン後にSQL Server等へ情報を書き込む場合、「ファイルのロック判定」と「DBトランザクション」の順序を厳密に制御せよ。
ファイルを開く前にDB側のレコードをロックし、失敗時にDBをロールバックする「二段階コミット」の思想をVBAレベルで実装することが、データ不整合を防ぐ唯一の道だ。

—

4. チーフアーキテクトからの助言

大規模なバッチ処理を組む際、最も恐れるべきは「想定外の例外」ではない。「想定内の排他エラーを放置すること」だ。

  • ログを残せ: どのファイルで何回リトライしたか。CSV等に書き出しておくことは、後々のトラブルシューティングで命を救う。
  • ゾンビプロセスの監視: `IsFileAvailable`が長期間Falseを返す場合、別のAutoCADインスタンスがクラッシュしている可能性がある。タスクマネージャーの`acmap.exe`等を監視するサブ処理を組み込むのも一つの手だ。

VBAはレガシーと言われることもあるが、APIを叩く力を手に入れた瞬間、それは最強の自動化ツールに化ける。堅牢性を担保し、自分自身の手を離れても動き続ける「自動化のインフラ」を構築してほしい。

健闘を祈る。

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