Nachverfolgen von Dateisystemänderungen im Hintergrund
Wichtige APIs
Die Klasse StorageLibraryChangeTracker ermöglicht es Apps, Änderungen an Dateien und Ordnern nachzuverfolgen, die von Benutzern im System verschoben werden. Mithilfe der Klasse StorageLibraryChangeTracker kann eine App Folgendes nachverfolgen:
- Dateivorgänge wie Hinzufügen, Löschen und Ändern
- Ordnervorgänge wie Umbenennungen und Löschungen
- Verschiebung von Dateien und Ordnern auf dem Laufwerk
In diesem Handbuch lernst du das Programmiermodell für die Verwendung der Änderungsnachverfolgung kennen. Außerdem findest du hier etwas Beispielcode sowie Informationen zu den verschiedenen Arten von Dateivorgängen, die von StorageLibraryChangeTracker nachverfolgt werden.
StorageLibraryChangeTracker eignet sich für Benutzerbibliotheken sowie für jeden beliebigen Ordner auf dem lokalen Computer. Dies schließt sekundäre Laufwerke und Wechseldatenträger mit ein, gilt aber nicht für NAS- oder Netzlaufwerke.
Verwenden der Änderungsnachverfolgung
Die Änderungsnachverfolgung wird im System als zirkulärer Puffer implementiert, in dem die letzten N Dateisystemvorgänge gespeichert werden. Apps können die Änderungen aus dem Puffer lesen und in ihrer eigenen Umgebung verarbeiten. Wenn die App die Verarbeitung der Änderungen abgeschlossen hat, werden diese als verarbeitet markiert und in Zukunft nicht mehr angezeigt.
Führe die folgenden Schritte aus, um die Änderungsnachverfolgung für einen Ordner zu verwenden:
- Aktiviere die Änderungsnachverfolgung für den Ordner.
- Warte auf Änderungen.
- Lies die Änderungen.
- Akzeptiere die Änderungen.
Die obigen Schritte werden in den nächsten Abschnitten anhand von Codebeispielen veranschaulicht. Das vollständige Codebeispiel befindet sich am Ende des Artikels.
Aktivieren der Änderungsnachverfolgung
Die App muss dem System zunächst signalisieren, dass sie die Änderungsnachverfolgung für eine bestimmte Bibliothek verwenden möchte. Hierzu ruft sie für die relevante Bibliothek die Methode Enable für die Änderungsnachverfolgung auf.
StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
videoTracker.Enable();
Einige wichtige Hinweise:
- Vergewissere dich vor der Erstellung des Objekts StorageLibrary, dass deine App im Manifest über Berechtigungen für die korrekte Bibliothek verfügt. Ausführlichere Informationen findest du unter Berechtigungen für den Dateizugriff.
- Enable ist threadsicher, setzt deinen Zeiger nicht zurück und kann beliebig oft aufgerufen werden. (Dies wird später noch genauer erläutert.)
Warten auf Änderungen
Nach der Initialisierung beginnt die Änderungsnachverfolgung damit, sämtliche Vorgänge zu erfassen, die innerhalb der Bibliothek stattfinden – selbst wenn die App gar nicht ausgeführt wird. Apps können sich für das Ereignis StorageLibraryChangedTrigger registrieren, um aktiviert zu werden, sobald eine Änderung vorgenommen wird.
Lesen der Änderungen
Die App kann nun Änderungen aus der Änderungsnachverfolgung abrufen, um eine Liste mit den Änderungen seit der letzten Überprüfung zu erhalten. Der folgende Code zeigt das Abrufen einer Liste mit Änderungen aus der Änderungsnachverfolgung:
StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
videosLibrary.ChangeTracker.Enable();
StorageLibraryChangeReader videoChangeReader = videosLibrary.ChangeTracker.GetChangeReader();
IReadOnlyList changeSet = await changeReader.ReadBatchAsync();
Die App muss die Änderungen nun nach Bedarf in ihrer eigenen Umgebung oder Datenbank verarbeiten.
Tipp
Der zweite Aufruf dient zur Vermeidung einer Racebedingung für den Fall, dass der Benutzer der Bibliothek einen weiteren Ordner hinzufügt, während deine App Änderungen liest. Ohne den zusätzlichen Aufruf von Enable tritt ein ecSearchFolderScopeViolation-Fehler (0x80070490) auf, wenn der Benutzer die Ordner in ihrer Bibliothek ändert.
Akzeptieren der Änderungen
Nachdem die App die Änderungen verarbeitet hat, muss sie das System durch Aufrufen der Methode AcceptChangesAsync anweisen, die Änderungen nicht mehr anzuzeigen.
await changeReader.AcceptChangesAsync();
Bei zukünftigen Lesevorgängen für die Änderungsnachverfolgung erhält die App nun nur noch neue Änderungen.
- Sollten Änderungen zwischen dem Aufruf von ReadBatchAsync und dem Aufruf von AcceptChangesAsync stattgefunden haben, wird der Zeiger nur bis zur neuesten Änderung versetzt, die von der App erfasst wurde. Die übrigen Änderungen sind beim nächsten Aufruf von ReadBatchAsync weiterhin verfügbar.
- Werden die Änderungen nicht akzeptiert, gibt das System die gleiche Gruppe von Änderungen zurück, wenn die App das nächste Mal ReadBatchAsync aufruft.
Wichtige Punkte
Im Zusammenhang mit der Verwendung der Änderungsnachverfolgung müssen einige Dinge berücksichtigt werden, um sicherzustellen, dass alles ordnungsgemäß funktioniert.
Pufferüberläufe
Wir versuchen zwar, in der Änderungsnachverfolgung genügend Speicherplatz zu reservieren, damit alle ausgeführten Vorgänge gespeichert werden können, bis sie von deiner App gelesen werden, es kann jedoch schnell passieren, dass der zirkuläre Puffer sich selbst überschreibt, bevor die App die Änderungen gelesen hat. Dieser Fall kann insbesondere eintreten, wenn der Benutzer Daten aus einer Sicherung wiederherstellt oder eine umfangreiche Bildersammlung von seinem Smartphone synchronisiert.
In diesem Fall gibt ReadBatchAsync den Fehlercode StorageLibraryChangeType.ChangeTrackingLost zurück. Wenn deine App diesen Fehlercode empfängt, bedeutet das Folgendes:
- Der Puffer hat sich seit der letzten Betrachtung selbst überschrieben. In diesem Fall empfiehlt es sich, die Bibliothek erneut zu durchforsten, da die Nachverfolgungsinformationen unvollständig sind.
- Die Änderungsnachverfolgung gibt bis zum Aufrufen von Reset keine weiteren Änderungen zurück. Nachdem die App „Reset“ aufgerufen hat, wird der Zeiger an der Position der neuesten Änderung platziert, und die Nachverfolgung wird normal fortgesetzt.
Solche Fälle sind zwar eher selten, wenn ein Benutzer aber eine große Anzahl von Dateien auf dem Datenträger verschiebt, soll die Änderungsnachverfolgung nicht zu stark aufgebläht werden und dadurch zu viel Speicherplatz beanspruchen. Mit dieser Lösung können Apps auf umfangreiche Dateisystemvorgänge reagieren, ohne die Benutzerfreundlichkeit unter Windows zu beeinträchtigen.
Änderungen an einer Speicherbibliothek (StorageLibrary)
Die Klasse StorageLibrary ist eine virtuelle Gruppe von Stammordnern, die wiederum weitere Ordner enthalten. Um dies mit einer Dateisystem-Änderungsnachverfolgung unter einen Hut zu bringen, haben wir folgende Entscheidungen getroffen:
- Sämtliche Änderungen an Nachfolgern der Stammbibliothekordner werden in der Änderungsnachverfolgung berücksichtigt. Die Stammbibliothekordner können mithilfe der Eigenschaft Folders ermittelt werden.
- Das Hinzufügen oder Entfernen von Stammordnern zu bzw. aus einem Element vom Typ StorageLibrary (über RequestAddFolderAsync bzw. RequestRemoveFolderAsync) führt nicht zur Erstellung eines Eintrags in der Änderungsnachverfolgung. Diese Änderungen können über das Ereignis DefinitionChanged oder mittels Aufzählung der Stammordner in der Bibliothek (mithilfe der Eigenschaft Folders) nachverfolgt werden.
- Wenn der Bibliothek ein nicht leerer Ordner hinzugefügt wird, werden keine Änderungsbenachrichtigungen oder Einträge in der Änderungsnachverfolgung generiert. Spätere Änderungen an den Nachfolgern dieses Ordners führen zur Generierung von Benachrichtigungen sowie zu Einträgen in der Änderungsnachverfolgung.
Aufrufen der Methode „Enable“
Apps müssen die Methode Enable aufrufen, sobald sie mit der Nachverfolgung des Dateisystems beginnen, sowie vor jeder Aufzählung der Änderungen. Dadurch wird sichergestellt, dass alle Änderungen von der Änderungsnachverfolgung erfasst werden.
Zusammenführung
Hier siehst du den gesamten Code. Er dient dazu, eine Registrierung für die Änderungen aus der Videobibliothek durchzuführen und mit dem Abrufen der Änderungen aus der Änderungsnachverfolgung zu beginnen.
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();
}