如何使用 Windows API 列出目录中的文件?

How to list files in a directory using the Windows API?

我有这段代码,它显示文件夹及其目录本身,而不是其内容。我想显示它的内容。我不想使用 boost::filesystem。

我该如何解决这个问题?

代码:

#include <windows.h>
#include <iostream>

int main()
{
    WIN32_FIND_DATA data;
    HANDLE hFind = FindFirstFile("C:\semester2", &data);      // DIRECTORY

    if ( hFind != INVALID_HANDLE_VALUE ) {
        do {
            std::cout << data.cFileName << std::endl;
        } while (FindNextFile(hFind, &data));
        FindClose(hFind);
    }
}

输出:

semester2
HANDLE hFind = FindFirstFile("C:\semester2", &data);       // DIRECTORY

您得到了目录,因为这正是您所要求的。如果您需要这些文件,请索要它们:

HANDLE hFind = FindFirstFile("C:\semester2\*", &data);  // FILES

(如果您愿意,您可以改为使用 *.*,但显然这仅适用于向后兼容性黑客攻击,因此可能应该避免。请参阅评论和 RbMm 的回答。)

Harry 的回答实际上会在您想要的文件夹中生成具有扩展名的文件和文件夹 "C:\semester2"

例如,如果您有一个名为 "C:\semester2\math.course" 的文件夹,上面的示例也会找到它。此外,如果您有一个名为 "C:\semester2\math_scores" 的文件(请注意它没有扩展名),它将找不到。

考虑到上述情况,我建议采用以下解决方案:

HANDLE hFind = FindFirstFile("C:\semester2\*", &data); 

这将列出目录下的整个项目列表。 可以通过以下方式过滤目录:

if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// directory
}
else
{
// file
}

以下可以参考:FileAttributes constants, FIND_DATA struct, FindFirstFile API

让我做一些关于 "*.*""*" 的笔记。这些文件管理器不相等。

我们的文件夹中可以存在 2 个不同的文件:somefilesomefile.

如果我们使用低级apiZwQueryDirectoryFile"*.*"作为搜索表达式(这是第10个参数-FileName [in, optional] ) - 我们只会得到 somefile.。但是如果我们使用 "*" 我们会得到两个文件 - somefilesomefile.

如果我们尝试 FindFirstFile("C:\semester2\*.*", &data);,我们可以注意到文件 somefilesomefile. 都被返回了。所以这里 "*.*""*" 具有相同的效果 - 用法没有区别。

为什么会这样?因为在 kernelbase 中的 FindFirstFileEx (kernel32 ) 中对 "*.*" 掩码进行特殊检查,如果它是真的 - 替换为 "" (具有相同的空名称效果为 "*" )。

我认为这样做是为了修复用户传递 "*.*" 而不是正确的 "*" 时的一个非常常见的错误,并为了与遗留代码的向后兼容性。

. and .. aren't actually part of the directory as it is stored on disk, but are added by the Win32 API.

这不是真的。

  • 对于 FAT 风格的文件系统,这实际上作为第 2 个条目存储在 FAT 目录中。
  • NTFS中没有这样的条目,但是NTFS.sys如果它们在掩码中则人为地添加这2个条目。

所以这不是在 Win32 API 级别完成的,而是在内核 - 驱动程序级别完成的。

总之,"*.*" 将在 Win32 API 下正常工作 - 但正确和干净的方法是在此处使用 "*"
"*.*"ZwQueryDirectoryFile 会出错 api.

这是一个示例实现:

#include <iostream>
#include <vector>
#include <string>
#include <Windows.h>

std::vector<std::string>
list_directory(
    const std::string &directory)
{
    WIN32_FIND_DATAA findData;
    HANDLE hFind = INVALID_HANDLE_VALUE;
    std::string full_path = directory + "\*";
    std::vector<std::string> dir_list;

    hFind = FindFirstFileA(full_path.c_str(), &findData);

    if (hFind == INVALID_HANDLE_VALUE)
        throw std::runtime_error("Invalid handle value! Please check your path...");

    while (FindNextFileA(hFind, &findData) != 0)
    {
        dir_list.push_back(std::string(findData.cFileName));
    }

    FindClose(hFind);

    return dir_list;
}

注意:如果您使用 C++ 11,最好使用 boost::filesystem 之类的东西,如果您使用 C++ 17,则使用 std::filesystem 之类的东西会更好。 此外,输入路径必须类似于 C:\path 而不是 C:\path\ 否则这将无法工作!!