重要な API
StorageLibraryChangeTracker クラスを使用すると、ユーザーがシステム内で変更を移動するときに、アプリでファイルとフォルダーの変更を追跡できます。 StorageLibraryChangeTracker クラスを使用すると、アプリは次を追跡できます。
- 追加、削除、変更などのファイル操作。
- 名前の変更や削除などのフォルダー操作。
- ドライブ上を移動するファイルとフォルダー。
このガイドを使用して、変更トラッカーを操作するためのプログラミング モデルを学習し、いくつかのサンプル コードを表示し、 StorageLibraryChangeTracker によって追跡されるさまざまな種類のファイル操作を理解します。
StorageLibraryChangeTracker は 、ユーザー ライブラリまたはローカル コンピューター上の任意のフォルダーに対して機能します。 これには、セカンダリ ドライブまたはリムーバブル ドライブが含まれますが、NAS ドライブやネットワーク ドライブは含まれません。
変更トラッカーの使用
変更トラッカーは、最後の N 個のファイル システム操作を格納する循環バッファーとしてシステムに実装されます。 アプリは、バッファーから変更を読み取り、独自のエクスペリエンスに処理できます。 アプリが変更を完了すると、変更が処理済みとしてマークされ、再び表示されることはありません。
フォルダーで変更トラッカーを使用するには、次の手順に従います。
- フォルダーの変更の追跡を有効にします。
- 変更を待ちます。
- 変更を読む
- 変更を受け入れます。
次のセクションでは、いくつかのコード例を含む各手順について説明します。 完全なコード サンプルは、記事の最後に示されています。
変更トラッカーを有効にする
アプリで最初に行う必要があるのは、特定のライブラリの変更追跡に関心があることをシステムに伝えることです。 これは、目的のライブラリの変更トラッカーで Enable メソッドを呼び出すことによって行われます。
StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
videoTracker.Enable();
いくつかの重要な注意事項:
- StorageLibrary オブジェクトを作成する前に、アプリにマニフェスト内の適切なライブラリへのアクセス許可があることを確認します。 詳細については、「 ファイル アクセス許可 」を参照してください。
- Enable はスレッド セーフであり、ポインターはリセットされず、何度でも呼び出すことができます (これについては後で詳しく説明します)。
変更を待つ
変更トラッカーが初期化されると、アプリが実行されていない間でも、ライブラリ内で発生したすべての操作の記録が開始されます。 StorageLibraryChangedTrigger イベントに登録することで、変更が発生するたびにアプリをアクティブ化するように登録できます。
変更を読む
その後、アプリは変更トラッカーからの変更をポーリングし、前回確認されてからの変更の一覧を受け取ることができます。 次のコードは、変更トラッカーから変更の一覧を取得する方法を示しています。
StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
videosLibrary.ChangeTracker.Enable();
StorageLibraryChangeReader videoChangeReader = videosLibrary.ChangeTracker.GetChangeReader();
IReadOnlyList changeSet = await changeReader.ReadBatchAsync();
アプリは、必要に応じて、変更を独自のエクスペリエンスまたはデータベースに処理する必要があります。
ヒント
有効にする 2 番目の呼び出しは、アプリが変更を読み取っている間にユーザーがライブラリに別のフォルダーを追加した場合に、競合状態から防御することです。 Enable の追加の呼び出しがないと、ユーザーがライブラリ内のフォルダーを変更している場合、コードは ecSearchFolderScopeViolation (0x80070490) で失敗します
変更を受け入れる
アプリが変更の処理を完了したら、 AcceptChangesAsync メソッドを呼び出して、それらの変更を再び表示されないようにシステムに指示する必要があります。
await changeReader.AcceptChangesAsync();
今後、アプリは変更トラッカーを読むときにのみ新しい変更を受け取ります。
- 変更がReadBatchAsync
呼び出しと AcceptChangesAsync の間で発生した場合に、ポインターはアプリが確認した最新の変更箇所までのみ進められます。 これらの他の変更は、次回 ReadBatchAsyncを呼び出す際に引き続き使用できます。 - 変更を受け入れなかった場合、次回アプリが ReadBatchAsync を呼び出すと、システムは同じ一連の変更を返します。
覚えておくべき重要なこと
変更トラッカーを使用する場合は、すべてが正しく動作していることを確認するために注意する必要がある点がいくつかあります。
バッファー オーバーラン
アプリが読み取れるまでシステムで発生するすべての操作を保持するために、変更トラッカーに十分な領域を確保しようとしますが、循環バッファーがそれ自体を上書きする前に、アプリが変更を読み取らないシナリオを想像するのは非常に簡単です。 特に、ユーザーがバックアップからデータを復元している場合、またはカメラフォンから大量の画像を同期している場合。
この場合、 ReadBatchAsync はエラー コード StorageLibraryChangeType.ChangeTrackingLost を返します。 アプリがこのエラー コードを受け取った場合は、次の 2 つのことを意味します。
- あなたが最後に確認した時点以降、バッファが自動的に上書きされました。 トラッカーからの情報が不完全になるため、ライブラリを再クロールすることをお勧めします。
- リセットを呼び出すまで、変更トラッカーはそれ以上の変更を返しません。 アプリ呼び出しがリセットされると、ポインターは最新の変更に移動され、追跡は正常に再開されます。
このようなケースが発生することはまれですが、ユーザーがディスク上で多数のファイルを移動しているシナリオでは、変更トラッカーが膨れ上がってストレージを過剰に占有しないようにしたいと思います。 これにより、Windows でのカスタマー エクスペリエンスを損なわない一方で、アプリは大規模なファイル システム操作に対応できるようになります。
StorageLibrary の変更
StorageLibrary クラスは、他のフォルダーを含むルート フォルダーの仮想グループとして存在します。 これをファイル システム変更トラッカーと調整するために、次の選択を行いました。
- ルート ライブラリ フォルダーの子孫に対する変更は、変更トラッカーで表されます。 ルート ライブラリ フォルダーは、 Folders プロパティを使用して見つけることができます。
- StorageLibrary (RequestAddFolderAsync および RequestRemoveFolderAsync) を使用してルートフォルダーを追加または削除しても、変更トラッカーにエントリは作成されません。 これらの変更は 、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();
}