【入門編】【上級プロフェッショナル向け】OneDrive/SharePoint上の「同期競合」を回避する、保存時のファイルロック状態監視アルゴリズム – AutoCAD VBA解析バイブル

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

「アクセス拒否」の壁を越えて。OneDrive時代のAutoCAD VBA:同期競合を回避する究極のファイル制御術

こんにちは。現場で数々の自動化ツールを構築してきた、あなたの先輩エンジニアです。

「マクロの記録」から一歩踏み出し、自分でコードを書き始めた皆さん。素晴らしい挑戦ですね。しかし、AutoCAD VBAの世界には、初心者からベテランまでを等しく悩ませる「見えない敵」が存在します。

それが、OneDriveやSharePointといったクラウドストレージとの「同期競合」です。

「昨日まで動いていたマクロが、今日に限って『アクセスが拒否されました(Error 70)』と怒られる……」
「保存処理の途中で、突然AutoCADが止まってしまう……」

もしあなたがそんな経験をしているなら、それはあなたのコードが悪いのではありません。「ファイルが今、OS(Windows)やクラウドによって掴まれている(ロックされている)」という、現代特有の状況を想定できていないだけなのです。

今日は、その壁を打ち破るための「プロフェッショナルな待ち(リトライ)アルゴリズム」を伝授します。これをマスターすれば、あなたの作るツールは「現場で本当に使える、信頼性の高い道具」へと進化します。

1. なぜクラウド環境で「保存失敗」が起きるのか?

まず、敵の正体を理解しましょう。図解的に説明すると、以下のような「すれ違い」が起きています。

1. あなた(AutoCAD): 「よし、図面を保存するぞ!」 $\rightarrow$ *ファイルを書き込みモードで開こうとする*
2. OneDrive(同期エンジン): 「おっ、ファイルが更新されたな! すぐにクラウドへアップロードするぞ!」 $\rightarrow$ *ファイルを読み取り/同期モードでロックする*
3. あなた(AutoCAD): 「あれ? ファイルが開けない! アクセス拒否だ!」 $\rightarrow$ エラー発生!

このように、AutoCADがファイルを操作しようとした瞬間に、OneDriveが「同期のためにファイルを掴んでしまっている」のが原因です。これをファイルロック(File Lock)と呼びます。

プロのエンジニアは、この「すれ違い」を前提に設計します。「一度失敗したら終わり」ではなく、「相手が手を離すまで、賢く待つ」。これが極限の知見です。

2. Windows APIという「強力な武器」を手に入れる

VBA標準の機能(FileSystemObjectなど)でもファイルの状態は分かりますが、クラウドの同期ロックを正確に、かつ高速に検知するには、Windows APIを直接叩くのが最も確実で、最もパフォーマンスが高い方法です。

今回は、Windowsの深層部にアクセスして「このファイルは今、他のプロセスに使われているか?」を判定する関数を実装します。

実装するアルゴリズムの設計図

1. 判定フェーズ: Windows API(`CreateFile`)を使い、ファイルに「独占的なアクセス」を試みる。
2. 判定結果:

  • 成功 $\rightarrow$ ロックされていない! $\rightarrow$ 保存実行へ。
  • 失敗 $\rightarrow$ ロックされている! $\rightarrow$ 「少し待機してリトライ」へ。

3. リトライフェーズ: `Sleep`関数で数ミリ秒〜数秒休み、再度判定。
4. タイムアウト: 無限に待つわけにはいかないので、一定回数失敗したらエラーとして終了する。

3. 【実践】最強のファイルロック監視コード

さあ、ここからが本番です。以下のコードは、そのまま実務のモジュールに組み込めるレベルの設計にしています。

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 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

' 指定した時間(ミリ秒)だけ処理を一時停止させるために必要です
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

' 定数の定義(APIで使用)
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>
Function IsFileLocked(ByVal filePath As String) As Boolean
    Dim hFile As LongPtr
    
    ' CreateFileを使用して、ファイルに「独占的なアクセス権」を要求してみる
    ' 他のプロセス(OneDrive等)が掴んでいると、ここで失敗して INVALID_HANDLE_VALUE が返る
    hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_NONE, 0, OPEN_EXISTING, 0, 0)
    
    If hFile = INVALID_HANDLE_VALUE Then
        ' 失敗した = 誰かが使っている(ロックされている)
        IsFileLocked = True
    Else
        ' 成功した = 独占的に開けた = ロックされていない
        CloseHandle hFile
        IsFileLocked = False
    End If
End Function

''' <summary>
''' クラウド同期によるロックを回避しながら、安全に保存を行うプロフェッショナル版保存関数
''' </summary>
''' <param name="doc">対象のAutoCADドキュメント</param>
''' <param name="savePath">保存先のフルパス</param>
Sub SafeSaveAs(ByVal doc As AcadDocument, ByVal savePath As String)
    Dim retryCount As Integer
    Const MAX_RETRIES As Integer = 10 ' 最大10回リトライ
    Const SLEEP_INTERVAL As Long = 1000 ' 待機時間(1000ms = 1秒)
    
    On Error GoTo ErrorHandler
    
    Debug.Print "保存プロセス開始: " & savePath

    ' --- リトライループの開始 ---
    Do
        If Not IsFileLocked(savePath) Then
            ' ロックされていないことが確認できたら、保存を実行
            doc.SaveAs savePath
            Debug.Print "保存に成功しました。"
            Exit Sub
        Else
            ' ロックされている場合
            retryCount = retryCount + 1
            Debug.Print "ファイルがロックされています。リトライ中... (" & retryCount & "/" & MAX_RETRIES & ")"
            
            If retryCount >= MAX_RETRIES Then
                Err.Raise vbObjectError + 513, "SafeSave", "同期エラー:ファイルが長時間ロックされています。手動で確認してください。"
            End If
            
            ' 次のリトライまで少し待つ(ここでOneDriveの同期完了を待つ)
            Sleep SLEEP_INTERVAL
        End If
    Loop
    ' --- リトライループ終了 ---

ErrorHandler:
    If Err.Number <> 0 Then
        MsgBox "【致命的なエラー】" & vbCrLf & Err.Description, vbCritical, "保存失敗"
    End If
End Sub

''' <summary>
''' テスト用メインルーチン
''' </summary>
Sub TestSafeSave()
    Dim targetDoc As AcadDocument
    Set targetDoc = ThisDrawing ' 現在開いている図面
    
    Dim myPath As String
    myPath = "C:\Users\YourName\OneDrive\Documents\Test_Project.dwg" ' ※実際のパスに変更してください
    
    ' 安全な保存を実行
    SafeSaveAs targetDoc, myPath
End Sub

4. コードの解説:ここをクリアすれば、VBAの基本はバッチリ!

初心者の方が「コピペして終わり」にならないよう、このコードの心臓部を噛み砕いて説明しますね。

① Windows API (`CreateFile`) の役割

通常のVBAでは `Open “file” For Input As #1` のような書き方をしますが、これは「中身を読む」ための命令です。
一方、今回の `CreateFile` は、「そのファイルを独占的に支配できるか?」をOSに直接聞きに行く、非常に強力な命令です。もしOneDriveがファイルを使っていれば、OSは「ダメだよ(Invalid Handle)」と答えます。この「ダメだよ」という返事を受け取ることが、ロック検知の鍵です。

② リトライループと `Sleep`

ここが「プロの設計」です。
エラーが出た瞬間にプログラムを終了させるのではなく、`Do…Loop` を使って「もう一度試す」というループを作っています。
そして、`Sleep` を入れるのが重要です。猛烈な勢いでリトライを繰り返すと、PCのCPUに負荷がかかり、かえって状況を悪化させます。「1秒休んで、また聞く」。この余裕が、クラウド同期との「すれ違い」を解消する魔法になります。

③ タイムアウト(MAX_RETRIES)

「いつまでも待ち続ける」のは、ユーザーにとって「フリーズした」ように見えてしまいます。
「10回試してもダメなら、諦めてエラーを出す」という期限を決めておく。これが、ユーザーに不快感を与えないための「おもてなし」の設計です。

5. 陥りやすいエラーと回避策

この技術を導入する際に、以下の点に注意してください。

  • パスの指定ミス: `IsFileLocked` は、存在しないパスを渡すとエラーになります。事前に `Dir(filePath) <> “”` でファイルの存在確認をすることをお勧めします。
  • 権限不足: ネットワークドライブや、管理者権限が必要なフォルダに保存しようとすると、ロックとは別の理由で `CreateFile` が失敗します。
  • 無限ループの恐怖: `MAX_RETRIES` を設定し忘れると、OneDriveが同期に失敗し続けている場合、AutoCADが永遠に動きません。必ず「出口」を作りましょう。

まとめ:一歩先のエンジニアへ

いかがでしたか?
単に「保存する」という命令の裏側には、OSやクラウドといった、目に見えないプロセスの動きが複雑に絡み合っています。

「マクロの記録」は、いわば「決まった動作をなぞるだけ」のものです。
しかし、今回のように「もしこうなったら、こう動く」という例外処理(エラーハンドリング)を組み込むことが、真のプログラミングです。

このコードを自分のツールに組み込んでみてください。きっと、あなたの作るツールは、同僚やクライアントから「このマクロ、すごく安定しているね!」と信頼されるようになるはずですよ。

応援しています。次はもっと深い、オブジェクトのライフサイクル管理についてお話ししましょう!

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