How to Track File Change in WinUI?

水 知 485 Reputation points
2026-05-25T04:39:57.24+00:00

Hi community!

I'm developing a file manager with WinUI3 and C#, and I have a question.

I want to add a file track function to automatically update the change of file content or file addition or deletion of a file/folder, or a rename/move action of them. I've searched for it and the results said that FileSystemWatcher could work.

The problem is that my program might not able to keep a background task, so I think maybe I should try another way, but I haven't found any useful content of it.

Windows development | WinUI
0 comments No comments

Answer accepted by question author
Jack Dang (WICLOUD CORPORATION) 18,975 Reputation points Microsoft External Staff Moderator
2026-05-25T06:41:57.7166667+00:00

Hi @水 知 ,

Thanks for reaching out.

The sample code below is just for reference, so you would likely need to adjust it a bit to fit your app structure, lifecycle, permissions, and the specific folders or libraries your project is working with.

If you need this to stay reasonably reliable even when the app is not running, I probably would not rely on FileSystemWatcher alone. It can still work well for live updates while your process is active, but it only works while the app is running, and it may miss events if the internal buffer overflows during heavier file activity.

For WinUI 3, a better fit may be the Windows file change tracking API, mainly StorageLibraryChangeTracker together with StorageLibraryChangeReader. That API is intended for tracking file adds, deletes, modifications, renames, and moves over time, including changes that happen while the app is closed.

This generally works best if the location you are watching is a user library such as Documents, Pictures, or Videos, or another supported local or removable folder. It usually would not be the right choice for network or NAS paths.

The suitable flow is to enable change tracking first:

using Windows.Storage;

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

Then when your app starts, resumes, or gets activated to process changes, enable it again before reading and pull the pending batch:

StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);

// Enable again before enumeration to avoid race conditions.
videosLibrary.ChangeTracker.Enable();

StorageLibraryChangeReader changeReader = videosLibrary.ChangeTracker.GetChangeReader();
var changeSet = await changeReader.ReadBatchAsync();

foreach (StorageLibraryChange change in changeSet)
{
  if (change.ChangeType == StorageLibraryChangeType.ChangeTrackingLost)
  {
    videosLibrary.ChangeTracker.Reset();
    // Re-enumerate and rebuild your index here.
    return;
  }

  // Handle add, delete, modify, rename, or move here.
}

await changeReader.AcceptChangesAsync();

Calling AcceptChangesAsync() is important because without it, the same batch would normally be returned again next time. If more changes happen after ReadBatchAsync() but before AcceptChangesAsync(), those newer ones should stay available for the next read.

If you want the app to wake up when changes happen even while it is not open, you could also look at registering a background task with StorageLibraryChangedTrigger. That can give you a way to process the tracked changes outside a normal foreground session.

There are a couple of practical details to keep in mind. The tracker uses a circular buffer, so if a very large number of operations happen before your app reads them, you may get ChangeTrackingLost, and the usual recovery would be to rescan the library and rebuild your cache or database. Also, adding or removing root folders from a StorageLibrary does not create change tracker entries, so if that matters in your case, you would probably want to handle it through StorageLibrary.DefinitionChanged or by rechecking StorageLibrary.Folders.

If helpful, here are the official docs that are most relevant:

Hope this helps! If my explanation and the information I provided were also helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

Was this answer helpful?

1 person found this answer helpful.

Answer accepted by question author

AI answer

2026-05-25T04:40:16.28+00:00

For a WinUI 3 file manager that must react to file additions, deletions, renames, moves, and content changes, and also handle changes while the app is not running, use the Windows file change tracking APIs instead of relying only on FileSystemWatcher.

FileSystemWatcher works only while the process is running and uses an in‑memory buffer that can overflow under heavy load, causing missed events. It also does not help when the app is closed.

For WinUI 3 on Windows 10 1809+ (with Windows App SDK), use StorageLibraryChangeTracker and related types:

  1. Choose the scope
    StorageLibraryChangeTracker works on user libraries (Documents, Pictures, Videos, etc.) or any local/removable drive folder (not network/NAS).
  2. Declare capabilities
    In the app package manifest, declare the appropriate library capabilities so the app can access the StorageLibrary to be tracked (see File access permissions in the referenced docs).
  3. Enable change tracking Call Enable on the library’s change tracker when starting to track:
       using Windows.Storage;
       
       StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
       StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
       videoTracker.Enable();
    
    Enable is thread‑safe, can be called multiple times, and does not reset the internal pointer.
  4. Receive changes while the app is not running After Enable is called, the system records file operations in a circular buffer even when the app is closed. To be activated when changes occur, register a background task using StorageLibraryChangedTrigger so the app can wake up and process changes.
  5. Read batched changes When the app (or background task) runs, read the accumulated changes:
       StorageLibrary videosLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
       
       // Call Enable again before enumeration to avoid race conditions
       videosLibrary.ChangeTracker.Enable();
       
       StorageLibraryChangeReader changeReader = videosLibrary.ChangeTracker.GetChangeReader();
       var changeSet = await changeReader.ReadBatchAsync();
       
       // Process changeSet: add/delete/modify/rename/move for files and folders
    
    The change tracker records file adds, deletes, modifications, folder renames/deletes, and moves within the tracked scope.
  6. Accept processed changes After updating the app’s internal state (e.g., refreshing the UI or syncing a database), mark the batch as processed so it will not be returned again:
       await changeReader.AcceptChangesAsync();
    
    • If more changes occur between ReadBatchAsync and AcceptChangesAsync, the pointer advances only to the last change already seen; newer ones remain for the next read.
    • If AcceptChangesAsync is not called, the same batch is returned again on the next read.
  7. Important behaviors
    • The tracker is a circular buffer of the last N operations; the app is responsible for persisting whatever history or index it needs.
    • Adding/removing root folders of a StorageLibrary does not create tracker entries; use StorageLibrary.DefinitionChanged or enumerate StorageLibrary.Folders for that.
    • If a folder with existing content is added to a library, no change entries are generated for the pre‑existing content; only subsequent changes are tracked.
    • Call Enable as soon as tracking starts and before every enumeration of changes to ensure all changes are captured and to avoid ecSearchFolderScopeViolation errors if the user changes library folders while reading.
  8. When to use FileSystemWatcher System.IO.FileSystemWatcher can still be used for real‑time UI updates while the app is running, but it:
    • Can miss events if its internal buffer overflows under heavy change load.
    • Does not track changes while the app is not running.
    • May report some names in 8.3 short format on some systems.
    If background persistence and reliability are required, prefer StorageLibraryChangeTracker as the primary mechanism and optionally combine it with FileSystemWatcher for immediate, in‑session updates.

References:

AI-generated content may be incorrect. Read our transparency notes for more information.

Was this answer helpful?

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.