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

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

AutoCAD VBAを掌握する極限の知見

OneDrive「オンデマンド保存」の罠:VBAでクラウドファイルを確実に制圧する極意

開発プロジェクトのリーダーである私のもとに、よくこんな悲鳴が届く。
「夜間バッチでOneDrive上のDWGを一括処理しようとしたら、ファイルを開くところでエラーが起きて止まった」
「手動で開くと何ともないのに、VBAの `Workbooks.Open` や `Documents.Open`(※AutoCADでは `Documents.Open`)に相当するメソッドが突如として沈黙する」

原因は明白だ。現代のインフラストラクチャにおいて避けて通れないOneDriveの「ファイルオンデマンド(Files On-Demand)」機能である。

クラウド上に実体があり、ローカルには「影(プレースホルダー)」しか存在しないファイルを、AutoCAD VBAの冷徹なオブジェクトモデルがそのまま叩こうとする。結果、ファイルが存在しない(あるいは読み込み権限が同期中でロックされている)と判定され、容赦なくRuntimeエラーを吐き捨てるのだ。

今回は、このモダンPC環境特有の罠を完全にハックし、VBAからクラウドストレージのステータスを検知・強制ダウンロードさせてから安全にAutoCAD図面を開く、プロダクション品質のソリューションを伝授する。

なぜ通常の `Dir` 関数や `CreateObject(“Scripting.FileSystemObject”)` では防げないのか?

アマチュアプログラマブルな発想であれば、こう考えるだろう。
*「`Dir(“C:\path\to\drawing.dwg”)` でファイルの存在確認をすればいいのではないか?」*

大間違いだ。
FSOや `Dir` 関数は、OneDriveのプレースホルダー(拡張属性としての再解析ポイント:Reparse Point)を「ファイルが存在する」と正常判定してしまう。なぜなら、Windows OSのファイルシステム層において、そのファイルはエクスプローラー上に見えているからだ。

しかし、AutoCADのコアエンジンが低レイヤーでファイルストリームを開こうとした瞬間、実体がクラウドの彼方にあるため、I/Oエラーを引き起こす。

我々が実装すべきは、「ファイルが見えるか」ではなく「ローカルに実体(バイトデータ)が存在するか」の厳密な判定である。

決定版:属性判定とシェルAPIによる自動ダウンロード制御

以下のコードは、単なる「動くコード」ではない。実務の現場で予期せぬ切断や同期遅延が発生してもプロセスがクラッシュしないよう、エラーハンドリングとシェル実行を完璧に同期させたエンタープライズグレードのモジュールだ。

Option Explicit

' Windows API Declarations for File Attribute Inspection
Private Declare PtrSafe Function GetFileAttributesW Lib "kernel32" (ByVal lpFileName As LongPtr) As Long
Private Const INVALID_FILE_ATTRIBUTES As Long = &HFFFFFFFF
Private Const FILE_ATTRIBUTE_RECALL_ON_OPEN As Long = &H40000
Private Const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS As Long = &H400000

''
' 指定されたDWGファイルがOneDriveのオンデマンド未ダウンロード状態(クラウド専用)か判定する
' @param filePath 判定するファイルのフルパス
' @return True = ローカルに実体なし(要ダウンロード), False = ローカルに実体あり
''
Public Function IsCloudOnlyFile(ByVal filePath As String) As Boolean
    Dim attr As Long
    attr = GetFileAttributesW(StrPtr(filePath))
    
    If attr = INVALID_FILE_ATTRIBUTES Then
        ' ファイルが存在しない、またはアクセス権がない
        IsCloudOnlyFile = False
        Exit Function
    End If
    
    ' FILE_ATTRIBUTE_RECALL_ON_OPEN または FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS が
    ' 立っている場合、それはクラウド上にのみ存在する(=オンデマンドのプレースホルダー)状態を指す
    If (attr And FILE_ATTRIBUTE_RECALL_ON_OPEN) <> 0 Or _
       (attr And FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) <> 0 Then
        IsCloudOnlyFile = True
    Else
        IsCloudOnlyFile = False
    End If
End Function

''
' クラウド上のファイルを強制的にローカルへ同期(ダウンロード)させ、AutoCADで安全に開く
' @param targetPath 開きたいDWGファイルのフルパス
' @return AcadDocumentオブジェクト
''
Public Function OpenCloudAwareDocument(ByVal targetPath As String) As AcadDocument
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' 1. 基本的な存在チェック
    If Not fso.FileExists(targetPath) Then
        Err.Raise vbObjectError + 1000, "OpenCloudAwareDocument", "指定されたファイルが存在しません: " & targetPath
    End If
    
    ' 2. オンデマンド未ダウンロード状態の判定
    If IsCloudOnlyFile(targetPath) Then
        Debug.Print "【OneDrive制御】ファイルがクラウド上にあります。ダウンロードをトリガーします: " & targetPath
        
        ' 強制的にファイルを読み込む(シェル経由でアクセスすることでOneDriveクライアントが同期を開始する)
        If Not TriggerOneDriveDownload(targetPath) Then
            Err.Raise vbObjectError + 1001, "OpenCloudAwareDocument", "ファイルのクラウドからのダウンロードに失敗しました。"
        End If
    End If
    
    ' 3. AutoCADのドキュメントコレクションに追加(オープン)
    ' ※注意: AutoCADのバージョンにより、Documents.Openの引数仕様が異なる場合があります。
    Dim targetDoc As AcadDocument
    Set targetDoc = ThisDrawing.Application.Documents.Open(targetPath)
    
    Set OpenCloudAwareDocument = targetDoc
End Function

''
' シェルオブジェクト経由でファイルにアクセスし、OneDriveのダウンロードプロセスを強制キックする
' @param filePath 対象ファイルのフルパス
' @return 成功したかどうか
''
Private Function TriggerOneDriveDownload(ByVal filePath As String) As Boolean
    On Error GoTo ErrorHandler
    
    Dim shellApp As Object
    Set shellApp = CreateObject("Shell.Application")
    
    ' フォルダとファイル名に分離
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Dim folderPath As String
    Dim fileName As String
    
    folderPath = fso.GetParentFolderName(filePath)
    fileName = fso.GetFileName(filePath)
    
    Dim ns As Object
    Set ns = shellApp.NameSpace(folderPath)
    
    If ns Is Nothing Then
        TriggerOneDriveDownload = False
        Exit Function
    End If
    
    Dim item As Object
    Set item = ns.ParseName(fileName)
    
    If Not item Is Nothing Then
        ' 「常にこのデバイスに保持する(Make available offline)」属性を一時的に付与するか、
        ' 単にプロパティにアクセスすることで同期を促す。
        ' 最も確実なのは、バイナリとしての読み込みストリームをシェル経由でかすめること。
        
        Dim dummyStream As Integer
        dummyStream = FreeFile
        
        ' プレースホルダーに対してバイナリリードを試みることで、
        ' WindowsのファイルシステムフィルタドライバがOneDriveへフェッチ要求を送信する
        Open filePath For Binary Access Read As #dummyStream
        Close #dummyStream
        
        ' 同期完了まで微小なウェイトを挟みつつ、実体化をポーリングする(最大10秒)
        Dim startTime As Double
        startTime = Timer
        Do While IsCloudOnlyFile(filePath)
            DoEvents
            If Timer - startTime > 10 Then
                TriggerOneDriveDownload = False
                Exit Function
            End If
        Loop
        
        TriggerOneDriveDownload = True
    Else
        TriggerOneDriveDownload = False
    End If
    
    Exit Function

ErrorHandler:
    On Error Resume Next
    Close #dummyStream
    TriggerOneDriveDownload = False
End Function

アーキテクトからの実践的アドバイス:なぜこの実装が「プロフェッショナル」なのか

1. Win32 APIの厳密な定数活用
`FILE_ATTRIBUTE_RECALL_ON_OPEN` などの属性値は、マイクロソフトのファイルシステム仕様書(MS-FSCC)に定義されているモダンOS向けのフラグだ。これを理解しているか否かで、クラウドストレージ連携バグに直面した際のデバッグスピードが文字通り10倍変わる。
2. バイナリリードによる同期トリガー(Dumb-to-Smart Hack)
シェル経由でただ待つのではなく、`Open … For Binary Access Read` をあえて実行することで、OSのI/Oサブシステムに「今すぐこのデータブロックが必要だ」と強制シグナルを送る。これが、OneDriveクライアントを最も確実かつプログラム的におびき出すキラーテクニックである。
3. タイムアウト機構(ポーリング)の重要性
ネットワーク環境やOneDriveの負荷によっては、ダウンロードに数秒のラグが生じる。`DoEvents` とタイマーを組み合わせたブロッキング回避ループを入れることで、AutoCADのUIスレッドを完全にフリーズさせることなく、スマートに同期完了を待つことができる。

結論

クラウドの普及により、ローカルディスクの概念は変質した。今や「そこにあるはずのファイルが開けない」という矛盾は、VBAエンジニアにとって日常的なリスクである。

今回提供したコードをあなたの自動化ライブラリの基盤層に組み込むことで、OneDrive、SharePoint、さらには将来的なクラウドストレージ環境においても、一切の例外を恐れない鉄壁の図面処理自動化パイプラインが完成する。

手戻りのない、真に堅牢なアーキテクチャを現場に導入してほしい。

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