如何删除由ReadDirectoryChangesW监视的孩子的父母

时间:2018-08-17 20:38:08

标签: c++ winapi visual-c++ readdirectorychangesw change-notification

使用ReadDirectoryChangesW监视文件夹会导致其父文件夹被锁定并且无法删除。

这里有关于此的帖子:

FindFirstChangeNotification locks parent folder

但是其中提到的唯一解决方案是,我们应该始终在顶层进行聆听。

有没有人找到一种更好的方法来代替顶级的观看呢?

有时候这可以一直到观看云端硬盘,并且在计算机上不会花费很多处理时间。

谢谢!

2 个答案:

答案 0 :(得分:1)

文件夹只有在为空的情况下才能被删除,否则我们将收到错误STATUS_DIRECTORY_NOT_EMPTY-表示尝试删除的目录不为空。

从另一面看-如果您有打开文件的句柄-除非将其关闭,否则不能将其删除(此处的某些更改从win10 rs1 开始)

因此,如果您用ReadDirectoryChangesW监视某些子文件夹,则您已打开了该文件的句柄,并且在不关闭该句柄之前,不能删除父文件(在WIN10_RS1 之前)。 / p>

通常的过程是-当有人尝试删除文件夹时-它必须枚举其中的所有文件(子文件夹)并首先将其删除。当将删除操作应用于调用了ReadDirectoryChangesW的文件夹时-io请求将以状态STATUS_DELETE_PENDING完成-已请求对未删除的文件对象执行非关闭操作。 (它转换为win32错误代码ERROR_ACCESS_DENIED-访问被拒绝。)。当您从ReadDirectoryChangesW收到此错误时,必须关闭此调用中使用的目录句柄。然后是加薪-首先是谁-您关闭目录句柄或其他代码尝试删除父文件夹...


从win10 rs1开始可能删除父文件,即使有人通过用NtSetInformationFile调用FileDispositionInformationEx或用SetFileInformationByHandle调用FileDispositionInfoEx对其子文件(文件夹)保持打开句柄。

新标记FILE_DISPOSITION_POSIX_SEMANTICS中的魔术(指定系统应执行POSIX风格的删除

  

通常,直到所有   文件的打开句柄已关闭,并且链接计数   文件为零。将文件标记为删除时   FILE_DISPOSITION_POSIX_SEMANTICS,该链接将从   POSIX删除句柄关闭后立即可见的名称空间,   但是文件的数据流仍然可以被其他现有用户访问   直到最后一个手柄已关闭。

因此,当我们使用此选项时-当然,文件本身不会被删除,直到ReadDirectoryChangesW的调用者不会关闭自身句柄,但是文件将从父文件夹中删除。结果父文件夹可以变成空,然后我们可以将其删除。

请注意,此处的DeleteFileWRemoveDirectoryW在这里不起作用,因为它们将旧的信息类FileDispositionInformationFILE_DISPOSITION_INFORMATION一起使用

ULONG DeletePosix(PCWSTR lpFileName)
{
    HANDLE hFile = CreateFileW(lpFileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 
        FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    static FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE| FILE_DISPOSITION_POSIX_SEMANTICS };

    ULONG dwError = SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi, sizeof(fdi)) 
        ? NOERROR : GetLastError();

    // win10 rs1: file removed from parent folder here
    CloseHandle(hFile);

    return dwError;
}

当然,子级必须在其他调用中以FILE_SHARE_DELETE打开,否则我们以后根本无法通过DELETE访问来打开它

答案 1 :(得分:0)

获取目录句柄时,为CreateFile()指定正确的属性很重要。试试这个:

HANDLE hDir = ::CreateFile(
    strDirectoryName,
    FILE_LIST_DIRECTORY,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
    NULL, // security descriptor
    OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
    NULL);

对于共享模式,也必须指定FILE_SHARE_DELETE