Как удалить родителя ребенка, за которым следит ReadDirectoryChangesW

Мониторинг папки с помощью ReadDirectoryChangesW приводит к тому, что ее родитель блокируется и не может быть удален.

Здесь есть пост об этом:

FindFirstChangeNotification блокирует родительскую папку

но единственное решение, упомянутое в этом, состоит в том, что мы должны всегда слушать на высшем уровне.

Кто-нибудь нашел лучший способ сделать это вместо того, чтобы смотреть на верхнем уровне?

Иногда это может привести к просмотру диска, и это не может занять много времени на машине.

Спасибо!

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 а также для режима обмена.

0

Другие решения

Папка может быть удалена только в том случае, если она пуста, в противном случае мы получили ошибку STATUS_DIRECTORY_NOT_EMPTYУказывает, что каталог, пытающийся быть удаленным, не является пустым.

с другой стороны — если у вас есть открывающий дескриптор для файла — его нельзя удалить, пока вы не закроете его дескриптором (что-то здесь изменилось начинаются с win10 rs1)

так что если вы контролируете какую-то дочернюю подпапку с ReadDirectoryChangesW вы открыли ручку к нему, а родитель не может (до WIN10_RS1) удаляться, пока вы не закроете эту ручку.

В общем случае процесс выглядит так: когда кто-то пытается удалить папку, он должен перечислить все файлы (подпапки) внутри нее и сначала удалить ее. когда операция удаления будет применена к папке, для которой 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 не закрывайте ручку самостоятельно, но файл будет удален из родительской папки. в результате родительская папка может стать пустой, после чего мы можем ее удалить.

Обратите внимание, что 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;
}

и, конечно, ребенок должен быть открыт с FILE_SHARE_DELETE в других звонках, иначе мы просто не сможем открыть его DELETE доступ позже

0