共用方式為


在背景中監控檔案系統的變動

重要的應用程式介面

StorageLibraryChangeTracker 類別可讓應用程式追蹤檔案和資料夾的變更,當使用者在系統中移動變更。 使用 StorageLibraryChangeTracker 類別,應用程式可以追蹤:

  • 檔案作業,包括新增、刪除、修改。
  • 資料夾作業,例如重新命名和刪除。
  • 在磁碟驅動器上移動的檔案和資料夾。

使用本指南來瞭解使用變更追蹤器的程序設計模型、檢視一些範例程序代碼,以及瞭解 StorageLibraryChangeTracker 所追蹤的不同檔案作業類型。

StorageLibraryChangeTracker 適用於用戶連結庫,或本機計算機上的任何資料夾。 這包括次要磁碟驅動器或可移除磁碟驅動器,但不包含 NAS 磁碟驅動器或網路磁碟驅動器。

使用變更追蹤器

變更追蹤器在系統上實作為環形緩衝區,用來儲存最近 N 次檔案系統操作。 應用程式能夠從緩衝區讀取變更,然後將這些變更整合進自己的使用體驗。 應用程式完成變更後,它會將變更標示為已處理,而且永遠不會再看到變更。

若要在資料夾上使用變更追蹤器,請遵循下列步驟:

  1. 啟用資料夾的變更追蹤。
  2. 等候變更。
  3. 讀取變更。
  4. 接受變更。

下一節會逐步解說每個步驟,其中包含一些程式代碼範例。 完整的程式代碼範例會在文章結尾提供。

啟用變更追蹤器

應用程式需要做的第一件事是告訴系統,它有興趣變更追蹤指定的程式庫。 它會藉由在目標庫的變更追蹤器上呼叫 Enable 方法來執行這項作業。

StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
videoTracker.Enable();

一些重要注意事項:

  • 在建立 StorageLibrary 物件之前,請確定您的應用程式具有清單檔中正確資料庫的權限。 如需詳細資訊,請參閱 檔案訪問許可權
  • 啟用 是執行緒安全的,不會重設您的指標,而且可以隨意多次呼叫(稍後會詳細說明)。

啟用空白變更追蹤器

等候變更

在變更追蹤器初始化之後,即使應用程式未執行,它也會開始記錄程式庫中發生的所有作業。 應用程式可以隨時註冊以啟用,方法是註冊 StorageLibraryChangedTrigger 事件。

變更新增至變更追蹤器,而不需要應用程式讀取變更

閱讀變更

然後,應用程式可以查詢變更追蹤器中的變更,並接收自上次檢查以來所發生的變更清單。 下列程式代碼示範如何從變更追蹤器取得變更清單。

StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
videosLibrary.ChangeTracker.Enable();
StorageLibraryChangeReader videoChangeReader = videosLibrary.ChangeTracker.GetChangeReader();
IReadOnlyList changeSet = await changeReader.ReadBatchAsync();

然後,應用程式會負責視需要處理變更以更新使用者體驗或資料庫。

將變更從變更追蹤器讀取到應用程式資料庫

小提示

啟用的第二次呼叫是為了防止競爭狀況,特別是在當您的應用程式正在讀取變更時,使用者將另一個資料夾新增至資料庫時。 如果使用者變更其文檔庫中的資料夾,且未額外呼叫 Enable,程式代碼將會失敗,並顯示錯誤代碼 ecSearchFolderScopeViolation (0x80070490)。

接受變更

應用程式完成處理變更之後,應該呼叫 AcceptChangesAsync 方法,告訴系統不要再顯示這些變更。

await changeReader.AcceptChangesAsync();

將變更標示為已讀,這樣它們就不會再顯示

未來,應用程式只有在讀取變更追蹤器時才會收到最新的變更。

  • 如果在呼叫 ReadBatchAsyncAcceptChangesAsync之間發生變更,則指標只會進階到應用程式最近看到的變更。 下次呼叫 ReadBatchAsync 時,仍然可以使用其他變更。
  • 不接受變更會導致系統在下一次呼叫 ReadBatchAsync 時傳回相同的一組變更。

要記住的重要事項

使用變更追蹤器時,您應該記住一些事項,以確保一切正常運作。

緩衝區滿溢

雖然我們嘗試在變更追蹤器中保留足夠的空間,以保存系統上發生的所有作業,直到您的應用程式可以讀取它們為止,但很容易想像應用程式在迴圈緩衝區覆寫本身之前不會讀取變更的案例。 尤其是在使用者從備份中還原資料或從手機相機同步大量圖片集合時。

在此情況下, ReadBatchAsync 會傳回錯誤碼 StorageLibraryChangeType.ChangeTrackingLost。 如果您的應用程式收到這個錯誤碼,這表示有幾件事:

  • 自上次查看以來,緩衝區已經自我覆寫。 最佳的行動方案是重新抓取資料庫,因為來自追蹤器的任何資訊都會不完整。
  • 在您呼叫 Reset 之前,變更追蹤器不會再傳回任何變更。 應用程式呼叫重設之後,指標會移至最新的變更,且追蹤會正常繼續。

取得這些案例應該很罕見,但在使用者在其磁碟上移動大量檔案的情況下,我們不希望變更追蹤器佔用太多儲存空間。 這應該可讓應用程式回應大量檔案系統作業,同時不會損害 Windows 中的客戶體驗。

StorageLibrary 的變更

StorageLibrary 類別會以包含其他資料夾的根資料夾虛擬群組的形式存在。 為了與文件系統變更追蹤器協調這一點,我們做了下列選擇:

  • 根連結庫資料夾下階的任何變更都會在變更追蹤器中表示。 您可以使用 Folders 屬性找到根文件庫資料夾。
  • StorageLibrary 新增或移除根資料夾(透過 RequestAddFolderAsyncRequestRemoveFolderAsync)將不會在變更追蹤器中建立條目。 這些變更可以透過 DefinitionChanged 事件追蹤,或使用 Folders 屬性列舉連結庫中的根資料夾。
  • 如果將已有內容的資料夾添加到文件庫,則不會產生變更通知或變更追蹤記錄。 該資料夾子項目的任何後續變更都會產生通知和變更紀錄條目。

呼叫 Enable 方法

應用程式應在開始追蹤檔案系統時,以及每次列舉變更之前,立即呼叫 Enable。 這可確保變更追蹤器會擷取所有變更。

把它放在一起

以下是所有用於接收影片庫變更通知的程式代碼,並開始從變更追蹤器中提取變更。

private async void EnableChangeTracker()
{
    StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
    StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
    videoTracker.Enable();
}

private async void GetChanges()
{
    StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
    videosLibrary.ChangeTracker.Enable();
    StorageLibraryChangeReader videoChangeReader = videosLibrary.ChangeTracker.GetChangeReader();
    IReadOnlyList changeSet = await changeReader.ReadBatchAsync();


    //Below this line is for the blog post. Above the line is for the magazine
    foreach (StorageLibraryChange change in changeSet)
    {
        if (change.ChangeType == StorageLibraryChangeType.ChangeTrackingLost)
        {
            //We are in trouble. Nothing else is going to be valid.
            log("Resetting the change tracker");
            videosLibrary.ChangeTracker.Reset();
            return;
        }
        if (change.IsOfType(StorageItemTypes.Folder))
        {
            await HandleFileChange(change);
        }
        else if (change.IsOfType(StorageItemTypes.File))
        {
            await HandleFolderChange(change);
        }
        else if (change.IsOfType(StorageItemTypes.None))
        {
            if (change.ChangeType == StorageLibraryChangeType.Deleted)
            {
                RemoveItemFromDB(change.Path);
            }
        }
    }
    await changeReader.AcceptChangesAsync();
}