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

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

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

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

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

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

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

Спасибо!

2 ответа

Папка может быть удалена только в том случае, если она пуста, в противном случае мы получили ошибку 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 доступ позже

Важно указать правильные атрибуты для 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 а также для режима обмена.

Другие вопросы по тегам