【问题标题】:FindFirstFile API didn't find all the files in a directoryFindFirstFile API 没有找到目录中的所有文件
【发布时间】:2018-06-13 04:55:21
【问题描述】:

我有以下代码可以在我的产品中查找文件:

destFolder = "c:\\myproduct\\base\\";

//download and unzip whoisactive.zip from some websites to the installation folder of my product
DownloadAndUnzipSql( "http://www.whoisactive.com/whoisactive.zip", destFolder)

WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFile(destFolder.c_str(), &ffd);

do {
    if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
        if ((string(ffd.cFileName).find("whoisactive") != string::npos) && (string(ffd.cFileName).find("sql") != string::npos)){
            MoveFile(ffd.cFileName, sqlFile.c_str());
            log.Debug(__FUNCTION__, "Succeed to rename file.");
            break;
        }
    }
} while (FindNextFile(hFind, &ffd) != 0);           

FindClose(hFind);

以前很好用,最近一个月下载的whoisactive.sql突然找不到了。我检查了 c:\myproduct\base\,whoisactive.sql 在那里,但是当我转储 FindFirstFile 和 FindNextFile 返回的所有文件时,whoisactive.sql 不包括在内。

我认为不是杀毒软件引起的,因为产品环境中没有安装杀毒软件。而且我认为这不是权限问题,因为 whoisactive.sql 已正确下载并解压缩。

我搜索了我的问题,发现以下两篇文章: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4659a528-dd51-4749-b751-a491bbdf5fa0/findfirstfile-caching?forum=vcgeneral Does FindFirstFile/FindNextFile API pair cache results returned?

FindFirstFile 似乎会缓存一个目录的文件列表,所以有时我们可能找不到新添加的文件。但我也不认为这是根本原因,因为奇怪的是我的代码在去年运行良好并且从未失败,并且最近一个月突然找不到 whoisactive.sql 并且直到现在才运行。如果缓存问题是根本原因,那为什么我现在总是可以重现问题?而且很奇怪,代码在我的开发环境下还能运行,但是在产品环境下就不行了。

所以我将我的代码更改为以下代码,然后它可以在产品和开发环境中运行:

destFolder = "c:\\myproduct\\base\\";

//download whoisactive.zip from some websites to the installation folder of my product
Download( "http://www.whoisactive.com/whoisactive.zip", destFolder);

//Unzip the whoisactive.zip and get the extracted file list.
string fileList;
Unzip(destFolder+"\\whoisactive.zip", fileList);

for (int i=0; i<fileList.size(); ++i) {
    if (fileList[i].find("sql") != string::npos && fileList[i].find("whoisactive") != string::npos) {
        string srcFile = destFolder+"\\"+fileList[i];
        string dstFile = destFolder+"\\"+sqlFile;

        if (DeleteFile(dstFile.c_str()) == 0)
            log.Debug(__FUNCTION__, "Failed to delete file %s, error code is %d.", dstFile.c_str(), GetLastError());

        if (MoveFile(srcFile.c_str(), dstFile.c_str()) == 0)
            log.Debug(__FUNCTION__, "Failed to rename file %s, error code is %d.", srcFile.c_str(), GetLastError());

        break;
    }
}

您可以看到变化是我不再在我的代码中使用 FindFirstFile 和 FindNextFile,我只是从 zip 文件中提取文件名并直接访问该文件。那你能告诉我为什么 FindFirstFile 和 FindNextFile 在我之前的代码中找不到下载的 sql 文件吗?谢谢。

【问题讨论】:

  • 您没有检查FindFirstFile的结果,没有使用这些函数的Unicode版本,并且在枚举文件时修改了目标文件夹。
  • 在我的情况下 FindFirstFile 返回了一个有效的句柄,但 FindNextFile 仍然没有返回所有文件;所有文件都不使用unicode文件名,如果这个问题是关于unicode版本api的,那么为什么我可以在同一目录中获取其他文件?而且我在枚举的时候没有修改目标文件夹,我在枚举之前修改了文件夹。
  • 您正在通过调用MoveFile 修改文件夹。请注意,您只传递了文件名,因此这意味着 destFolder 被设置为当前工作目录。

标签: c++ windows api sdk


【解决方案1】:

您根本没有搜索目录的内容。您将将目录本身按原样 ("c:\\myproduct\\base\\") 传递给FindFirstFile(),所以这就是它会找到的全部内容 - 目录本身的属性,仅此而已。

要正确枚举目录,您需要执行通配符搜索。将**.*附加到目录路径的末尾,例如:

destFolder = "c:\\myproduct\\base\\";

//download and unzip whoisactive.zip from some websites to the installation folder of my product
DownloadAndUnzipSql("http://www.whoisactive.com/whoisactive.zip", destFolder);

WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA((destFolder + "*.*").c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE) {
    do {
        if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
            if (lstrcmpiA(ffd.cFileName, "whoisactive.sql") == 0) {
                if (MoveFileA((destFolder + ffd.cFileName).c_str(), (destFolder + sqlFile).c_str()))
                    log.Debug(__FUNCTION__, "Succeed to rename file.");
                else
                    log.Debug(__FUNCTION__, "Failed to rename file, error code is %d.", GetLastError());
                break;
            }
        }
    }
    while (FindNextFileA(hFind, &ffd));

    if (GetLastError() != ERROR_NO_MORE_FILES) {
        log.Debug(__FUNCTION__, "Failed to find next file, error code is %d.", GetLastError());
    }

    FindClose(hFind);
}
else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
    log.Debug(__FUNCTION__, "No files found.");
}
else {
    log.Debug(__FUNCTION__, "Failed to find first file, error code is %d.", GetLastError());
}

但是,由于您只对特定文件真正感兴趣,因此您根本不需要使用Find(First|Next)File()。只要无条件调用MoveFile(),如果文件不存在就让它失败:

destFolder = "c:\\myproduct\\base\\";

//download and unzip whoisactive.zip from some websites to the installation folder of my product
DownloadAndUnzipSql("http://www.whoisactive.com/whoisactive.zip", destFolder);

if (MoveFileA((destFolder + "whoisactive.sql").c_str(), (destFolder + sqlFile).c_str()))
    log.Debug(__FUNCTION__, "Succeed to rename file.");
else
    log.Debug(__FUNCTION__, "Failed to rename file, error code is %d.", GetLastError());

【讨论】:

  • 嗨 Remy,感谢您的回答,但即使我在搜索目录中添加了“.”,我仍然找不到下载的 sql 文件。感谢您的第二个代码片段,它可以工作,但我仍然想知道为什么我的代码不起作用。
  • @JunGe 你需要在路径中添加"*.*",而不是"."
猜你喜欢
  • 1970-01-01
  • 2011-01-07
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多