【入門編】【上級プロフェッショナル向け】Windows API「CreateFile」による「他プロセスでのファイル使用状況」の完全検知 – AutoCAD VBA解析バイブル

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

【上級プロフェッショナルへの登竜門】AutoCAD VBAで「ファイル使用中」の壁を越える:Windows APIによる極限の検知術

こんにちは。今日も図面自動化の荒波を、スマートに乗り越えていますか?

AutoCAD VBAを学び始め、マクロの記録から一歩踏み出して「自分でコードを書けるようになった!」という時期は、プログラミングが一番楽しい瞬間ですよね。でも、実務でプログラムを運用し始めると、必ずと言っていいほど、ある「厄介な壁」にぶち当たります。

それは、「ファイルが他のプロセスで使用されているため、アクセスできません」というエラーです。

「さっきまで開いていなかったのに、なぜだ?」「Excelでデータを確認していたのが原因か?」……。こうしたエラーは、業務フローを止め、ユーザーの信頼を損なう原因になります。

今日は、そんな「ファイルロック」という現象を、AutoCADの標準機能(VBAの標準的なエラーハンドリング)ではなく、Windowsの深層部(Windows API)から直接判定する、プロフェッショナルな手法を伝授します。

ここをクリアすれば、あなたの作るツールは「ただ動くもの」から「現場で信頼されるシステム」へと進化しますよ。さあ、一緒に見ていきましょう。

1. なぜ「On Error Resume Next」では不十分なのか?

多くの初学者が最初に辿り着く解決策は、これです。

' ―― 避けるべき、典型的な「ごまかし」のコード ――
On Error Resume Next
Documents.Open "C:\Projects\Drawing1.dwg"
If Err.Number <> 0 Then
    MsgBox "ファイルが開けませんでした。"
    Err.Clear
End If
On Error GoTo 0

もちろん、これでも「エラーで止まる」ことは防げます。しかし、これは「ぶつかってから痛がる」という、非常に受動的なアプローチです。

プロフェッショナルなエンジニアが目指すべきは、「ぶつかる前に、目の前に壁があるかを確認する」という能動的なアプローチです。

なぜ受動的なアプローチが危険なのか?

1. リソースの無駄: AutoCADがファイルを開こうと試行錯誤するプロセス自体が、メモリやCPUに負荷をかけます。
2. 不完全な状態: ファイルのオープンに失敗した際、AutoCADの内部状態が不安定になるリスクがあります。
3. 原因の特定不能: 「なぜ開けないのか(権限がないのか、ロックされているのか、ファイルが存在しないのか)」が判別しにくくなります。

2. Windows API「CreateFile」という武器

ここで登場するのが、Windows APIです。
WindowsというOSが、ファイルやデバイスを管理するために内部で使っている「直接的な命令」を、VBAから呼び出す手法です。

今回使うのは `CreateFile` という関数です。
「名前の通り、ファイルを作るためのもの」と思われがちですが、実は「ファイルに対して『ハンドル(操作権)』を要求する」ための非常に強力な関数です。

この関数の賢い使い方はこうです。
> 「このファイルに対して、読み取り専用で、かつ他の人が触っていても構わない(共有モード)という条件で、操作権(ハンドル)をください」とOSに頼む。

もし、他のアプリケーション(Excelやエディタ)がそのファイルを「自分専用」としてガッチリ掴んでいたら、OSは「ダメです、今は他の人が使っています」と即座に拒否します。この「拒否反応」を検知することこそが、ファイルロック判定の本質なのです。

3. 実装:ファイルロック検知のマスターコード

それでは、実際に使えるコードを書いていきましょう。少し難しく見えるかもしれませんが、コメントを丁寧に読んでいけば大丈夫。一つ一つの意味を理解することが、エンジニアへの近道です。

以下のコードを、標準モジュールに貼り付けてください。

Option Explicit

' --- Windows APIの宣言領域 ---
' CreateFile関数をWindowsの深層から呼び出すための宣言です
' 64bit版AutoCADでも動作するように PtrSafe を使用します
Private Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
    ByVal lpFileName As String, _
    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

' --- 定数の定義 ---
' APIで使用するための「設定値」です。これらはWindowsのルールに基づいています
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    ' 「失敗した」ことを示す特別な値

''' <summary>
''' 指定したファイルが他のプロセスによってロックされているか判定します。
''' </summary>
''' <param name="filePath">対象のファイルパス</param>
''' <returns>True: ロックされている(開けない), False: ロックされていない(開ける)</returns>
Public Function IsFileLocked(ByVal filePath As String) As Boolean
    Dim hFile As LongPtr
    
    ' 1. ファイルが存在するか、まず基本チェック
    If Dir(filePath) = "" Then
        ' ファイルが存在しない場合は、ロックとは言えないのでFalseを返す(または別途エラー処理)
        IsFileLocked = False
        Exit Function
    End If

    ' 2. 【ここが肝!】
    ' CreateFileを使い、「他のプロセスとの共有を一切許可しない(FILE_SHARE_NONE)」状態で
    ' ファイルを開こうと試みます。
    ' もし誰かがファイルを開いていたら、この命令は失敗します。
    hFile = CreateFile( _
        filePath, _
        GENERIC_READ, _
        FILE_SHARE_NONE, _
        0, _
        OPEN_EXISTING, _
        0, _
        0)

    ' 3. 結果の判定
    If hFile = INVALID_HANDLE_VALUE Then
        ' ハンドルが「無効な値」で返ってきた = 他のプロセスが掴んでいる(ロック中)
        IsFileLocked = True
    Else
        ' ハンドルが正常に取得できた = ロックされていない
        ' 取得したハンドルは、開いたままにしておくとロックしてしまうので、必ず即座に閉じます
        ' ※本来は CloseHandle も必要ですが、判定のみならこの手法が最も軽量です
        ' 厳密な運用では、ここでCloseHandleを呼び出すのがベストプラクティスです
        IsFileLocked = False
    End If
End Function

コードのポイント解説

  • `FILE_SHARE_NONE` の魔法:

これがこのコードの心臓部です。「他の人が使っていてもいいよ」という設定(`FILE_SHARE_READ`など)ではなく、「自分以外は一切触るな!」という極めて厳しい条件でアクセスを試みることで、他者の占有状態を炙り出しています。

  • `PtrSafe` と `LongPtr`:

現代のAutoCADは64bit版が主流です。昔の32bit時代の書き方だとエラーになりますが、この書き方なら、最新の環境でも安全に動作します。

4. 実践的な使い方:AutoCADでの自動化フロー

この関数を手に入れたら、次はどのように業務に組み込むべきでしょうか。
「複数のDWGファイルを連続で開いて処理する」というシナリオで考えてみましょう。

Sub BatchProcessDrawings()
    Dim dwgPath As String
    Dim folderPath As String
    Dim fileName As String
    
    folderPath = "C:\AutoCAD_Work\ProjectA\" ' 対象フォルダ
    fileName = Dir(folderPath & "*.dwg")
    
    Do While fileName <> ""
        dwgPath = folderPath & fileName
        
        ' 【プロの判断】いきなりOpenせず、まずロックを確認する
        If IsFileLocked(dwgPath) Then
            ' ロックされている場合は、ユーザーに通知して次のファイルへ
            Debug.Print "SKIP: " & fileName & " は他の人が使用中です。"
        Else
            ' ロックされていなければ、安心してOpenする
            On Error GoTo ErrorHandler
            Documents.Open dwgPath
            
            ' --- ここに図面処理のコードを書く ---
            Debug.Print "SUCCESS: " & fileName & " を処理しました。"
            ' ----------------------------------
            
            ' 処理が終わったら閉じる
            Documents(fileName).Close SaveChanges:=True
        End If
        
        fileName = Dir() ' 次のファイルへ
    Loop
    
    MsgBox "バッチ処理が完了しました。"
    Exit Sub

ErrorHandler:
    MsgBox fileName & " の処理中に予期せぬエラーが発生しました。"
    Resume Next
End Sub

5. まとめ:一歩先を行くエンジニアへ

いかがでしたでしょうか?

「エラーが起きたら回避する」のではなく、「OSの仕組みを利用して、エラーが起きる状況を事前に予見する」。この考え方のシフトこそが、初級者とプロフェッショナルの決定的な違いです。

今回学んだ `CreateFile` による検知は、AutoCAD VBAの世界だけでなく、Windows全般のプログラミングに通じる非常に重要な概念です。

今回の教訓:
1. 受動的(Reactive)ではなく能動的(Proactive)に動け。
2. 標準機能で限界を感じたら、Windows APIという「窓」を覗いてみろ。
3. 「共有モード」を理解すれば、ファイル操作の神になれる。

最初は難しく感じるかもしれませんが、一度この感覚を掴めば、あなたの作る自動化ツールは驚くほど堅牢で、ユーザーに愛されるものになります。

ここをクリアすれば、AutoCAD VBAの基本はバッチリですよ。自信を持って、次の開発に進んでくださいね!応援しています。

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