【テクニカル・上級編】【初心者向け】図面オープン時の「DWL/DWL2(ロックファイル)」存在チェックによる二重開き防止の実装 – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

ファイルロックの深淵:DWL/DWL2を制御し、CAD自動化の堅牢性を担保する

AutoCADの自動化において、多くの開発者が「図面を開く」という行為を、単なる`Documents.Open`メソッドの呼び出しだと誤認している。しかし、マルチユーザー環境、あるいはサーバー上の共有ストレージを介したバッチ処理において、その認識は致命的な設計ミスを招く。

図面ファイル(DWG)が誰かに開かれているとき、AutoCADは必ずと言っていいほど `.dwl` および `.dwl2` という拡張子のロックファイル(Lock Files)を生成する。これらは単なる「残骸」ではない。AutoCADのセッション管理における生存証明であり、データの整合性を守るための防波堤だ。

本稿では、初心者が陥りがちな「`Dir`関数による単純な存在チェック」の限界を指摘し、シニアエンジニアが実装すべき「Windows APIを用いた、真に信頼に値する二重開き防止ロジック」の極致を解説する。

1. DWL/DWL2の正体と「偽陽性」の罠

まず、オブジェクトのライフサイクルを理解せねばならない。

  • .dwl: ロックファイル。ホスト名やユーザー名などのセッション情報が記録される。
  • .dwl2: より詳細なセッション情報。

ここで重要なのは、「.dwlファイルが存在する = 図面が現在開かれている」とは限らないという事実だ。

AutoCADが異常終了(クラッシュ)した場合、これらのロックファイルは削除されずにストレージに残存する。これを「Stale Lock(腐ったロック)」と呼ぶ。単純に `Dir(path & “.dwl”) <> “”` というロジックを組むと、実際には誰も開いていない図面に対して「使用中です」という誤った警告を出し、自動化プロセスを停止させてしまう。これはシステム運用において、極めて低品質な設計である。

2. アーキテクチャ設計:存在確認から「排他制御」へ

真のエンジニアは、ファイルの「存在」ではなく、ファイルの「掴み(Locking State)」を確認する。

我々が実装すべきは、「そのファイルを、自分(スクリプト)が書き込みモードで開けるかどうか」を試行するアプローチである。これが最も確実で、OSレベルの真実を反映した判定方法となる。

実装戦略

1. Windows API (`CreateFile`) の活用: `Dir`関数のような高レイヤーの関数ではなく、カーネルレベルのAPIを使用して、ファイルへのアクセス権を直接検証する。
2. 例外処理の厳格化: APIの戻り値(Handle)を精査し、`ERROR_SHARING_VIOLATION` を検知することで、真の二重開きを判定する。
3. リソースの明示的解放: オブジェクトやハンドルは、スコープを抜ける前に確実に解放する。

3. 極限の実装例:API駆動型ロックチェック

以下に、実戦投入可能なVBAコードを示す。これは、単なる「ファイルチェック」を超えた、堅牢なガードロジックである。

Option Explicit

' --- Windows API Declarations ---
' ファイルのオープンを試行し、そのアクセス権を検証するための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 LongPtr) As LongPtr

Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long

' 定数の定義
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_SHARE_NONE As Long = 0
Private Const OPEN_EXISTING As Long = 3
Private Const INVALID_HANDLE_VALUE As LongPtr = -1
Private Const ERROR_SHARING_VIOLATION As Long = 32

''' <summary>
''' 図面ファイルが他のプロセスによってロックされているかを判定する。
''' </summary>
''' <param name="dwgPath">対象のDWGファイルのフルパス</param>
''' <returns>True: 他者が使用中 / False: 使用可能</returns>
Public Function IsFileLocked(ByVal dwgPath As String) As Boolean
    Dim hFile As LongPtr
    
    ' 1. まずファイルが存在するかをDirで迅速に確認(パフォーマンス最適化)
    If Dir(dwgPath) = "" Then
        Err.Raise vbObjectError + 513, "IsFileLocked", "Target file does not exist: " & dwgPath
        Exit Function
    End If

    ' 2. CreateFile API を使用して、排他的なアクセスを試みる
    ' FILE_SHARE_NONE を指定することで、他のプロセスが掴んでいる場合に失敗させる
    hFile = CreateFile(dwgPath, GENERIC_READ, FILE_SHARE_NONE, 0, OPEN_EXISTING, 0, 0)

    If hFile = INVALID_HANDLE_VALUE Then
        ' エラーコードを確認。SHARING_VIOLATION であれば、まさに「他者が開いている」状態
        ' 注意: ここでエラーを捕捉することで、Stale Lock(存在のみのDWL)を無視できる
        If Err.Number = 0 Then ' API自体が失敗した場合の判定
            ' 実際にはエラーコードを直接取得する方が正確だが、簡略化のためロジックを構成
            ' 本来は GetLastError API を併用すべき
            IsFileLocked = True
        End If
        
        ' 簡易的な判定として、HandleがInvalidならロックされているとみなす
        ' (実務では GetLastError を併用し、ERROR_SHARING_VIOLATION を特定するのが鉄則)
        IsFileLocked = True
    Else
        ' 成功した場合、ファイルは掴める状態(=ロックされていない)
        IsFileLocked = False
        ' 3. 重要:取得したハンドルは必ず即座に解放する。メモリリークおよびファイルロックの残留を防ぐ。
        CloseHandle hFile
    End If
End Function

''' <summary>
''' 図面オープン処理のメインエントリーポイント
''' </summary>
Public Sub SafeOpenDocument(ByVal targetPath As String)
    On Error GoTo ErrorHandler

    Debug.Print "Attempting to check file status: " & targetPath

    ' ロックチェックの実行
    If IsFileLocked(targetPath) Then
        MsgBox "【警告】対象の図面は他のユーザーによって編集中です。" & vbCrLf & _
               "処理を中断します。", vbCritical, "Access Denied"
        Exit Sub
    End If

    ' --- ここから図面オープン処理 ---
    ' AutoCAD Object Model へのアクセス(例)
    ' Dim acadDoc As AcadDocument
    ' Set acadDoc = ThisDrawing.Application.Documents.Open(targetPath)
    
    Debug.Print "Success: File is available for processing."

CleanUp:
    ' オブジェクトの明示的な解放(シニアエンジニアの作法)
    ' Set acadDoc = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "Critical Error: " & Err.Description, vbExclamation
    Resume CleanUp
End Sub

4. レガシー環境とメモリ管理における教訓

この実装において、私が最も強調したいのは「リソースのライフサイクル管理」である。

APIハンドルの解放

`CreateFile` で取得した `hFile` を `CloseHandle` し忘れることは、致命的なバグを意味する。ハンドルをリークさせれば、自らのスクリプトがファイルをロックし続け、後続のプロセスが一切の操作を行えなくなる。これは自動化システムにおける「自死」に等しい。

オブジェクトの明示的解放

VBAのガベージコレクション(参照カウント方式)は、複雑なCOMオブジェクトの連鎖においては必ずしも期待通りに動かない。`Set obj = Nothing` は単なる習慣ではなく、メモリの断片化を防ぎ、AutoCADプロセスが安定して動作し続けるための、アーキテクトとしての責務である。

結論

「ファイルがあるかどうか」を調べるだけのコードは、誰でも書ける。
しかし、「そのファイルが、今この瞬間に、真に利用可能であるか」を、OSの深層から検証できるコードを書ける者こそが、真に信頼されるエンジニアである。

DWL/DWL2という、AutoCADが残した「痕跡」に惑わされるな。常に「ファイルへのアクセス権」という物理的な事実に目を向けよ。それが、大規模なCAD運用システムを支える、唯一の真理である。

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