重要的 API
StorageLibraryChangeTracker 类允许应用在用户移动文件和文件夹时跟踪文件和文件夹中的更改。 使用 StorageLibraryChangeTracker 类,应用可以跟踪:
- 文件操作,包括添加、删除、修改。
- 重命名和删除等文件夹操作。
- 在驱动器上移动的文件和文件夹。
使用本指南可了解用于处理更改跟踪器的编程模型,查看一些示例代码,并了解 StorageLibraryChangeTracker 跟踪的不同类型的文件操作。
StorageLibraryChangeTracker 适用于用户库或本地计算机上的任何文件夹。 这包括辅助驱动器或可移动驱动器,但不包括 NAS 驱动器或网络驱动器。
使用更改跟踪器
更改跟踪器在系统上实现为存储最后 N 个文件系统操作的循环缓冲区。 应用可以从缓冲区读取更改,然后将其处理成自己的体验。 应用完成更改后,它会将更改标记为已处理,并且永远不会再次看到这些更改。
若要对文件夹使用更改跟踪器,请执行以下步骤:
- 为文件夹启用更改跟踪。
- 等待更改。
- 查看更改
- 接受更改。
下一部分将演练每个步骤,其中包含一些代码示例。 本文末尾提供了完整的代码示例。
启用更改跟踪器
应用需要做的第一件事是告诉系统,它有兴趣跟踪给定库的更改。 它通过在感兴趣的库的更改跟踪器上调用 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();
然后,应用负责根据需要将更改处理到自己的体验或数据库中。
小窍门
启用的第二次调用是为了防止在应用程序读取更改时,如果用户将另一个文件夹添加到库中而导致的竞态条件。 如果没有对启用代码的额外调用,如果用户正在更改其库中的文件夹,则 ecSearchFolderScopeViolation (0x80070490) 将失败
接受更改
应用处理完更改后,应通过调用 AcceptChangesAsync 方法告知系统不要再次显示这些更改。
await changeReader.AcceptChangesAsync();
该应用程序将仅在未来读取更改跟踪器时接收新的更改。
- 如果在调用 ReadBatchAsync 和 AcceptChangesAsync 之间发生了更改,则指针将仅高级到应用看到的最新更改。 下次调用 ReadBatchAsync 时,这些其他更改仍将可用。
- 不接受这些更改将导致系统下次调用 ReadBatchAsync 时返回相同的更改集。
需要记住的重要事项
使用更改跟踪器时,应记住一些事项,以确保一切正常运行。
缓冲区溢出
尽管我们尝试在更改跟踪器中保留足够的空间来保存系统上发生的所有操作,直到应用可以读取它们,但很容易想象应用在循环缓冲区覆盖自身之前不会读取更改的情况。 尤其是在用户从备份还原数据或从相机手机同步大量图片时。
在这种情况下,ReadBatchAsync 将返回错误代码 StorageLibraryChangeType.ChangeTrackingLost。 如果应用收到此错误代码,则意味着以下几点:
- 自上次查看缓冲区以来,缓冲区已自我覆盖。 最佳做法是重新抓取库,因为跟踪器中的任何信息都将不完整。
- 在调用 “重置”之前,更改跟踪器不会再返回任何更改。 应用程序调用重置后,指针将移动到最新的更改位置,跟踪过程将正常恢复。
很少出现这种情况,但在用户在其磁盘上移动大量文件的情况中,我们不希望更改跟踪器膨胀并占用过多的存储。 这应该允许应用对大规模文件系统操作做出反应,同时不会损害 Windows 中的客户体验。
对 StorageLibrary 的更改
StorageLibrary 类作为包含其他文件夹的根文件夹的虚拟组存在。 为了将此与文件系统更改跟踪器进行协调,我们做出了以下选择:
- 对根库文件夹后代所做的任何更改都将在更改跟踪器中表示。 可以使用 Folders 属性找到根库文件夹。
- 通过 RequestAddFolderAsync 和 RequestRemoveFolderAsync 从 StorageLibrary 添加或删除根文件夹不会在更改跟踪器中创建条目。 可以通过 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();
}