注意:建议重复的问题讨论了CreateFile
,ERROR_FILE_NOT_FOUND
,在文件上有现有句柄,以及标记要在以后删除的文件。虽然这是一个类似的主题,但这些问题都与我的案例无关。
我有以下方法删除目录。我已经使用了一段时间没有问题。
但是最近,我确实通过网络路径删除目录和文件(只是连接到我的路由器的USB驱动器)来完成它的步伐。
除了一个没有删除目录中所有文件的区域外,所有内容似乎都运行良好,因此RemoveDirectory()
失败。 (我已将每个文件添加到列表中并验证该列表不包含未删除的文件。)
所有文件的命名都非常相似,并且没有返回或删除的文件名称没有异常。如果我再次运行程序,它将删除剩余的文件,然后在另一个目录上稍后出现相同的错误。
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_PATH
的路径;但是,尽管这些名称相当长,但它们都不超过MAX_PATH
。 (在这种情况下,该方法只返回输入值。)
此外,我正在删除具有此属性的文件和目录的只读属性。但同样,我已经确认这不会在我正在使用的任何文件上发挥作用。
最后,这不是我正在使用的文件正在发生变化的情况。我是唯一一个可以访问此外部驱动器的人。
有没有人看到FindFirstFile
/ FindNextFile
会遗漏几个文件的情况?或者通过网络共享访问文件可能会干扰这些功能的行为?
答案 0 :(得分:2)
在使用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. i.e. it deletes the folder.
}
fileOp->Release();
}
return 0;
}
答案 1 :(得分:2)
虽然Windows文件系统和SMB的Windows实现都确保files aren't left out of a directory enumeration, even if the contents of the directory are changing,这似乎不是SMB protocol itself的要求。 (但我不是专家,所以我可能忽略了一些东西。)无论如何,无论服务器的行为在技术上是否正确,你都可能需要按原样处理它。
我猜想shell API已经处理了这种情况,但在你的情况下我建议不要使用它,因为我不相信它支持长路径名。
如果您知道任何给定目录中永远不会有过多的条目,并且在您枚举它的同时没有其他进程将删除目录中的文件,您可能更愿意阅读该列表首先是文件,然后开始删除它们。我知道你已经在这些方面建立了一个概念验证。
时间效率稍低但内存效率更高,我认为更强大的方法是同时枚举和删除文件(如已发布的代码中所示),但随后循环并重新枚举,直到除了.
和..
条目之外,您发现该目录为空。唯一明显的缺点是额外的服务器往返。因人而异。 : - )