Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我在递归删除目录时遇到问题?_C++_Winapi_Recursion_Directory - Fatal编程技术网

C++ 为什么我在递归删除目录时遇到问题?

C++ 为什么我在递归删除目录时遇到问题?,c++,winapi,recursion,directory,C++,Winapi,Recursion,Directory,我编写了一个应用程序,它使用WIN32 api创建临时目录层次结构。现在,当我想在关闭应用程序时删除目录时,我遇到了一些问题 假设我有一个目录层次结构:C:\temp\directory\subdirectory\ 我正在使用这个递归函数: bool Dir::deleteDirectory(std::string& directoryname, int flags) { if(directoryname.at(directoryname.size()-1) != '\\')

我编写了一个应用程序,它使用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;
}
此函数迭代临时目录的目录内容;对于temp目录中的每个目录,它不断地调用自己,直到它位于最低目录;示例中的子目录

还定义了3个标志

 enum DirectoryDeletion
 {
    CONTENTS = 0x1,
    DIRECTORY = 0x2,
    DIRECTORY_AND_CONTENTS = (0x1 | 0x2)
 };

当使用这个函数时,它只删除最低的子目录,而我不能删除层次结构中较高的子目录,因为它说目录不是空的。当我去查看目录时,“subdirectory”仅在应用程序结束后才被删除。然而,当我试图将其封装在一个非递归的简单主应用程序中时,我完全没有删除目录的问题

您没有从所有那些
FindFirstFile
调用中关闭dhandle,因此当您尝试删除它时,每个目录都有一个对它的引用

还有,为什么需要创建
DirectoryHandle
?它不是必需的,并且可能还会阻止目录删除


当你的应用程序关闭时,这些句柄将被强制关闭,并且(我猜)最后一次尝试的删除将成功。

尝试调用FindFilea返回的关闭句柄。

我没有看到你的
数据句柄的
FindClose
。打开的句柄表示目录仍在使用中

说明:“当不再需要搜索句柄时,请使用
FindClose
功能而不是
CloseHandle
关闭它。”


CloseHandle
似乎适用于下面的
DirectoryHandle
,但不适用于Find循环中使用的
dhandle

嗯,我在这段代码中发现了几个错误。。这是我发现的

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;
}

主要问题已经得到了回答,但我注意到了一点。你的主
,而
循环对我来说似乎有点脆弱

while(true)
{
     if(FindNextFileA(dhandle, &fdata))
     {
         //...
     } else if(GetLastError() == ERROR_NO_MORE_FILES)    break;
}
如果
FindNextFile
结束,则此操作将结束,因为目录中没有其他文件。但如果它因为其他原因而结束呢?如果发生异常情况,您可能会以无限循环结束

我认为如果
FindNextFile
由于任何原因失败,那么您应该停止循环并开始通过递归调用返回。因此,我建议只需删除
GetLastError
测试,然后让它“
else break;


事实上,经过片刻思考,我可能会将其简化为:

while(FindNextFileA(dhandle, &fdata))
{
    //...
}

有一个WindowsAPI,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 );
}

SHFileOperations在Windows7上运行良好。事实上,文件上说

IFileOperation只能应用于单线程单元(STA)情况。它不能用于多线程单元(MTA)情况。对于MTA,您仍然必须使用SHFileOperation

但是,我对SHFileOperations的问题是,它似乎不支持长度超过260个字符的路径,并且不支持长文件名的\?\前缀


这是一个真正的难题……但是如果您想要处理长度超过260个字符的路径(NTFS支持,但不支持Windows资源管理器、命令提示符命令等)

确实,忘记FindClose吧。非常感谢。当我读到你的主题行时,我以为你在试图删除win32目录…警告:这在Vista+上不起作用。是的,MSDN文档中说它只被IFileOperation“取代”,但实际上,Vista和7上的fou DELETE被破坏了。对不起,我不同意。我刚刚在Win7上编译了上面的代码,并调用:DeleteDirectAndAllSubfolders(L“D:\\somefolder”)进行了测试,效果非常好。现在我记得当我第一次研究这个问题时,它可能不适用于XP。但我现在在文档中没有看到任何建议。如果目录正在使用(例如,在该文件夹中有一个打开的shell文件夹和/或带有curdir的dos提示符),则该操作将失败