Windows C ++: ошибка перемещения запрещенной папки

Код ниже — это фрагмент моего проекта, который, помимо прочего:

  1. Получает коллекцию содержимого папки из указанной локальной папки (папка загрузки)
  2. Копирует содержащиеся файлы в удаленную сетевую папку. Если есть подпапки, их файлы копируются, но структура папок не сохраняется.
  3. Перемещает исходные локальные папки в другое локальное расположение (папка «Архив»)

Проблема в том, что я в движении. Если перемещаемая папка содержит одну или несколько вложенных папок, перемещение завершается с ошибкой «Отказано в доступе». Я сузил причину до предыдущего вызова функции, который генерирует список файлов для копирования. Я выделил этот вызов в коде ниже. Это в основном (см. Первый вызов getFolderItemsRecursive ()). Я не понимаю, почему эта рекурсивная функция создает проблемы. Это как если бы, собирая информацию, она каким-то образом помечает что-то как «в использовании». Обратите внимание, что я на самом деле не копирую файлы, так как уже прокомментировал эти строки. Так что это не так.

Для запуска приведенного ниже кода вам нужно создать пару папок где-нибудь и ввести местоположения в коде. Код предполагает c: _UploadTests и c: _archivedtests. Чтобы воспроизвести проблему, создайте по крайней мере одну подпапку со своей собственной подпапкой в ​​_UploadTest. Например. с: _UploadTests \ Foo \ Bar \

#include "windows.h"#include "stdafx.h"#include <cstdint>
#include <vector>

#define RECURSION_DEPTH_NON 0
#define RECURSION_DEPTH_FULL -1

// Object needed bacause WIN32_FIND_DATAA doesn't contain file path data and we need this with each object.
typedef struct fileObject{
std::string path;
WIN32_FIND_DATAA metaData;
} fileObject;

void getFolderItems(std::vector<WIN32_FIND_DATAA>& output, const std::string& path) {
WIN32_FIND_DATAA findfiledata;
HANDLE hFind = INVALID_HANDLE_VALUE;

char fullpath[MAX_PATH];
GetFullPathNameA(path.c_str(), MAX_PATH, fullpath, 0);
std::string fp(fullpath);

hFind = FindFirstFileA((fp + "\\*").c_str(), &findfiledata);
if (hFind != INVALID_HANDLE_VALUE) {
do {
switch (findfiledata.dwFileAttributes) {
case FILE_ATTRIBUTE_DIRECTORY:                  // Its a directory
if (findfiledata.cFileName[0] != '.')
output.push_back(findfiledata);
break;
case FILE_ATTRIBUTE_NORMAL:                     // Its a file
case FILE_ATTRIBUTE_ARCHIVE:
case FILE_ATTRIBUTE_COMPRESSED:
output.push_back(findfiledata);
break;
}
} while (FindNextFileA(hFind, &findfiledata) != 0);
}
}

/// Gets a list of directory contents and their sub folders under a specified path
/// @param[out] output Empty vector to be filled with result
/// @param[in]  path   Input path, may be a relative path from working dir
/// @param[in]  depth  Max depth of recursion relative to 'path'. -1 = full recursion, 0 = non, 1 = allow one level down, etc
void getFolderItemsRecursive(std::vector<fileObject>& output, const std::string& path, int depth) {
std::vector<WIN32_FIND_DATAA> thisLvl;
fileObject fileObj;

// Get all the items from this level folder
getFolderItems(thisLvl, path);

// Loop through the items and dive deeper into any subfolders
for (std::vector<WIN32_FIND_DATAA>::iterator i = thisLvl.begin(); i != thisLvl.end(); ++i) {
// Add the current folder to the object list
fileObj.metaData = *i;
fileObj.path = path;
output.push_back(fileObj);

if (fileObj.metaData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
if (depth == RECURSION_DEPTH_FULL)
getFolderItemsRecursive(output, path + std::string("\\") + fileObj.metaData.cFileName, RECURSION_DEPTH_FULL);
else if (depth > 0)
getFolderItemsRecursive(output, path + std::string("\\") + fileObj.metaData.cFileName, depth--);
}
}
}int main(int argc, char** argv) {
// manually create the list of upload folders
std::vector<std::string> uploadFoldersVector;
uploadFoldersVector.push_back("c:\\_UploadTests");

// For each upload folder, get a list of sub folders and loop through them copying out the files.
for (uint8_t i = 0; i < uploadFoldersVector.size(); i++) {
std::string srcPath;
std::string dstPath;
BYTE flags;

// Get a list of this folders contents
std::vector<fileObject> uploadFiles;

/*********************
The function below seems to be the culprit when called with RECURSION_DEPTH_FULL - look in all subfolders and their children.
A similar call but with RECURSION_DEPTH_NON doesn't cause any issues.
**********************/
getFolderItemsRecursive(uploadFiles, uploadFoldersVector[0], RECURSION_DEPTH_FULL);

// For each file object, copy it to the network archive.
// Removed for StackOverflow for simplicity - adds nothing to the problem definition

// Finally move this folder into the local archive folder
// Get a list of immediate sub folders
uploadFiles.clear();
getFolderItemsRecursive(uploadFiles, uploadFoldersVector[0], RECURSION_DEPTH_NON);

flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;

for (uint8_t j = 0; j < uploadFiles.size(); j++) {
srcPath = uploadFoldersVector[0] + "\\" + uploadFiles[j].metaData.cFileName;
dstPath = "c:\\_archivedtests" + (std::string)uploadFiles[j].metaData.cFileName;

if (!MoveFileExA(srcPath.c_str(), dstPath.c_str(), flags)) {
fprintf(stderr, "Error moving folder %s to local archive. \n\tWindows returned code: %ld\n", srcPath.c_str(), GetLastError());
}
}
}

getchar();
return 0;
}

0

Решение

getFolderItems() не звонит FindClose() после того, как цикл итерации закончен, вы пропускаете открытые дескрипторы к папкам, которые итерируются, поэтому вы видите их как «используемые».

if (hFind != INVALID_HANDLE_VALUE) {
do {
...
} while (FindNextFileA(hFind, &findfiledata) != 0);
FindClose(hFind); // <-- ADD THIS!
}

getFolderItems() также есть и другие ошибки:

  • if (findfiledata.cFileName[0] != '.') неправильная проверка, так как могут присутствовать другие папки, кроме . а также .. что также начинается с ., поэтому вам нужно использовать этот вид проверки вместо этого:

    if ((strcmp(findfiledata.cFileName, ".") != 0) && (strcmp(findfiledata.cFileName, "..") != 0))
    
  • используя switch проверять атрибуты неправильно, поскольку записи могут иметь несколько атрибутов. Вам нужно использовать побитовое И (&) оператор для проверки конкретных атрибутов:

    if (findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
    // Its a directory
    ...
    }
    else
    {
    // Its a file
    if (findfiledata.dwFileAttributes & (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_COMPRESSED))
    ...
    }
    

Кроме того, вместо перемещения отдельных файлов с MoveFileExA()рассмотрите возможность использования SHFileOperation() или же IFileOperation::MoveItems() вместо этого вы можете перемещать несколько файлов за одну операцию.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]