C ++使用ReadDirectoryChangesW监视文件更改不会触发所有操作?

时间:2017-07-22 17:33:56

标签: c++ createfile readdirectorychangesw

我有一个应用程序,它必须监视特定目录中的任何更改。我使用以下代码,但是当我重命名文件时,不会触发Action FILE_ACTION_RENAMED_NEW_NAME。实际上,UNDISCOVERED ACTION触发了哪些会导致意外行为?我在这里做错了什么?

char Dir[] = "DIRPATH";
HANDLE hDir = CreateFile(
    Dir,
    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];
FILE_NOTIFY_INFORMATION *fni = NULL;

while (TRUE)
{
    //strFileNotifyInfo = NULL;
    DWORD dwBytesReturned = 0;
    if (ReadDirectoryChangesW(hDir, (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL) == 0)
    {
        Exit(GetLastErrorAsString());
    }
    else
    {
        char fileName[MAX_PATH] = ""; 
        DWORD offset = 0;
        do {
            fni = (FILE_NOTIFY_INFORMATION*)(&strFileNotifyInfo[offset]);
            int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName, fni->FileNameLength / sizeof(WCHAR), fileName, sizeof(fileName), NULL, NULL);
            string Test = Dir;
            Test += "\\";
            Test += fileName;
            switch (fni->Action) {
                case FILE_ACTION_ADDED:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory added: " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File added: " << Dir << "\\" << fileName << endl;
                        }
                    break;
                case FILE_ACTION_MODIFIED:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory modified: " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File modified: " << Dir << "\\" << fileName << endl;
                        }
                    break;
                case FILE_ACTION_REMOVED:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory removed: " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File removed: " << Dir << "\\" << fileName << endl;
                        }
                    break;
                case FILE_ACTION_RENAMED_NEW_NAME:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory renamend (NEW): " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File renamed (NEW): " << Dir << "\\" << fileName << endl;
                        }
                    break;
                case FILE_ACTION_RENAMED_OLD_NAME:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory renamed (OLD): " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File renamed (OLD): " << Dir << "\\" << fileName << endl;
                        }
                    break;
                default:
                    if (boost::filesystem::is_directory(Test)) {
                        cout << "Directory UNDISCOVERED ACTION: " << Dir << "\\" << fileName << endl;
                        }
                    else {
                        cout << "File UNDISCOVERED ACTION: " << Dir << "\\" << fileName << endl;
                        }
                    break;
                }
            ::memset(fileName, '\0', sizeof(fileName));
            offset += fni->NextEntryOffset;
            }
        while (fni->NextEntryOffset != 0);
        cout << "Loop: " << nCounter++ << endl;
    }
}

在名为file.txt的地图中将文件从file2.txt重命名为MAP的一些示例输出:

File renamed (OLD): DIRPATH\MAP\file.txt
Directory UNDISCOVERED ACTION: DIRPATH
Loop: 0
Directory modified: DIRPATH\MAP
Loop: 1

1 个答案:

答案 0 :(得分:2)

FILE_NOTIFY_INFORMATION不是固定大小的结构。它表示一个固定大小的标题,后跟文件名 - 一个可变大小的字符串。

&strFileNotifyInfo[1]指向strFileNotifyInfo[0]后面的文件名中间的某个偏移量。它的值只是错误解释的内存块,里面装满了文件名字符;基本上是随机垃圾。

相反,您应该使用FILE_NOTIFY_INFORMATION::NextEntryOffset在缓冲区中找到FILE_NOTIFY_INFORMATION的以下实例。

您的代码看起来像这样:

BYTE buffer[4096];
ReadDirectoryChangesW(hDir, buffer, sizeof(buffer), ...);
BYTE* p = buffer;
for (;;) {
  FILE_NOTIFY_INFORMATION* info =
      reinterpret_cast<FILE_NOTIFY_INFORMATION*>(p);

  // Work with `info` as necessary

  if (!info->NextEntryOffset) break;  // this was last entry
  p += info->NextEntryOffset;
}