文件夹只有在空的情况下才能删除,否则会报错STATUS_DIRECTORY_NOT_EMPTY - 表示要删除的目录不为空。
从另一边 - 如果你有打开文件的句柄 - 在你不关闭它之前不能删除它句柄(这里的一些变化从 win10 rs1 开始)
因此,如果您使用ReadDirectoryChangesW 监视某些子文件夹,则您已经打开了它的句柄,并且在您不关闭此句柄之前不能(在 WIN10_RS1 之前)删除父文件夹。
一般过程看起来像 - 当有人尝试删除文件夹时 - 它必须枚举其中的所有文件(子文件夹)并首先删除它。当删除操作将应用于调用 ReadDirectoryChangesW 的文件夹时 - io 请求将以状态 STATUS_DELETE_PENDING 完成 - 已请求对具有删除挂起的文件对象的非关闭操作。 (它转换为 win32 错误代码 ERROR_ACCESS_DENIED - 访问被拒绝。)。当您从ReadDirectoryChangesW 收到此错误时,您必须关闭此调用中使用的目录句柄。然后是 raise - 谁是第一个 - 你关闭目录句柄或其他代码尝试删除父文件夹...
从 win10 rs1 开始可能删除父级,即使有人通过调用NtSetInformationFile 和FileDispositionInformationEx 或SetFileInformationByHandle 和FileDispositionInfoEx 来持有它的子文件(文件夹)的打开句柄。
新标志 FILE_DISPOSITION_POSIX_SEMANTICS 中的神奇之处(指定系统应该执行 POSIX 样式的删除)
通常,标记为删除的文件直到全部删除后才会真正删除
文件的打开句柄已关闭,并且该文件的链接计数
文件为零。使用标记删除文件时
FILE_DISPOSITION_POSIX_SEMANTICS,链接从
POSIX 删除句柄关闭后可见的命名空间,
但是文件的数据流仍然可以被其他现有的
句柄,直到最后一个句柄被关闭。
所以当我们使用这个 - 文件本身当然不会被删除,直到ReadDirectoryChangesW的调用者不关闭自己的句柄,但文件将从父文件夹中删除。结果父文件夹可以变成空的,之后我们可以删除它。
注意这里的DeleteFileW和RemoveDirectoryW在这里是行不通的,因为他们使用了旧的信息类FileDispositionInformation和FILE_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;
}
当然child必须在其他调用中用FILE_SHARE_DELETE打开,否则我们以后根本无法用DELETE打开它