如何在Windows中使用FindNextFile调用来列出目录内容?

How to use FindNextFile call in Windows to list directory contents?

本文关键字:调用 Windows FindNextFile      更新时间:2023-10-16

我有一小段代码,旨在使用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

我看了以下相关问题:

  • 使用 C 和 Windows 列出目录内容
  • 如何使用Windows API列出目录中的文件?

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

#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有什么区别?我觉得这个问题与这些有某种关系?

FindNextFile

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

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

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

在你的第一个代码片段中,你显然是在用定义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。 因此,您需要调用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;
}

在第二个代码片段中,std::cout没有用于wchar_t数据的重载operator<<,但它确实具有用于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(宽、Unicode)风格,并提供基于TCHAR的通用宏,以根据UNICODE条件映射到其中一个宏。 在这种情况下,FindFirstFile()映射到FindFirstFileA()FindFirstFileW()FindNextFile()映射到FindNextFileA()FindNextFileW()WIN32_FIND_DATA映射到WIN32_FIND_DATAAWIN32_FIND_DATAW,等等。

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