Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ WinApi: ReadDirectoryChangesW() Receiving Double Notifications

I try to understand ReadDirectoryChangesW function so I can be effectively informed on change of content in several directories (files overwritten, files deleted, renamed, etc..).

One of my recent observations is, that for every file write operation, I receive always two notifications for single file.

I traced that very carefully, and I am sure, that if I overwrite a file (say a .txt file with a new content - basically couple of extra letters inside), ReadDirectoryChangesW() notifies me two times per that file save.

This is serious thing, as I expect to be notified only once per change. I do not wish to unintentionally repeat operations that should happen only once in my application.

Is this behavior known? Is there a way to receive only one notification per one change, please? Is there way to effectively avoid double notifications?

I use:

  • Unmanaged C++
  • Visual Studio 2012
  • Windows 7 x64

I use pretty basic code to do my tests, but you will want to see it, so here it is:

HANDLE hDir = CreateFile(
    lpDir,
    FILE_LIST_DIRECTORY,
    FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
    NULL, 
    OPEN_EXISTING, 
    FILE_FLAG_BACKUP_SEMANTICS, 
    NULL);

    int nCounter = 0;
    FILE_NOTIFY_INFORMATION strFileNotifyInfo[1024];
    DWORD dwBytesReturned = 0;   

    while(TRUE)
    {
        if( ReadDirectoryChangesW ( hDir, (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL) == 0)
        {
            ErrorCheck(_T("Reading Directory Change"));
        }
        else
        {
            _tcout << _T("File Modified: ") << strFileNotifyInfo[0].FileName << endl;
            _tcout << _T("Loop: ") << nCounter++ << endl;
        }
    }
like image 441
Bunkai.Satori Avatar asked Dec 26 '12 05:12

Bunkai.Satori


1 Answers

ReadDirectoryChangesW() has a very myopic view of the file system. It sees every change to the file system and dutifully reports them. And yes, there is often more than one when you write to a file. It is an implementation detail of the particular file system you are using, but any common one in Windows also keeps a directory entry for a file that stores metadata for the file.

So you see the write for the file data. But you also see it changing the directory entry. In particularly the file size, likely to change when you write a file and add data to the file. And the last-write and last-access timestamps recorded in the directory entry. The api is otherwise blind to the kind of change being made, it only sees the low-level write. It is also completely unaware of what particular process asked for the write.

This is something you will have to deal with, there is no way to distinguish these writes. All that you know is "the file was changed". How, why, by whom and how often is entirely undiscoverable.

Something else you will have to deal with is that, at the time the notification is generated, the process that writes the file is very likely to still have a lock on the file. Which prevents you from doing anything useful with the file yourself. Like reading the file or copying it is likely to fail. You have to wait until the process is done with the file and has closed its handle to the file. There's no way to discover this, other than by trying to open the file yourself and deny any sharing. This requires a timer, periodically trying to acquire a lock on the file yourself. Once you got that plumbing in place, getting more than one change notification for the file doesn't matter anymore.

like image 100
Hans Passant Avatar answered Sep 20 '22 22:09

Hans Passant