下面的代码是我项目中的一个片段,其中包括:
- 从指定的本地文件夹(上传文件夹)获取文件夹内容的集合
- 将包含的文件复制到远程网络文件夹。如果有子文件夹,则复制其文件,但不保留文件夹结构。
- 将原始本地文件夹移动到不同的本地位置(存档文件夹)
我遇到的麻烦是在移动操作中。如果要移动的文件夹包含一个或多个子文件夹,则移动将失败并显示“拒绝访问”错误。我已将原因缩小到先前的函数调用,该函数调用生成要复制的文件列表。我在下面的代码中突出显示了这个调用。它在 main 中(参见第一次调用 getFolderItemsRecursive())。我不明白为什么这个递归函数会产生问题。就好像通过收集信息,它以某种方式将某些东西标记为“正在使用”。请注意,我实际上并没有复制文件,因为我已经注释掉了这些行。所以不是这样。
要运行下面的代码,您需要在某处创建几个文件夹并将位置输入到代码中。代码假定 c:_UploadTests 和 c:_archivedtests。要重现该问题,请在 _UploadTest 中创建至少一个带有自己的子文件夹的子文件夹。例如 c:_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;
}