Why does saving a file in Notepad fire multiple FindFirstChangeNotification events?

Many people have noticed that the Read­Directory­ChangesW and Find­First­Change­Notification functions (and therefore their BCL equivalent File­System­Watcher and WinRT equivalent Storage­Folder­Query­Result) fire multiple FILE_ACTION_MODIFIED events when you save a file in Notepad. Why is that?

Because multiple things were modified.

Notepad opens the file for writing, writes the new data, calls Set­End­Of­File to truncate any excess data (in case the new file is shorter than the old file), then closes the handle. Two things definitely changed, and a third thing might have changed.

  • The file last-modified time definitely changed.
  • The file size definitely changed.
  • The file last-access time might have changed.

It's therefore not surprising that you got two events, possibly three.

Remember the original design goals of the Read­Directory­ChangesW function: It's for letting an application cache a directory listing and update it incrementally. Given these design goals, filtering out redundant notifications in the kernel is not required aside from the performance benefits of reduced chatter. In theory, Read­Directory­ChangesW could report a spurious change every 5 seconds, and the target audience for the function would still function correctly (albeit suboptimally).

Given this intended usage pattern, any consumer of Read­Directory­ChangesW needs to accept that any notifications you receive encompass the minimum information you require in order to keep your cached directory information up to date, but it can contain extra information, too. If you want to respond only to actual changes, you need to compare the new file attributes against the old ones.

Bonus chatter: Actually, the two things that changed when Notepad set the file size are the allocation size and the file size (which you can think of as the physical and logical file sizes, respectively). Internally, this is done by two separate calls into the I/O manager, so it generates two change notifications.

Comments (9)
  1. Electron Shephers says:

    Surely it should be:

    • The file last-modified time definitely changed.

    • The file size might have changed.

    • The file last-access time definitely changed.

  2. kinokijuf says:

    @Electron starting with Vista, the last-access time is no longer updated.

  3. Electron Shepherd says:

    It was more the "file size has definitely changed" part. Whether the size changes or not depends on the edits made – there's no "definitely" about it.

  4. Cesar says:

    There's no "definitely" about the last-modified time too. If the last-modified time on the filesystem was in the future, and you time the click on the save button just right, you could in theory get the same last-modified time.

    (Raymond, sorry for contributing to the nitpicking. I will shut up now.)

  5. guest2014 says:

    Maybe by change he means a write. Even an update by the same value is also a change, in this context.

    [That's what I meant, yes. The file system change notification does not check whether a write was equal to the previous data. (This gets really expensive for large file writes.) Remember the purpose of change notifications. Spurious notifications are legal. -Raymond]
  6. morlamweb says:

    @kinokijuf: The reason why the last-accessed time "might have" changed is that it's a configurable option in Windows.  Sure, it's off by default in modern versions, but it's possible to enable it even in Windows 7 & 8.  Defrag Tools on Channel9 recently did an episode on last-access time and symbols.  Look for it on the Channel9 website; or, lookup the documentation for fsutil.

  7. Mike Dimmick says:

    Even in older versions of Windows, which did have last-accessed modification turned on by default, NTFS wouldn't update it if it had been updated less than an hour ago. Given the design goals, I would expect the notification to only indicate the last-access change if NTFS had stored a new value, not for every actual access.

    This API is maddening if you're trying to write an event-driven service that detects the presence of a new file to operate on. You want to be told when the writer has finished writing. Let's say some generic process that you can't control is creating the file, such as copying a file into a folder through Windows Explorer or uploading via FTP, so you can't get a notification from the writing process. The FileSystemWatcher API does not tell you that. It tells you that the name was created when the file is opened. It tells you that the modified time has changed, but it's not entirely clear from the documentation whether that is a valid signal that the process has finished writing to the file (since NTFS only updates modified time when you close the file) and it mentions deferring the notification until the cache is sufficiently flushed. This leaves the developer handling the creation notification by polling, attempting to open the file for reading and backing off on failure.

  8. @Mike Dimmick: To be fair, there's no good way for a filesystem monitor API to really notify you that a new file has been created and can be safely opened.  Such an event would be subject to many race conditions and sloppy programming: another program may open the file, a virus scanner could kick in, the security ACLs could be changed, the network could become unavailable, etc.  The best way in any case is to attempt to open, gracefully handle any failures, and try again if necessary.  That's what you should do any time you're working with files.

  9. Joshua Ganes says:

    For the first time since I've become a regular reader, you've written about a topic that I had just been learning myself. I implemented a system to to monitor file changes in a failed attempt to achieve my goals as described in my latest blog post: amish-programmer.blogspot.ca/…/desperate-measures-time-to-hack.html

    I had to discover the properties described in this post for myself. Perhaps next time you could schedule relevant posts ahead of my working on related projects ;)

Comments are closed.