我编写了一个使用WIN32 api创建临时目录层次结构的应用程序。现在,当想要在关闭应用程序时删除目录时,我遇到了一些问题。
所以假设我有一个目录层次结构:C:\ temp \ directory \ subdirectory \
我正在使用这个递归函数:
bool Dir::deleteDirectory(std::string& directoryname, int flags)
{
if(directoryname.at(directoryname.size()-1) != '\\') directoryname += '\\';
if ((flags & CONTENTS) == CONTENTS)
{
WIN32_FIND_DATAA fdata;
HANDLE dhandle;
directoryname += "\\*";
dhandle = FindFirstFileA(directoryname.c_str(), &fdata);
// Loop through all the files in the main directory and delete files & make a list of directories
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
std::string filename = fdata.cFileName;
if(filename.compare("..") != 0)
{
std::string filelocation = directoryname.substr(0, directoryname.size()-2) + StringManip::reverseSlashes(filename);
// If we've encountered a directory then recall this function for that specific folder.
if(!isDirectory(filelocation)) DeleteFileA(filename.c_str());
else deleteDirectory(filelocation, DIRECTORY_AND_CONTENTS);
}
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
directoryname = directoryname.substr(0, directoryname.size()-2);
}
if ((flags & DIRECTORY) == DIRECTORY)
{
HANDLE DirectoryHandle;
DirectoryHandle = CreateFileA(directoryname.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
bool DeletionResult = (RemoveDirectoryA(directoryname.c_str()) != 0)?true:false;
CloseHandle(DirectoryHandle);
return DeletionResult;
}
return true;
}
此函数迭代临时目录的目录内容;并且对于临时目录中的每个目录,它会一直调用自己,直到它位于最低目录;示例中的子目录。
还定义了3个标志
enum DirectoryDeletion
{
CONTENTS = 0x1,
DIRECTORY = 0x2,
DIRECTORY_AND_CONTENTS = (0x1 | 0x2)
};
使用此功能时,它只删除最低的子目录,我无法删除层次结构中较高的子目录,因为它表示该目录不为空。当我去查看目录'子目录'时,仅在应用程序结束后删除。但是,当我尝试将其封装在非递归的简单主应用程序中时,我完全没有删除目录的问题。
答案 0 :(得分:11)
有一个Windows API,SHFileOperation,它将为您执行递归文件夹删除。
LONG DeleteDirectoryAndAllSubfolders(LPCWSTR wzDirectory)
{
WCHAR szDir[MAX_PATH+1]; // +1 for the double null terminate
SHFILEOPSTRUCTW fos = {0};
StringCchCopy(szDir, MAX_PATH, wzDirectory);
int len = lstrlenW(szDir);
szDir[len+1] = 0; // double null terminate for SHFileOperation
// delete the folder and everything inside
fos.wFunc = FO_DELETE;
fos.pFrom = szDir;
fos.fFlags = FOF_NO_UI;
return SHFileOperation( &fos );
}
答案 1 :(得分:8)
您没有从所有FindFirstFile
次调用中关闭dhandle,因此当您尝试删除它时,每个目录都会引用它。
而且,为什么需要创建DirectoryHandle
?它不是必需的,也可能会阻止删除目录。
当你的应用关闭时,这些句柄被强制关闭,(我猜)最后一次尝试删除然后成功。
答案 2 :(得分:6)
好吧,我在这段代码中发现了几个错误..这就是我找到的
bool Dir::deleteDirectory(std::string& directoryname, int flags)
{
if(directoryname.at(directoryname.size()-1) != '\\') directoryname += '\\';
if ((flags & CONTENTS) == CONTENTS)
{
WIN32_FIND_DATAA fdata;
HANDLE dhandle;
//BUG 1: Adding a extra \ to the directory name..
directoryname += "*";
dhandle = FindFirstFileA(directoryname.c_str(), &fdata);
//BUG 2: Not checking for invalid file handle return from FindFirstFileA
if( dhandle != INVALID_HANDLE_VALUE )
{
// Loop through all the files in the main directory and delete files & make a list of directories
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
std::string filename = fdata.cFileName;
if(filename.compare("..") != 0)
{
//BUG 3: caused by BUG 1 - Removing too many characters from string.. removing 1 instead of 2
std::string filelocation = directoryname.substr(0, directoryname.size()-1) + filename;
// If we've encountered a directory then recall this function for that specific folder.
//BUG 4: not really a bug, but spurious function call - we know its a directory from FindData already, use it.
if( (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
DeleteFileA(filelocation.c_str());
else
deleteDirectory(filelocation, DIRECTORY_AND_CONTENTS);
}
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
directoryname = directoryname.substr(0, directoryname.size()-2);
//BUG 5: Not closing the FileFind with FindClose - OS keeps handles to directory open. MAIN BUG
FindClose( dhandle );
}
}
if ((flags & DIRECTORY) == DIRECTORY)
{
HANDLE DirectoryHandle;
DirectoryHandle = CreateFileA(directoryname.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
//BUG 6: Not checking CreateFileA for invalid handle return.
if( DirectoryHandle != INVALID_HANDLE_VALUE )
{
bool DeletionResult = (RemoveDirectoryA(directoryname.c_str()) != 0)?true:false;
CloseHandle(DirectoryHandle);
return DeletionResult;
}
else
{
return true;
}
}
return true;
}
答案 3 :(得分:6)
SHFileOperations在Windows 7上运行良好。实际上在IFileOperation文档中说
IFileOperation只能应用于单线程单元(STA)情况。它不能用于多线程公寓(MTA)情况。对于MTA,您仍然必须使用SHFileOperation。
但是我的SHFileOperations问题是它似乎不支持超过260个字符的路径,并且不支持长文件名的\?\前缀。
这真是一个痛苦......但是如果你想要处理超过260个字符的路径(NTFS支持的路径 - 但不是Windows资源管理器,命令提示命令等),仍然需要一个递归函数。
答案 4 :(得分:1)
尝试调用FindClose来关闭FindFileFileA返回的句柄。
答案 5 :(得分:1)
我的FindClose
没有dhandle
。打开句柄意味着该目录仍在使用中。
MSDN说:“当不再需要搜索句柄时,请使用FindClose
函数关闭它,而不是CloseHandle
。”
(CloseHandle
似乎更适合DirectoryHandle
向下,但不适用于查找循环中使用的dhandle
。)
答案 6 :(得分:0)
主要问题已经得到解答,但这是我注意到的事情。您的主while
循环对我来说似乎有点脆弱......
while(true)
{
if(FindNextFileA(dhandle, &fdata))
{
//...
} else if(GetLastError() == ERROR_NO_MORE_FILES) break;
}
如果FindNextFile
结束,则结束,因为目录中没有更多文件。但是,如果因其他原因而结束呢?如果出现异常情况,似乎最终会出现无限循环。
我认为如果FindNextFile
因任何原因而失败,那么您将需要停止循环并开始通过递归调用返回。因此,我建议您只需删除GetLastError
测试,然后将其设为“else break;
”
实际上,经过一段时间的考虑,我可能会把它简化为:
while(FindNextFileA(dhandle, &fdata))
{
//...
}