作为一个有着好几年工作经验的programmer,自己对一些简单功能的实现往往很自信,比如这次“删除一个非空文件夹”。

心中的方案:
1.windows api中的DeleteFile,RemoveDirectory.代码如下:

bool APIDeleteFolder(LPCTSTR folderpath)
{
  /**路径合法性验证**/
  ........

  CString strFilePath(folderpath);
  strFilePath = strFilePath + _T("*.*");

  CFileFind finder;
  bool bRet = finder.FindFile(strFilePath);
  while (bRet)
  {
    bRet = finder.FindNextFile();
    /*跳过当前目录“.”和上一级目录“..”*/
    if (finder.IsDots())
    {
      continue;
    }

    CString strSubFile = finder.GetFilePath();
    if (finder.IsDirectory())
    {
      if (!APIDeleteFolder(strSubFile))
      {
        finder.Close();
        return FALSE;
      }
    }
    else
    {
      if (!DeleteFile(strSubFile))
      {
        LOG4CXX_WARN(g_logger, "DeleteFile failed! path = " << strSubFile << " errorCode = " << GetLastError());
        finder.Close();
        return FALSE;
      }
    }

  }

  finder.Close();
  if(!RemoveDirectory(folderpath))
  {
    LOG4CXX_WARN(g_logger, "RemoveDirectory failed! path = " << folderpath << " errorCode = " << GetLastError());
    return false;
  }

  return true;
}

2.windows shell编程中的SHFileOperation:代码如下:

bool SHDeleteDirectory( const std::string &path )
  {
    /*确保目录的路径以2个\0结尾*/
    CHAR czFolder[MAX_PATH+1];
    ZeroMemory(czFolder, (MAX_PATH+1)*sizeof(CHAR));
    strcpy(czFolder, path.c_str());
    unsigned int iPathLen = path.length();
    czFolder[iPathLen] = _T('\0');
    czFolder[iPathLen+1] = _T('\0');

    SHFILEOPSTRUCTA FileOp;
    ZeroMemory(&FileOp, sizeof(SHFILEOPSTRUCT));
    FileOp.fFlags |= FOF_SILENT;              /*不显示进度*/
    FileOp.fFlags |= FOF_NOERRORUI;              /*不报告错误信息*/
    FileOp.fFlags |= FOF_NOCONFIRMATION;          /*直接删除,不进行确认*/
    FileOp.hNameMappings = NULL;
    FileOp.hwnd = NULL;
    FileOp.lpszProgressTitle = NULL;
    FileOp.wFunc = FO_DELETE;
    FileOp.pFrom = czFolder;                /*要删除的目录,必须以2个\0结尾*/
    FileOp.pTo = NULL;
    FileOp.fFlags &= ~FOF_ALLOWUNDO;          

    /*删除目录*/
    int ret = SHFileOperationA(&FileOp);
    if (0 != ret)
    {
      LOG4CXX_WARN(g_logger, "SHDeleteDirectory failed! path = " << path << "errorCode = " << ret);
      return false;
    }
    else
      return true;
  }


3.boost库中filesystem中的remove_all:代码如下:


  bool BoostDeleteDirectory( const std::string & path )
  {
    try{
      boost::filesystem::path b_dir_path(path);
      if(boost::filesystem::exists(b_dir_path))
      {
        boost::filesystem::remove_all(b_dir_path);
      }
    }catch(boost::filesystem::filesystem_error & e)
    {
      LOG4CXX_WARN(g_logger,
        "DeleteDirectory "
        << "path1=" << e.path1().directory_string() << " , "
        << "path2=" << e.path2().directory_string() << " , "
        << "what=" << e.what() << " , "
        << "code=" << e.code().value() << ","
        << "codemessage=" << e.code().message() << ""        
        )
      return false;
    }

    return true;
  }

      习惯了进行跨平台的编程,而且想找个代码量小的,于是就使用了boost库remove_all。单元测试完成后集成入程序,...之后交予测试。在测试的过程中偶尔会遇见文件夹删不掉的情况,而且从boost捕获的异常来看是某个文件拒绝访问。于是猜测一定是文件资源被占用了,因为我们这个程序有很多地方对文件进行操作,所以一直怀疑是其他操作模块未释放此文件资源,但是在进行了仔细的代码审查后未发现资源占用问题,再用Filemon和unlock软件查看这个文件均未显示有占用情况(而且手动删除却能正常删除)。于是,开始怀疑boost库的适用问题,于是就写了以上windows api和shell的删除目录实现,经过测试对于DeleteFile来说仍然对这个文件提示拒绝访问,但是用SHFileOperation却删除了这个文件及目录。心中不免产生了大大的疑问,这是怎么回事?

       开始再次仔细查看这几个方法的官方文档。真没看到有任何能够解释这个问题的条目。真没想到啊,竟然要栽在这么一个小功能上了。但是无意的一个右键查看文件属性却让自己彻底崩溃了,“shit”, 这些文件的属性是只读的,修改属性后boost和windows api都能正常删除了。

 通过这么个事,我想说明什么呢?
“有经验”的我们(我们有两三个人一直在寻找导致这个问题出现的原因)会不会有时候在考虑问题时会忽略事物的本质而让自己偏离简单的解决方向呢?