Been working on this for a week now. The goal is to move everything, directories and files, withing a Base directory (Ex. C:\Windows\) to a sub-directory in the base directory (Ex. C:\Windows\System32\drivers\etc\)
Currently, I have the program get all of the directories and sub-directories starting at the base and store it in a vector. Afterwards it uses a range for loop to go through the vector, creates the name of that directory in an a folder I'm using for a backup (_wmkdir), and then checks if there are any files in the directory. If there are files, it moves them to the backup (using SHFILEOPSTRUCT). After backing everything up I am moving all of the old directories to a trash folder (again with SHFILEOPSTRUCT. I've tried FO_DELETE and FO_MOVE, the old directories will be deleted either way).
Now the problem, some directories will fail to be moved to the .trash folder. I don't know why, it's not consistent, and it's not every folder. It does move it but there is still a "ghost" of he old directory. It's not accessible and I can't delete it. If I close the program the ghost directory is gone.
Is there an easier way or is there a fix?
Delete and move operations
Code:
bool explorer_operation(std::wstring pathFrom, std::wstring pathTo, size_t operation, bool allow_undo = false)
{
for (int i = 0; i != 2; ++i) {
pathFrom.push_back('\0');
pathTo.push_back('\0');
}
SHFILEOPSTRUCT fileop;
fileop.hwnd = NULL; // no status display
fileop.wFunc = operation;
fileop.pFrom = pathFrom.c_str();
fileop.pTo = pathTo.c_str();
fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user
if (allow_undo)
fileop.fFlags |= FOF_ALLOWUNDO;
fileop.fAnyOperationsAborted = FALSE;
fileop.lpszProgressTitle = NULL;
fileop.hNameMappings = NULL;
return (SHFileOperation(&fileop) == 0);
}
bool file_move(std::wstring from, std::wstring to)
{
return explorer_operation(from, to, FO_MOVE);
}
bool DeleteDirectory(std::wstring path)
{
return explorer_operation(path, L"", FO_DELETE);
}
and the functions that are backing up the old files
Just a note: sorting the vector does make the paths sort properly for the base directory all the way to the last sub-directory in the base
Code:
C:\Windows\
C:\Windows\System32\
C:\Windows\System32\en-US\
C:\Windows\System32\en-US\Licenses\
C:\Windows\System32\en-US\Licenses\eval\
C:\Windows\System32\en-US\Licenses\eval\Enterprise\
Is how the vector is sorted and then I'm going through it in reverse order.
Code:
void backupLastVersion(const std::wstring &source, const std::wstring &dest, const std::wstring &testDir)
{
std::wcout << "Backing up last version" << std::endl;
vwstr directories, files, dir_to_delete;
const std::set<std::wstring> exclude_files{ L"unrar.dll", L"Installer.log", L"installer.log" };
std::wstring currentDir = format_directory(getWorkingDir());
std::wstring lastVer = currentDir;
size_t start = 0;
if ((start = dest.find_last_of('\\')) != std::wstring::npos) {
lastVer += { dest.begin() + start + 1, dest.end() };
}
listDirectories(directories, testDir);
listFiles(files, testDir);
for (vwstr::size_type i = 0; i != directories.size(); i++) {
listDirectories(directories, directories[i]);
}
std::sort(directories.begin(), directories.end());
for (auto &wstr : directories) {
if (wstr.size() >= testDir.size()) {
std::wstring _dest = format_directory(dest + std::wstring{ wstr.begin() + testDir.size(), wstr.end() });
_wmkdir(_dest.c_str());
if (listFiles(files, wstr)) {
for (auto file : files) {
// Get the current file name
std::wstring current_file{ file.begin() + file.find_last_of('\\') + 1, file.end() };
if (file.size() >= testDir.size() &&
// If the current file path has the last version directory in it, exclude
file.find(lastVer) == std::wstring::npos &&
// If the current file path has .rar in it, exclude
file.find(L".rar") == std::wstring::npos &&
// If the current file name is in the excluded files list, exclude
exclude_files.find(current_file) == exclude_files.end()) {
// Create the destination path
auto _dest = dest + std::wstring{ file.begin() + testDir.size(), file.end() };
file_move(file, _dest);
}
}
files.clear();
}
else {
std::wcout << L"No files found in directory " << wstr << std::endl;
}
if (wstr != (testDir + L"Data") && wstr != (testDir + L"Data\\lib") && wstr != (testDir + L"Data\\lib\\installer") && wstr.find(L"installer") == std::wstring::npos) {
dir_to_delete.push_back(wstr);
wstr = L"";
}
}
}
std::wcout << L"Created all directories" << std::endl;
std::wcout << L"Copied all files" << std::endl;
std::cin.get();
for (auto iter = dir_to_delete.rbegin(); iter != dir_to_delete.rend(); ++iter) {
if (*iter != testDir && !iter->empty()) {
if (!file_move(format_directory(*iter), L"C:\\Users\\Kyle\\Documents\\Visual Studio 2013\\Projects\\C++ Primer Projects\\Debug\\test\\Data\\lib\\installer\\trash\\")) {
std::wcout << L"Error moving directory " << (*iter) << std::endl;
}
/*
if (!DeleteDirectory(format_directory(*iter))) {
std::wcout << L"Error deleting directory " << ((*iter) << std::endl;
}
*/
}
}
std::wcout << L"Moved all directories" << std::endl;
std::cin.get();
std::wcout << L"Backup complete" << std::endl;
}
DWORD listDirectories(vwstr &directories, const std::wstring dir)
{
if (dir == L"") return 0;
// Declare variables, count used for number of directories, hFind for FindFirstFile, data used to store file data
DWORD count = 0;
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA data;
// Find first file in the current working directory, storying data in data as a reference
hFind = FindFirstFile((dir + (dir.back() == '\\' ? L"*" : L"\\*")).c_str(), &data);
// If hFind is not an invalid handle
if (hFind != INVALID_HANDLE_VALUE) {
// Go through each file in the directory
do
{
// If the file attributes is a directory
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Add directory to my directories (std::vector<std::wstring). if (count > 1) because I want to ignore the "." and ".." directories
if (count > 1) {
directories.push_back(dir + (dir.back() == '\\' ? L"" : L"\\") + data.cFileName);
} // end if (count > 1)
// Add 1 to count, used as the return for how many directories found in the current directory
count++;
}
} while (FindNextFile(hFind, &data) != 0);// end while (FindNextFile(hFind, &data) != 0);
}
// Return count of directories found
return (count - 2);
} // end DWORD listDirectories(std::vector<std::wstring>&, cosnt std::wstring)
DWORD listFiles(vwstr &files, const std::wstring dir)
{
if (dir == L"") return 0;
// Declare variables, count used for number of directories, hFind for FindFirstFile, data used to store file data
DWORD count = 0;
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA data;
// Find first file in the current working directory, storying data in data as a reference
hFind = FindFirstFile((dir + (dir.back() == '\\' ? L"*.*" : L"\\*.*")).c_str(), &data);
// If hFind is not an invalid handle
if (hFind != INVALID_HANDLE_VALUE) {
// Go through each file in the directory
do
{
// If the file attributes is a directory
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
files.push_back(dir + (dir.back() == '\\' ? L"" : L"\\") + data.cFileName);
// Add 1 to count, used as the return for how many directories found in the current directory
count++;
}
} while (FindNextFile(hFind, &data) != 0);// end while (FindNextFile(hFind, &data) != 0);
}
// Return count of directories found
return count;
} // end DWORD listFiles(vwstr&, const std::wstring)
I've tried _wrename to move the directories (That completely fails every time) and I've tried doing FO_DELETE and FO_MOVE to get rid of the old unused directories. Files move properly without any problems every time but getting rid of the directories is breaking.