简体   繁体   English

FindFirstFile / FindNextFile不返回文件夹中的所有文件

[英]FindFirstFile/FindNextFile not returning all files in folder

Note: The question suggested as a duplicate discusses CreateFile , ERROR_FILE_NOT_FOUND , having existing handles on a file, and flagging a file to be deleted later. 注意:建议作为重复的问题讨论CreateFileERROR_FILE_NOT_FOUND ,在文件上具有现有句柄,以及标记稍后要删除的文件。 While it is a similar topic, none of those issues relate to my case. 虽然这是一个类似的主题,但这些问题都与我的案例无关。

I have the following method to delete a directory. 我有以下方法来删除目录。 I've been using it for a while without problems. 我已经使用它一段时间没有问题。

But recently, I've really been putting it through its paces with a large task deleting directories and files over a network path (just a USB drive connected to my router). 但是最近,我确实通过网络路径(只是连接到我的路由器的USB驱动器)删除目录和文件的大任务来完成它的步伐。

Everything seems to work great except for one area where it is not deleting all the files in a directory and so RemoveDirectory() fails. 除了一个没有删除目录中所有文件的区域外,一切似乎都很RemoveDirectory() ,因此RemoveDirectory()失败了。 (I have added each file to a list and verified that list did not contain the files that were not deleted.) (我已将每个文件添加到列表中,并验证该列表不包含未删除的文件。)

All files are named very similarly and there is nothing unusual about the name of the files that are not returned or deleted. 所有文件的命名都非常相似,并且没有返回或删除的文件名称没有异常。 If I run the program again, it will delete the remaining files and then have the same error a little later on another directory. 如果我再次运行程序,它将删除剩余的文件,然后稍后在另一个目录上出现相同的错误。

bool CBackupWorker::DeleteDirectory(LPCTSTR lpszName)
{
    if (!DirectoryExists(lpszName))
    {
        ASSERT(false);  // Unexpected
        return true;
    }

    CFileFind find;
    BOOL bContinue = find.FindFile(AppendPath(lpszName, _T("*")));
    while (bContinue)
    {
        bContinue = find.FindNextFile();
        if (find.IsDirectory())
        {
            if (!find.IsDots())
            {
                if (!DeleteDirectory(find.GetFilePath()))
                    return false;
            }
        }
        else
        {
            if (find.IsReadOnly())
                ClearReadOnlyAttribute(find);
            if (!::DeleteFile(ConvertToExtendedLengthPath(find.GetFilePath())))
            {
                LogErrorV(::GetLastError(), _T("ERROR DELETING FILE : '%s'"), (LPCTSTR)find.GetFilePath());
                return false;
            }
        }
    }

    CString sPath = ConvertToExtendedLengthPath(lpszName);
    DWORD dwAttributes = ::GetFileAttributes(sPath);
    if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_READONLY))
        ::SetFileAttributes(sPath, dwAttributes & (DWORD)~FILE_ATTRIBUTE_READONLY);
    if (!::RemoveDirectory(sPath))
    {
        LogErrorV(::GetLastError(), _T("ERROR DELETING DIRECTORY : '%s'"), lpszName);
        return false;
    }
    return true;
}

A couple of notes on the code: ConvertToExtendedLengthPath() adds a prefix so that paths longer than MAX_PATH will be allowed; 关于代码的ConvertToExtendedLengthPath()注意事项: ConvertToExtendedLengthPath()添加一个前缀,以便允许长于MAX_PATH路径; however, although these names are fairly long, none of them exceed MAX_PATH . 但是,尽管这些名称相当长,但它们都没有超过MAX_PATH (In this case, the method just returns the input value.) (在这种情况下,该方法只返回输入值。)

Also, I'm removing the read-only attribute on files and directories that have this attribute. 此外,我正在删除具有此属性的文件和目录的只读属性。 But again, I've confirmed this is not coming into play on any of the files I'm currently working with. 但同样,我已经确认这不会在我正在使用的任何文件上发挥作用。

Finally, this is not a case where the files I'm working with are changing. 最后,这不是我正在使用的文件正在发生变化的情况。 I am the only one with access to this external drive. 我是唯一一个可以访问此外部驱动器的人。

Has anyone seen any case where FindFirstFile / FindNextFile will miss a few files? 有没有人见过FindFirstFile / FindNextFile会遗漏几个文件的情况? Or where accessing files over a network share could interfere with the behavior of these functions? 或者通过网络共享访问文件可能会干扰这些功能的行为?

I have personally not observed such odd behaviour while using FindFirstFile / FindNextFile combo. 在使用FindFirstFile / FindNextFile组合时,我个人没有观察到这种奇怪的行为。

However, if you want to delete a directory and its contents, here's a solution: 但是,如果要删除目录及其内容,这是一个解决方案:

// VadaPoché_SO.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <strsafe.h>
#include <Shobjidl.h>

HRESULT CreateAndInitializeFileOperation(REFIID riid, void **ppv) //this function is copied verbatim from https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/winui/shell/appplatform/fileoperations/FileOperationSample.cpp
{
    *ppv = NULL;
    // Create the IFileOperation object
    IFileOperation *pfo;
    HRESULT hr = CoCreateInstance(__uuidof(FileOperation), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
    if (SUCCEEDED(hr))
    {
        // Set the operation flags.  Turn off  all UI
        // from being shown to the user during the
        // operation.  This includes error, confirmation
        // and progress dialogs.
        hr = pfo->SetOperationFlags(FOF_NO_UI);
        if (SUCCEEDED(hr))
        {
            hr = pfo->QueryInterface(riid, ppv);
        }
        pfo->Release();
    }
    return hr;
}

int main()
{
    using namespace std;

    const wchar_t *dirFullPath = L"C:\\test1\\test"; //this directory, and its contents, if any, will be deleted.

    IShellItem* itemDirToDelete = NULL;
    IFileOperation *fileOp = NULL;
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);

    if (FAILED(hr))
    {
        cout << "CoInitializeEx failed. Error code returned: 0x" << hex << hr << endl;
        return -1;
    }

    if (FAILED(SHCreateItemFromParsingName(dirFullPath, NULL, IID_PPV_ARGS(&itemDirToDelete))))
    {
        cout << "SHCreateItemFromParsingName failed. Error code returned: 0x" << hex << hr << endl;
        return -1;
    }

    if (SUCCEEDED(CreateAndInitializeFileOperation(IID_PPV_ARGS(&fileOp))))
    {
        //Note: contrary to its name DeleteItem, this does NOT do the actual deletion. 
        //It only declares an intention to perform deletion.
        if (SUCCEEDED(fileOp->DeleteItem(itemDirToDelete, NULL))) 
        {
            hr = fileOp->PerformOperations(); //This is the statement that acts on the intention declared above. i.e. it deletes the folder.
        }
        fileOp->Release();
    }

    return 0;
}

Although both the Windows file system and the Windows implementation of SMB ensures that files aren't left out of a directory enumeration, even if the contents of the directory are changing, this does not appear to be a requirement of the SMB protocol itself . 尽管Windows文件系统和SMB的Windows实现都确保文件不会被排除在目录枚举之外,但即使目录的内容发生变化,这似乎也不是SMB协议本身的要求。 (I'm hardly an expert, though, so I may have overlooked something.) At any rate, regardless of whether the behaviour of your server is technically correct or not, you will presumably need to deal with it as-is. (但我不是专家,所以我可能忽略了一些东西。)无论如何,无论服务器的行为在技术上是否正确,你都可能需要按原样处理它。

I would guess that the shell API already deals with this situation, but in your case I would recommend against using it, because I don't believe it supports long path names. 我猜想shell API已经处理了这种情况,但在你的情况下我建议不要使用它,因为我不相信它支持长路径名。

If you know that there will never be an excessively large number of entries in any given directory, and that no other process will be deleting files in the directory at the same time you are enumerating it, you might prefer to read the list of files first and then start deleting them. 如果您知道在任何给定目录中永远不会有过多的条目,并且在您枚举它的同时没有其他进程将删除目录中的文件,您可能更愿意首先读取文件列表然后开始删除它们。 I gather you've already built a proof-of-concept along these lines. 我知道你已经在这些方面建立了一个概念验证。

A slightly less time-efficient but more memory-efficient and I think more robust approach would be to enumerate and delete the files at the same time (as already shown in the posted code) but then loop around and re-enumerate until you find that the directory is empty apart from the . 时间效率稍低但内存效率更高,我认为更强大的方法是同时枚举和删除文件(如已发布的代码中所示),然后循环并重新枚举,直到找到该目录是空的除了. and .. entries. ..条目。 The only obvious downside to this is the extra round-trip to the server. 唯一明显的缺点是额外的服务器往返。 YMMV. 因人而异。 :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM