«

»

Jun
12
2012

How to Delete Directories Recursively with Win32

Well, while I’m at it, here’s the counterpart to the recursive directory creation function from my last post, a function that recursively deletes a directory and all its contents.

Ordinarily, you could just use the shell API to achieve this on classic Win32:

/// <summary>Deletes a directory and everything in it</summary>
/// <param name="path">Path of the directory that will be deleted</param>
void deleteDirectory(const std::wstring &path) {
  SHFILEOPSTRUCTW fileOperation;
  fileOperation.wFunc = FO_DELETE;
  fileOperation.pFrom = path.c_str();
  fileOperation.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION;

  int result = ::SHFileOperationW(&fileOperation);
  if(result != 0) {
    throw std::runtime_error("Could not delete directory");
  }
}

But WinRT/Metro applications cannot use the shell API and have to do it manually. So here’s a piece of code that takes care of directory deletion using nothing but Win32 API calls that are also available to WinRT/Metro applications:

/// <summary>Automatically closes a search handle upon destruction</summary>
class SearchHandleScope {

  /// <summary>Initializes a new search handle closer</summary>
  /// <param name="searchHandle">Search handle that will be closed on destruction</param>
  public: SearchHandleScope(HANDLE searchHandle) :
    searchHandle(searchHandle) {}

  /// <summary>Closes the search handle</summary>
  public: ~SearchHandleScope() {
    ::FindClose(this->searchHandle);
  }

  /// <summary>Search handle that will be closed when the instance is destroyed</summary>
  private: HANDLE searchHandle;

};

/// <summary>Recursively deletes the specified directory and all its contents</summary>
/// <param name="path">Absolute path of the directory that will be deleted</param>
/// <remarks>
///   The path must not be terminated with a path separator.
/// </remarks>
void recursiveDeleteDirectory(const std::wstring &path) {
  static const std::wstring allFilesMask(L"\\*");

  WIN32_FIND_DATAW findData;

  // First, delete the contents of the directory, recursively for subdirectories
  std::wstring searchMask = path + allFilesMask;
  HANDLE searchHandle = ::FindFirstFileExW(
    searchMask.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0
  );
  if(searchHandle == INVALID_HANDLE_VALUE) {
    DWORD lastError = ::GetLastError();
    if(lastError != ERROR_FILE_NOT_FOUND) { // or ERROR_NO_MORE_FILES, ERROR_NOT_FOUND?
      throw std::runtime_error("Could not start directory enumeration");
    }
  }

  // Did this directory have any contents? If so, delete them first
  if(searchHandle != INVALID_HANDLE_VALUE) {
    SearchHandleScope scope(searchHandle);
    for(;;) {

      // Do not process the obligatory '.' and '..' directories
      if(findData.cFileName[0] != '.') {
        bool isDirectory = 
          ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ||
          ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);

        // Subdirectories need to be handled by deleting their contents first
        std::wstring filePath = path + L'\\' + findData.cFileName;
        if(isDirectory) {
          recursiveDeleteDirectory(filePath);
        } else {
          BOOL result = ::DeleteFileW(filePath.c_str());
          if(result == FALSE) {
            throw std::runtime_error("Could not delete file");
          }
        }
      }

      // Advance to the next file in the directory
      BOOL result = ::FindNextFileW(searchHandle, &findData);
      if(result == FALSE) {
        DWORD lastError = ::GetLastError();
        if(lastError != ERROR_NO_MORE_FILES) {
          throw std::runtime_error("Error enumerating directory");
        }
        break; // All directory contents enumerated and deleted
      }

    } // for
  }

  // The directory is empty, we can now safely remove it
  BOOL result = ::RemoveDirectory(path.c_str());
  if(result == FALSE) {
    throw std::runtime_error("Could not remove directory");
  }
}

This code is free for the taking and you can use it however you want.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1 comment

  1. Ariel says:

    Thank you! This is (almost) exactly what I needed.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Please copy the string cp1pY5 to the field below:

Social Widgets powered by AB-WebLog.com.