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) {
std::vector doubleNullTerminatedPath;
std::copy(path.begin(), path.end(), std::back_inserter(doubleNullTerminatedPath));
doubleNullTerminatedPath.push_back(L'\0');
doubleNullTerminatedPath.push_back(L'\0');
SHFILEOPSTRUCTW fileOperation;
fileOperation.wFunc = FO_DELETE;
fileOperation.pFrom = &doubleNullTerminatedPath[0];
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;
// We'll iterate over the direct contents of the directory in any order
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
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 entry 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 = ::RemoveDirectoryW(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.