FindFirstFile / FindNextFile不返回文件夹中的所有文件

注意:build议作为重复的问题讨论CreateFileERROR_FILE_NOT_FOUND ,在文件上具有现有句柄,并标记稍后要删除的文件。 虽然这是一个类似的话题,但这些问题都不涉及我的案子。

我有以下方法来删除一个目录。 我一直在使用它一段时间没有问题。

但最近,我真的一直在通过它的步伐,通过一个networkingpath(只是连接到我的路由器的USB驱动器)删除目录和文件的大任务。

除了一个不删除目录中的所有文件的区域,一切似乎都很好,因此RemoveDirectory()失败。 (我已经将每个文件添加到列表中,并validation该列表不包含未被删除的文件。)

所有文件的命名非常相似,没有返回或删除的文件的名称没有任何exception。 如果我再次运行程序,它将删除剩余的文件,然后在另一个目录稍后有相同的错误。

 bool CBackupWorker::DeleteDirectory(LPCTSTR lpszName) { if (!DirectoryExists(lpszName)) { ASSERT(false); // Unexpected return true; } CFileFind find; BOOL bContinue = find.FindFile(AppendPath(lpszName, _T("*"))); while (bContinue) { bContinue = find.FindNextFile(); if (find.IsDirectory()) { if (!find.IsDots()) { if (!DeleteDirectory(find.GetFilePath())) return false; } } else { if (find.IsReadOnly()) ClearReadOnlyAttribute(find); if (!::DeleteFile(ConvertToExtendedLengthPath(find.GetFilePath()))) { LogErrorV(::GetLastError(), _T("ERROR DELETING FILE : '%s'"), (LPCTSTR)find.GetFilePath()); return false; } } } CString sPath = ConvertToExtendedLengthPath(lpszName); DWORD dwAttributes = ::GetFileAttributes(sPath); if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_READONLY)) ::SetFileAttributes(sPath, dwAttributes & (DWORD)~FILE_ATTRIBUTE_READONLY); if (!::RemoveDirectory(sPath)) { LogErrorV(::GetLastError(), _T("ERROR DELETING DIRECTORY : '%s'"), lpszName); return false; } return true; } 

关于代码的一些注释: ConvertToExtendedLengthPath()添加一个前缀,以便允许超过MAX_PATHpath; 但是,尽pipe这些名称相当长,但是没有一个超过MAX_PATH 。 (在这种情况下,该方法只是返回input值。)

此外,我删除具有此属性的文件和目录的只读属性。 但是,我再次证实,这不会影响我正在使用的任何文件。

最后,这不是我正在使用的文件正在改变的情况。 我是唯一一个可以访问这个外部驱动器的人。

有没有人看到任何情况下FindFirstFile / FindNextFile将错过几个文件? 或者,通过networking共享访问文件会干扰这些function的行为?

Solutions Collecting From Web of "FindFirstFile / FindNextFile不返回文件夹中的所有文件"

尽管Windows文件系统和SMB的Windows实现都确保文件不会被排除在目录枚举之外,即使目录的内容正在改变,这似乎也不是SMB协议本身的要求。 (尽管我并不是专家,但是我可能忽略了一些东西。)无论如何,无论服务器的行为是否在技术上是正确的,您都可能需要按原样处理它。

我猜猜shell API已经处理了这种情况,但在你的情况下,我会建议不要使用它,因为我不相信它支持长路径名称。

如果你知道在任何给定的目录中永远不会有太多的条目,并且没有其他进程会在你列举它的同时删除目录中的文件,那么你可能首选读取文件列表然后开始删除它们。 我收集你已经建立了这样一个概念验证。

时间效率稍低,但更具有内存效率,我认为更可靠的方法是同时枚举和删除文件(已经在发布的代码中已经显示),然后循环并重新枚举,直到找到该目录是空的除了...条目。 唯一明显的缺点是对服务器的额外往返。 因人而异。 🙂

我个人没有在使用FindFirstFile / FindNextFile组合时观察到这种奇怪的行为。

但是,如果你想删除一个目录及其内容,这里有一个解决方案:

 // VadaPoché_SO.cpp : Defines the entry point for the console application. #include "stdafx.h" #include <iostream> #include <strsafe.h> #include <Shobjidl.h> HRESULT CreateAndInitializeFileOperation(REFIID riid, void **ppv) //this function is copied verbatim from https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/winui/shell/appplatform/fileoperations/FileOperationSample.cpp { *ppv = NULL; // Create the IFileOperation object IFileOperation *pfo; HRESULT hr = CoCreateInstance(__uuidof(FileOperation), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo)); if (SUCCEEDED(hr)) { // Set the operation flags. Turn off all UI // from being shown to the user during the // operation. This includes error, confirmation // and progress dialogs. hr = pfo->SetOperationFlags(FOF_NO_UI); if (SUCCEEDED(hr)) { hr = pfo->QueryInterface(riid, ppv); } pfo->Release(); } return hr; } int main() { using namespace std; const wchar_t *dirFullPath = L"C:\\test1\\test"; //this directory, and its contents, if any, will be deleted. IShellItem* itemDirToDelete = NULL; IFileOperation *fileOp = NULL; HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hr)) { cout << "CoInitializeEx failed. Error code returned: 0x" << hex << hr << endl; return -1; } if (FAILED(SHCreateItemFromParsingName(dirFullPath, NULL, IID_PPV_ARGS(&itemDirToDelete)))) { cout << "SHCreateItemFromParsingName failed. Error code returned: 0x" << hex << hr << endl; return -1; } if (SUCCEEDED(CreateAndInitializeFileOperation(IID_PPV_ARGS(&fileOp)))) { //Note: contrary to its name DeleteItem, this does NOT do the actual deletion. //It only declares an intention to perform deletion. if (SUCCEEDED(fileOp->DeleteItem(itemDirToDelete, NULL))) { hr = fileOp->PerformOperations(); //This is the statement that acts on the intention declared above. ie it deletes the folder. } fileOp->Release(); } return 0; }