【テクニカル・上級編】【上級プロフェッショナル向け】OneDriveの「オンデマンド保存」によるファイル未ダウンロード状態をVBAで判定・回避 – AutoCAD VBA解析バイブル

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

虚像のDWGを掴むな:OneDrive「オンデマンド」環境におけるファイル・ハイドレーションの極意

現代のエンタープライズ環境において、設計データの管理はローカルサーバーからクラウドストレージ、すなわちOneDriveやSharePointへと移行した。しかし、この「進化」は、長年堅牢な自動化システムを構築してきた我々エンジニアに、新たな、そして極めて厄介な敵をもたらした。

それが、「ファイル・オンデマンド(Files On-Demand)」である。

ファイルシステム上には確かにファイルが存在し、`Dir()` 関数や `FileSystemObject` は `True` を返す。しかし、その実体(データ本体)はローカルディスクには存在せず、クラウド上の雲の中に隠れている。この「虚像(Placeholder)」の状態のまま AutoCAD の `Documents.Open` メソッドを叩けば、システムは予期せぬタイムアウト、あるいは「ファイルが見つかりません」という、論理的に矛盾したエラーを吐いて沈黙する。

本稿では、この「実体なきファイル」を検知し、強制的にローカルへ引き戻す(ハイドレーションさせる)ための、Windows APIとShellオブジェクトを駆使した極限の制御手法を伝授する。

1. なぜ標準的な手法は敗北するのか

まず、なぜ既存のコードが通用しないのか、そのアーキテクチャ上の理由を理解せよ。

OneDriveのオンデマンド機能は、Windowsの Reparse Point(再解析ポイント) 技術を利用している。ファイルは「実体」ではなく、クラウド上のリソースへの「ポインタ」としてファイルシステムに登録されている。

  • `Dir()` 関数の限界: ファイルシステム上のエントリ(名前、属性、サイズ等)は参照できるため、存在確認としては「成功」してしまう。
  • AutoCADの挙動: AutoCADのファイルオープン処理は、OSのファイルハンドルを取得しようとする。この際、OneDriveのクライアントがバックグラウンドでダウンロードを開始するが、AutoCAD側がその「待ち時間」を許容できない場合、I/Oエラーとして処理が中断される。

我々に求められるのは、単なる存在確認ではない。「そのファイルは、今この瞬間に、メモリへ展開可能な状態で手元にあるか?」という問いへの回答である。

2. Windows Shellを用いた属性判定の設計

最も確実な判定方法は、Windows Shellの属性を直接参照することだ。OneDriveのファイルは、特定の属性ビットを持っている。

しかし、VBAから直接これらを扱うのは非効率である。そこで、`Shell.Application` オブジェクトを利用し、`FolderItem` の属性を精査するアプローチを採る。

実装の核となるロジック

以下のコードは、ファイルが「オンラインのみ(実体なし)」の状態であるかを判定するプロフェッショナルな関数である。

' --- Module: mModule_FileControl ---
Option Explicit

' Windows APIの宣言 (64bit環境対応)

If VBA7 Then

Private Declare PtrSafe Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long

Else

Private Declare Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long

End If

' ファイル属性の定数 Private Const FILE_ATTRIBUTE_REPARSE_POINT As Long = &H400 ''' <summary> ''' ファイルがOneDriveのオンデマンド状態(実体なし)かどうかを判定する ''' </summary> ''' <param name="filePath">フルパス</param> ''' <returns>True: オンラインのみ(実体なし), False: ローカルに実体あり</returns> Public Function IsFileOnlineOnly(ByVal filePath As String) As Boolean Dim shellApp As Object Dim folderPath As String Dim fileName As String Dim folderItem As Object Dim attr As Long ' パスの分離 folderPath = Left(filePath, InStrRev(filePath, "\") - 1) fileName = Mid(filePath, InStrRev(filePath, "\") + 1) ' Shell.Applicationによる高度な属性解析 Set shellApp = CreateObject("Shell.Application") Set folderItem = shellApp.Namespace(folderPath).ParseName(fileName) If folderItem Is Nothing Then IsFileOnlineOnly = False ' ファイルが存在しない場合はFalse(存在確認は別途行うこと) Exit Function End If ' Attributesプロパティから、クラウド特有のフラグを読み取る ' OneDriveのファイルはReparse Point属性を持つことが多い attr = folderItem.Attributes ' Reparse Point かつ、サイズが0(あるいは極端に小さい)等の条件を組み合わせる手法もあるが、 ' 最も堅牢なのは「実際にアクセスを試みて、OSのハイドレーションを強制すること」である。 ' ここでは判定ロジックとしてReparse Pointの有無を返す。 If (attr And FILE_ATTRIBUTE_REPARSE_POINT) <> 0 Then IsFileOnlineOnly = True Else IsFileOnlineOnly = False End If ' オブジェクトの明示的解放(メモリリーク防止) Set folderItem = Nothing Set shellApp = Nothing End Function

3. 強制ハイドレーション(ダウンロード)の戦略

判定ができても、次に「どうやってダウンロードさせるか」が問題となる。
最も洗練された方法は、「ファイルを低レベルなストリームで開こうと試みる」ことだ。これにより、Windows OSは「ファイルの実体が必要である」と判断し、OneDriveクライアントに対して即座にダウンロードを要求する。

以下に、これらを統合した「究極のファイルオープン・ラッパー」を示す。

''' <summary>
''' ファイルの実体を確保し、AutoCADで安全に開く
''' </summary>
''' <param name="targetPath">対象DWGファイルのフルパス</param>
''' <param name="acadApp">AutoCADアプリケーションオブジェクト</param>
Public Sub SafeOpenDWG(ByVal targetPath As String, ByVal acadApp As Object)
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' 1. 基本的な存在確認
    If Not fso.FileExists(targetPath) Then
        Err.Raise vbObjectError + 513, "FileControl", "Target file does not exist: " & targetPath
    End If
    
    ' 2. オンデマンド状態の判定
    Debug.Print "Checking status: " & targetPath
    If IsFileOnlineOnly(targetPath) Then
        Debug.Print "Status: Online-only detected. Initiating hydration..."
        
        ' 3. ハイドレーションの強制
        ' ファイルをバイナリモードで開き、最初の1バイトを読み取ることで、
        ' OSに実体のロードを強制させる。これが最も低負荷かつ確実な「トリガー」となる。
        On Error Resume Next
        Dim fileStream As Integer
        fileStream = FreeFile
        Open targetPath For Binary Access Read As #fileStream
        Dim dummyByte As Byte
        dummyByte = Input(1, #fileStream)
        Close #fileStream
        On Error GoTo 0
        
        ' ダウンロード完了までの僅かな待機(OSのI/Oキューを考慮)
        DoEvents
        Sleep 500 ' Windows APIのSleepを使用することを推奨
        Debug.Print "Hydration complete."
    End If

    ' 4. AutoCADでのオープン
    ' ここまで到達した時点では、ファイルは物理的にローカルに存在する。
    On Error GoTo ErrorHandler
    acadApp.Documents.Open targetPath
    
    Set fso = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "Failed to open drawing: " & Err.Description, vbCritical
    Set fso = Nothing
End Sub

' Windows API: Sleepの宣言

If VBA7 Then

Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Else

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

End If

4. アーキテクトの視点:パフォーマンスとライフサイクル

この実装において、シニアエンジニアが留意すべきは「リソースの管理」と「エラーの局所化」である。

1. オブジェクトの明示的解放:
`Shell.Application` は COM オブジェクトであり、参照カウンタが適切に処理されないと、AutoCAD プロセスが終了しても `explorer.exe` 側に残存し、ファイルロックを引き起こす原因となる。`Set obj = Nothing` は、単なる作法ではなく、堅牢なシステムの必須要件である。
2. バイナリ・アクセスの妙:
`Open … For Binary Access Read` を使用する理由は、単なる「ファイル読み込み」ではない。ファイル全体をメモリに載せるのではなく、「OSに対して、このファイルの実体が必要であるという意思表示(Signal)を送る」ことにある。これにより、数GBある巨大なDWGファイルであっても、メモリ消費を最小限に抑えつつ、ダウンロードをトリガーできる。
3. 待機時間の設計:
`DoEvents` と `Sleep` の組み合わせは、一見すると「泥臭い」ものに見えるかもしれない。しかし、非同期で行われるOneDriveのネットワークI/Oと、同期的なAutoCADのファイルオープン処理の間に、適切な「物理的な隙間」を作ることは、実行時エラーを回避するための高度なエンジニアリングである。

結論

クラウドストレージ時代の自動化において、ファイルシステムはもはや「静的なもの」ではない。常に流動的であり、実体と虚像が混在している。

我々プロフェッショナルに求められるのは、その流動性を前提とした、防御的かつ能動的な設計である。今回の「ハイドレーション制御」を実装に組み込むことで、君の構築するシステムは、環境の変化に左右されない、真にプロフェッショナルな堅牢性を手にするだろう。

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