エラーで止まるマクロは卒業。Windows APIとDWL解析で実現する「プロ仕様」の図面オープン制御
「あ、またファイルがロックされてる……」
自動化ツールを運用していると、必ずと言っていいほど直面する壁があります。それは、「誰かがその図面を開いているために、プログラムがエラーで止まってしまう」という問題です。
多くの初学者は、ここで `On Error Resume Next` を使い、エラーを無理やり無視して進もうとします。しかし、それはエンジニアとして「もっとも避けるべき禁忌」の一つです。エラーを無視すれば、データの上書き失敗や、不完全な図面状態での処理といった、取り返しのつかない事故を招くからです。
今日は、マクロの記録レベルを脱却し、「誰が使っているか」を特定し、「解放されるまでスマートに待機する」という、実務レベルのシステム設計(アーキテクチャ)の考え方を伝授します。ここをマスターすれば、あなたの作るツールは「単なるマクロ」から「信頼される業務アプリケーション」へと進化します。
1. なぜ「エラーが出る」だけで終わってはいけないのか?
プロフェッショナルな自動化エンジニアは、エラーを「異常事態」としてではなく、「制御すべき状態(ステート)」として捉えます。
図面がロックされているとき、ユーザーが求めているのは「エラーメッセージ」ではなく、「いつ開けるのか?」、あるいは「誰に声をかければいいのか?」という情報です。
プロが設計する「高度なオープン制御」の3ステップ
1. 検知 (Detection): ファイルがロックされているか、誰が持っているかを特定する。
2. 待機 (Waiting): CPUに負荷をかけず、スマートにリトライ(再試行)を行う。
3. 通知 (Notification): ユーザーに状況を伝え、キャンセル手段も提供する。
2. AutoCADの秘密:`.dwl` ファイルを読み解く
AutoCADは図面を開くと、同じフォルダ内に `.dwl` または `.dwl2` という拡張子の小さなファイルを作成します。これが「ロックファイル」です。
実は、このファイルの中には「現在その図面を開いているユーザー名」がテキストとして書き込まれています。Windows APIを駆使して複雑なハンドル操作を行う必要はありません。この「AutoCADの仕様」を逆手に取るのが、最もスマートで軽量なアプローチです。
3. 実装:オープン待機キューの構築
では、実際に「誰が使っているか」を特定し、指定回数リトライするコードを書いていきましょう。
ここでは、以下の2つのWindows APIを使用します。
- `Sleep`: CPUを休ませ、PCが固まるのを防ぐため。
- `DoEvents`: プログラム実行中も、ユーザーが「キャンセル」ボタンを押せるようにするため。
Option Explicit
' --- Windows API の宣言 ---
' プログラムの実行を一時停止させるためのAPI
If VBA7 Then
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Else
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
End If
''' <summary>
''' 図面をスマートに開くメインルーチン
''' </summary>
''' <param name="dwgPath">開きたいDWGファイルのフルパス</param>
Sub SmartOpenDWG(dwgPath As String)
Dim maxRetries As Integer: maxRetries = 5 ' 最大試行回数
Dim retryInterval As Long: retryInterval = 5000 ' 試行間隔(5秒)
Dim currentTry As Integer
Dim isOpened As Boolean
Dim lockedUser As String
isOpened = False
For currentTry = 1 To maxRetries
' 1. ファイルが開けるかチェック
If IsFileReady(dwgPath) Then
' ファイルが解放されていればオープン
On Error Resume Next
Documents.Open dwgPath
If Err.Number = 0 Then
isOpened = True
Exit For
Else
MsgBox "図面のオープンに失敗しました: " & Err.Description, vbCritical
Exit Sub
End If
On Error GoTo 0
Else
' 2. ロックされている場合、ユーザー名を特定
lockedUser = GetLockingUser(dwgPath)
If currentTry < maxRetries Then
' ユーザーへの通知(ステータスバーへの表示など)
Debug.Print "試行 " & currentTry & ": " & lockedUser & " が使用中です。待機します..."
' 3. 待機(ここが重要!)
' SleepでCPU負荷を下げ、DoEventsでWindowsのイベント(クリック等)を受け付ける
Dim startTime As Double
startTime = Timer
Do While Timer < startTime + (retryInterval / 1000)
DoEvents ' これがないと、待機中にExcel/AutoCADが「応答なし」になる
Loop
Else
MsgBox "図面が長時間ロックされています。" & vbCrLf & _
"使用中のユーザー: " & lockedUser & vbCrLf & _
"作業を中断します。", vbExclamation, "オープン待機タイムアウト"
End If
End If
Next currentTry
If isOpened Then
Debug.Print "図面のオープンに成功しました。"
End If
End Sub
''' <summary>
''' ファイルが他のプロセスで使用中かどうかを判定する
''' </summary>
Function IsFileReady(filePath As String) As Boolean
Dim fileNum As Integer
Dim errNum As Long
On Error Resume Next
fileNum = FreeFile
' 書き込みモードで開こうとしてみる(これができればロックされていない)
Open filePath For Binary Access Read Write Lock Read Write As #fileNum
Close #fileNum
errNum = Err.Number
On Error GoTo 0
' エラー番号70は「書き込み禁止(使用中)」を指す
If errNum = 0 Then
IsFileReady = True
Else
IsFileReady = False
End If
End Function
''' <summary>
''' .dwlファイルから使用中のユーザー名を抽出する
''' </summary>
Function GetLockingUser(dwgPath As String) As String
Dim dwlPath As String
Dim fileNum As Integer
Dim line As String
Dim userName As String
' .dwl または .dwl2 を探す
dwlPath = Replace(dwgPath, ".dwg", ".dwl")
If Dir(dwlPath) = "" Then dwlPath = Replace(dwgPath, ".dwg", ".dwl2")
If Dir(dwlPath) = "" Then
GetLockingUser = "不明(ロックファイルが見つかりません)"
Exit Function
End If
' ロックファイルの中身を解析
On Error Resume Next
fileNum = FreeFile
Open dwlPath For Input As #fileNum
Do While Not EOF(fileNum)
Line Input #fileNum, line
' DWLファイル内にはユーザー名が含まれている(形式はAutoCADバージョンに依存するが、基本は行末付近)
' ここでは簡易的に行の内容を返す
If InStr(line, " ") > 0 Then
userName = Trim(line)
Exit Do
End If
Loop
Close #fileNum
On Error GoTo 0
If userName <> "" Then
GetLockingUser = userName
Else
GetLockingUser = "不明なユーザー"
End If
End Function
4. エンジニアとしての「こだわり」:ここが設計の肝です
このコードには、単なる「動くコード」を超えた、設計上の意図が3つ込められています。
① `DoEvents` による「ユーザー体験(UX)」の確保
`Sleep` 関数だけを使うと、プログラムが待機している間、AutoCADの画面は真っ白になり、マウス操作も受け付けない「フリーズ状態」になります。`DoEvents` をループ内で回すことで、待機中もユーザーが「キャンセル」ボタンを押したり、他のウィンドウを操作したりできるようになります。これは、「道具としての使いやすさ」を決定づける要素です。
② `IsFileReady` による「先行検知」
いきなり `Documents.Open` を叩いてエラーを待つのではなく、事前に `Open … For Binary Access Read Write` を試みることで、エラーハンドリングのコストを下げ、より確実に「使用中かどうか」を判定しています。
③ ライフサイクルに基づいた `.dwl` 解析
Windowsの高度なAPI(`NtQuerySystemInformation`など)を使えば、全プロセスのハンドルをスキャンしてファイル所有者を特定できますが、これは管理者権限が必要だったり、非常に動作が重かったりします。AutoCADが自ら書き出す `.dwl` を読む手法は、「対象となるソフトウェアの振る舞いを知り尽くした者」だけがたどり着ける、軽量で確実な最適解です。
5. まとめ:次のステップへ
今回学んだことは、単なる「ファイルを開くテクニック」ではありません。
- エラーを予測し、制御する
- OSやアプリケーションの仕組み(仕様)を利用する
- ユーザーを不安にさせない設計をする
これらは、どのようなプログラミング言語においても共通する「設計思想」です。
もし、あなたが作成したツールが「誰かが開いているだけで止まる」のであれば、ぜひこの「待機キュー」の考え方を導入してみてください。それだけで、あなたのマクロの格は、周囲のエンジニアから一目置かれるレベルへと跳ね上がるはずです。
ここをクリアすれば、AutoCAD VBAの基本はバッチリですよ。さあ、もっと複雑な自動化の世界へ踏み出しましょう!
