【问题标题】:How to use FindNextFile call in Windows to list directory contents?如何在 Windows 中使用 FindNextFile 调用列出目录内容?
【发布时间】:2019-06-12 16:38:39
【问题描述】:

我有一小段代码旨在使用 Windows API 列出目录的内容。

代码:

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

int wmain() {

    WIN32_FIND_DATA data;
    std::string dir = "c:\\* "; 
    HANDLE hFind = FindFirstFileA(dir.c_str(), &data);      // DIRECTORY

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

错误:

Error C2664 'HANDLE FindFirstFileA(LPCSTR,LPWIN32_FIND_DATAA)': cannot convert argument 2 from 'WIN32_FIND_DATA *' to 'LPWIN32_FIND_DATAA'

错误会根据我的修复尝试而改变。我尝试了以下方法:

  • 试图将FindFirstFile 更改为FindFirstFileW / FindNextFileA
  • 试图在目录字符串L"C:\\*" 前面放置一个L

我查看了以下相关问题:

但是没有让它成功列出内容。我得到的最接近的是这段代码,它编译时没有错误:

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

int main()
{
    WIN32_FIND_DATA data;
    HANDLE hFind = FindFirstFileW(L"C:\\*", &data);

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

这可以编译,但是当我运行二进制文件时,它会输出:

0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C 0000009A9DBAF32C

在我们讨论的过程中,FindNextFileFindNextFileAFindNextFileW 之间有什么区别?我觉得这个问题在某种程度上与这些有关?

【问题讨论】:

  • 使用 std::wcout 代替。

标签: c++ windows winapi


【解决方案1】:

FindNextFile 是一个宏,可解析为 FindNextFileA (ANSI) 或 FindNextFileW (Unicode UTF16-LE),具体取决于是否定义了 UNICODE(和 _UNICODE)。

因此您将WIN32_FIND_DATA 更改为WIN32_FIND_DATAA,因为FindFirstFileA 需要参数的ANSI 版本。

请参阅https://devblogs.microsoft.com/oldnewthing/20040212-00/?p=40643,了解定义UNICODE_UNICODE 之间的区别

【讨论】:

  • 需要明确的是,虽然这是使FindFirstFileA 工作的解决方案,但它通常不是正确的解决方案。现在是 2019 年,编写 Unicode 友好的代码。正如汉斯在问题 cmets 中所说,正确的解决方案是使用 API 的W 版本(或无后缀版本,但确保您的构建环境定义UNICODE/_UNICODE,它应该默认在现代Visual Studio 版本),只需将您的输出发送到支持宽字符输出的std::wcout
  • 或者,如果您的应用程序的其余部分很好并且是 UTF-8,则在退出此功能时转换为 UTF-8,这样您就可以使用 std::cout将组成字节逐字传输到支持 UTF-8 的终端。
【解决方案2】:

在您的第一个代码 sn-p 中,您显然正在编译您的项目并定义了 UNICODE(很明显,您可以将 WIN32_FIND_DATA 传递给 FindNextFileW() 而不会出错)。当UNICODE 被定义时,WIN32_FIND_DATA 映射到WIN32_FIND_DATAW。但是FindFirstFileA() 想要一个WIN32_FIND_DATAA。因此,该错误的最简单解决方案是将WIN32_FIND_DATA 更改为WIN32_FIND_DATAA 以满足对FindFirstFileA() 的调用。但是随后您将遇到一个新错误,因为您会将WIN32_FIND_DATAA 传递给FindNextFileW(),而WIN32_FIND_DATAW 则需要WIN32_FIND_DATAW。因此,您需要致电 FindNextFileA() 来修复该错误:

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

int wmain() {

    WIN32_FIND_DATAA data;
    std::string dir = "c:\\* "; 
    HANDLE hFind = FindFirstFileA(dir.c_str(), &data);      // DIRECTORY

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

在您的第二个代码 sn-p 中,std::cout 没有针对 wchar_t 数据的重载 operator&lt;&lt;,但它确实具有针对 void* 的重载。 WIN32_FIND_DATAW::cFileName 字段是一个wchar_t[] 数组,它衰减为指向第一个字符的wchar_t* 指针,并且所有指针都可以隐式转换为void*。这就是为什么您看到正在打印内存地址的原因。您需要改用std::wcout

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

int wmain()
{
    WIN32_FIND_DATAW data;
    std::wstring dir = L"c:\\*";
    HANDLE hFind = FindFirstFileW(dir.c_str(), &data);

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

大多数处理字符串数据的 Win32 API 都有 A (ANSI) 和 W (Wide, Unicode) 风格,并提供基于 TCHAR 的通用宏来映射到基于 @987654350 的一个或另一个@有条件的。在这种情况下,FindFirstFile() 映射到 FindFirstFileA()FindFirstFileW()FindNextFile() 映射到 FindNextFileA()FindNextFileW()WIN32_FIND_DATA 映射到 WIN32_FIND_DATAAWIN32_FIND_DATAW,等等。

因此,一般规则是,您在 API 函数调用中明确声明(或省略)的后缀也应根据需要在任何相关变量声明中重复。如果显式调用A API 函数,请显式使用适当的A 变量类型。如果显式调用W API 函数,请显式使用适当的W 变量类型。如果您在 API 函数调用中省略了A/W,请在变量声明中省略它(或者如果需要,将TCHAR 替换为T)。

【讨论】:

  • 好答案。 OP 需要在 char 和 wide-char 之间进行选择,然后在 stdlib 和 WinAPI 中选择相同的实现,而不是随机混合和匹配/猜测。
猜你喜欢
  • 2011-01-19
  • 2012-11-12
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2019-09-05
  • 2019-03-26
  • 2019-01-11
  • 2020-07-28
相关资源
最近更新 更多