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

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

OneDrive/SharePoint時代のAutoCAD自動化:同期競合を制する「ファイルロック監視アルゴリズム」の実装

AutoCADの自動化プロセスを設計する際、多くのエンジニアが陥る「見えない罠」がある。それは、コードの論理的欠陥ではなく、「モダンなストレージ環境との物理的な衝突」だ。

かつて、ファイル操作はローカルディスクとの一対一の対話だった。しかし、現代の業務環境はOneDriveやSharePointといったクラウドストレージが標準だ。これらは、ファイルが更新された瞬間にバックグラウンドで「同期プロセス」を走らせ、ファイルハンドルを強奪する。

AutoCADが図面を保存しようとした瞬間に、OneDriveがそのファイルを同期のためにロックしている——。このコンマ数秒のタイミングのズレが、`Permission Denied (Err 70)` や `Path/File access error (Err 75)` を引き起こし、せっかくの自動化ツールを「不安定なゴミ」へと変えてしまうのだ。

本記事では、単なるリトライ処理ではない。Windows APIを駆使し、ファイルの状態を決定論的に監視する、プロフェッショナル仕様の「ファイルロック回避アルゴリズム」を伝授する。

1. なぜ `On Error Resume Next` では足りないのか

初心者は、エラーが出たら `Sleep` させてリトライする、というコードを書く。

' ―― 避けるべき、プロフェッショナルとは呼べない実装 ――
On Error Resume Next
Do
    Err.Clear
    ThisDrawing.SaveAs filePath
    If Err.Number = 0 Then Exit Do
    DoEvents
    Sleep 1000
Loop

この設計がなぜ致命的なのか。理由は2つある。

1. 不確実な待機時間: 1秒で済むのか、1分かかるのかが予測できない。同期が長引けば、無限ループ、あるいはユーザーへの応答なしのフリーズを招く。
2. 不完全な状態判定: `SaveAs` が失敗したとき、ファイルが「書き込み途中」なのか「ロックされているだけ」なのかを判別できていない。最悪の場合、破損したファイルで上書きされるリスクがある。

我々が目指すべきは、「ファイルが物理的に解放されるまで、指数関数的バックオフ(Exponential Backoff)を用いて、スマートに待ち続ける」設計である。

2. 極限の設計思想:ファイルロック監視アルゴリズム

堅牢な設計には、以下の3つのコンポーネントが必要だ。

1. Low-level Lock Checker: Windowsのファイルシステムレベルで、対象ファイルが他のプロセス(OneDrive等)に掴まれていないかを判定する関数。
2. Smart Waiter (Exponential Backoff): 失敗するたびに待機時間を段階的に増やす制御ロジック。これにより、短時間のロックには即座に対応し、長時間の同期にはシステム負荷を抑えて待機できる。
3. Atomic Save Pattern: 直接ファイルを上書きせず、「一時ファイルへの保存 $\to$ 元ファイルの解放確認 $\to$ 置換」というアトミックな操作を行うことで、データ消失を防ぐ。

3. 実装:プロダクション・グレードのコード

以下に、そのまま業務ツールに組み込めるレベルのモジュールを示す。64bit版AutoCAD(VBA環境)を想定し、`PtrSafe` を適用している。

Option Explicit

' Windows 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="targetDoc">保存対象のAutoCADドキュメント</param> ''' <param name="targetPath">保存先フルパス</param> Public Sub SafeSaveDWG(ByRef targetDoc As Object, ByVal targetPath As String) Dim tempPath As String tempPath = targetPath & ".tmp" ' 1. まずは一時ファイルとして保存(元のファイルを汚さない) On Error GoTo ErrorHandler targetDoc.SaveAs tempPath ' 2. 保存した一時ファイルが、同期プロセスによってロックされていないか確認 ' ロックされている間は、指数関数的バックオフで待機する If Not WaitForFileAvailability(tempPath) Then Err.Raise vbObjectError + 513, "SafeSave", "ファイルが長時間ロックされており、解放されませんでした。" End If ' 3. 元のファイルがロックされている可能性があるため、解放を待つ If Not WaitForFileAvailability(targetPath) Then ' 元ファイルが存在しない場合は新規作成なので無視して良いが、 ' 存在する場合はロック解除を待機する必要がある If Dir(targetPath) <> "" Then Err.Raise vbObjectError + 514, "SafeSave", "既存の図面ファイルが同期中のため、置換できません。" End If End If ' 4. アトミックな置換操作 ' 既存ファイルを削除し、一時ファイルをリネームする If Dir(targetPath) <> "" Then Kill targetPath Name tempPath As targetPath Debug.Print "Successfully saved: " & targetPath Exit Sub ErrorHandler: Debug.Print "Critical Error in SafeSave: " & Err.Description ' 一時ファイルが残っていればクリーンアップ If Dir(tempPath) <> "" Then Kill tempPath Err.Raise Err.Number, "SafeSave", Err.Description End Sub ''' <summary> ''' ファイルが利用可能(ロックされていない)になるまで待機する。 ''' 指数関数的バックオフアルゴリズムを採用。 ''' </summary> Private Function WaitForFileAvailability(ByVal filePath As String) As Boolean Dim retryCount As Integer Dim maxRetries As Integer Dim waitTime As LongPtr Dim currentWait As LongPtr maxRetries = 8 ' 最大リトライ回数 currentWait = 500 ' 初期待機時間 (0.5秒) For retryCount = 1 To maxRetries If IsFileAvailable(filePath) Then WaitForFileAvailability = True Exit Function End If ' 指数関数的バックオフ: 待機時間を倍増させていく (500ms, 1000ms, 2000ms...) Sleep currentWait currentWait = currentWait * 2 DoEvents ' OSに制御を戻し、同期プロセスが進む余地を作る Next retryCount WaitForFileAvailability = False End Function ''' <summary> ''' ファイルを排他的に開けるか試行することで、ロック状態を判定する。 ''' </summary> Private Function IsFileAvailable(ByVal filePath As String) As Boolean Dim fileNum As Integer On Error Resume Next fileNum = FreeFile ' Binaryモードかつ排他的アクセスを試みる Open filePath For Binary Access Read Write Lock Read Write As #fileNum If Err.Number = 0 Then Close #fileNum IsFileAvailable = True Else ' Err 70 (Permission Denied) 等が発生した場合はロック中と判断 IsFileAvailable = False End If On Error GoTo 0 End Function

4. アーキテクトの視点:設計の要諦

このコードを理解する上で、以下の3つのポイントを脳に刻んでほしい。

① 指数関数的バックオフの重要性

なぜ `currentWait = currentWait * 2` としているのか。
OneDriveの同期は、数ミリ秒で終わることもあれば、数分かかることもある。一定のインターバル(例:常に1秒)でリトライすると、同期中のCPU負荷を無駄に高め、かつ、長い同期に対しては非効率だ。待ち時間を倍々に増やすことで、「素早いレスポンス」と「低負荷な待機」を両立させている。

② `DoEvents` の真価

ループ内で `DoEvents` を呼び出すことは、単なる「おまじない」ではない。これは、VBAの実行スレッドに一時的に制御を戻し、Windowsのメッセージキューを処理させる命令だ。これにより、バックグラウンドで走っている同期プロセス(OneDrive.exe)がCPUリソースを確保しやすくなり、結果としてファイル解放が早まる。

③ アトミック性の確保

`targetDoc.SaveAs targetPath` を直接実行してはいけない。もし保存中に通信エラーや同期エラーが起きれば、元の図面は「中途半端な状態のファイル」に置き換わり、破損する。
必ず 「一時ファイルへ保存 $\to$ 成功を確認 $\to$ 置換」 というステップを踏むこと。これが、プロフェッショナルが守るべきデータの聖域だ。

結論

AutoCADの自動化は、CADの機能を知っているだけでは不十分だ。その背後にあるWindows OS、ファイルシステム、そしてクラウドストレージの挙動を理解して初めて、実戦に耐えうる「武器」となる。

今回紹介したアルゴリズムは、単なるエラー回避策ではない。「不確実な環境において、いかに決定論的な結果を導き出すか」という、ソフトウェアエンジニアリングの本質的な回答である。

このコードを、君のツールキットの核として組み込んでほしい。

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