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

スポンサーリンク

AutoCAD VBAの深淵:共有ファイルロックを完全制圧するAPI実装術

AutoCADのバッチ処理において、最も避けるべきは「ファイル使用中(File in Use)」による実行時エラーの放置だ。VBA標準の`Open`メソッドの例外処理で凌ごうとするのは素人のやり方である。

大規模な設計現場では、同一ファイルを複数のプロセスやユーザーが参照する。我々が構築すべきは、OSレベルの排他制御を透過的に監視し、リソースが解放されるまで静かに待機する「堅牢な待機キュー」だ。

本稿では、Windows APIを直接叩き、AutoCADのプロセスを汚さずにファイルロックを検知する極限の手法を伝授する。

—

1. なぜVBA標準の「エラー回避」では不十分なのか

`On Error Resume Next` で囲んで `Workbooks.Open` をリトライする手法は、単に「運任せ」にしているに過ぎない。

  • カーネルレベルの競合: Windowsのファイルシステムは、別のプロセスがファイルを開いている際、`CreateFile`関数を通じてアクセス拒否を返す。
  • メモリリークの温床: 中途半端なエラーハンドリングは、AutoCADのCOMオブジェクトの解放を妨げ、ゾンビプロセスを生成する。
  • 排他制御の不確実性: ネットワークドライブ経由の場合、OSのキャッシュ更新と実際のロック解除には数ミリ秒のラグがある。

これらを解決するには、Windows APIの `CreateFile` を直接呼び出し、排他属性 `FILE_SHARE_READ | FILE_SHARE_WRITE` を考慮した「接続テスト」を実装する必要がある。

—

2. Windows APIによるファイルロック判定の実装

以下のコードは、ファイルが「書き込み可能な状態でオープン可能か」を、AutoCAD本体を動かす前に検証するアーキテクチャだ。

‘ 必要なAPI宣言(標準モジュールへ記述)
If Win64 Then
Private Declare PtrSafe Function CreateFile Lib “kernel32” Alias “CreateFileW” (ByVal lpFileName As LongPtr, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As LongPtr, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib “kernel32” (ByVal hObject As LongPtr) As Long
Else
Private Declare Function CreateFile Lib “kernel32” Alias “CreateFileW” (ByVal lpFileName As Long, 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 Function CloseHandle Lib “kernel32” (ByVal hObject As Long) As Long
End If

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

‘ ファイルがロックされているか判定する関数
Public Function IsFileLocked(ByVal filePath As String) As Boolean
Dim hFile As LongPtr

‘ APIでファイルハンドルを取得試行
hFile = CreateFile(StrPtr(filePath), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)

If hFile = INVALID_HANDLE_VALUE Then
IsFileLocked = True ‘ ロックされている
Else
CloseHandle hFile
IsFileLocked = False
End If
End Function

—

3. 堅牢な待機キュー(バックオフアルゴリズム)の実装

単純なループリトライはCPUを占有し、システム全体のパフォーマンスを著しく低下させる。指数関数的に待機時間を増やす「指数バックオフ」を採用し、システムに負荷をかけずに確実に待機する。

Public Sub SafeOpenDrawing(dwgPath As String)
Dim retryCount As Integer: retryCount = 0
Dim maxRetries As Integer: maxRetries = 10
Dim waitTime As Long: waitTime = 1000 ‘ 初期待機1秒

Do While IsFileLocked(dwgPath)
If retryCount >= maxRetries Then
Err.Raise vbObjectError + 1000, “SafeOpen”, “ファイルが長時間ロックされています: ” & dwgPath
End If

‘ 指定時間待機
Sleep waitTime

‘ 待機時間を倍増(指数バックオフ)
waitTime = waitTime 2
retryCount = retryCount + 1
Loop

‘ ここで初めてAutoCADのOpenメソッドを実行
ThisDrawing.Application.Documents.Open dwgPath
End Sub

—

4. チーフアーキテクトからの助言:メモリとライフサイクル

VBA開発で最も見落とされがちなのが、`Documents.Open` で生成されたオブジェクトの管理だ。

1. 明示的なオブジェクト解放: `Set doc = Nothing` は必須。特にループ内で大量の図面を処理する場合、AutoCADのCOMサーバー上に残存する参照カウントは、メモリリークを招き、最終的にAutoCAD本体をクラッシュさせる。
2. `ThisDrawing` の過信を捨てる: バッチ処理では、`Application.Documents` を明示的に扱い、現在の図面コンテキストと処理対象の図面コンテキストを厳格に分離せよ。
3. レガシー環境の罠: DXFの読み込み時には、必ず `Application.Preferences.Files.AutoSavePath` などを一時的に書き換える必要がある。APIでロックを確認するだけでなく、AutoCAD自体の設定状態を「クリーン」に保つラッパーを構築することが、真のエンジニアの責務である。

結論

AutoCAD VBAは古臭い言語ではない。OSの深層部まで理解し、APIを駆使する者にとって、それは今なお最強の自動化ツールである。

ファイルロックという「壁」に突き当たったとき、エラーを恐れて回避するのではなく、Windowsの構造を味方につけ、制御下に置く。その姿勢こそが、あなたのシステムを「動くもの」から「止まらない基盤」へと昇華させる唯一の道である。

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