在处理文件的相关代码中,会频繁使用到Windows系统API函数FindFirstFile,这个函数功能很强大,很多功能都不开它。本文就根据我们在项目中使用该函数的情况,来大概地梳理一下使用FindFirstFile都可以实现哪些常用的功能。
我们先来看一下FindFirstFile接口的声明:
HANDLE FindFirstFileA(
[in] LPCSTR lpFileName,
[out] LPWIN32_FIND_DATAA lpFindFileData
);
第一参数是文件或文件夹名称,一般是绝对路径,是传入参数。第二个参数是关于文件信息的结构体WIN32_FIND_DATA。
如果第一个参数传入的是文件路径(单个文件的路径),则该函数返回后,获取的是该文件的信息;如果第一个参数传入的是文件夹路径(文件夹),则该函数会获取到文件夹中的第一个文件信息。
FindFirstFile函数的强大功能是因为其能获取到关于文件的WIN32_FIND_DATA结构体信息,该结构体入如下所示:(以宽字节的WIN32_FIND_DATAW为例)
typedef struct _WIN32_FIND_DATAW {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
WCHAR cFileName[ MAX_PATH ];
WCHAR cAlternateFileName[ 14 ];
#ifdef _MAC
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
#endif
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
该结构体中包含文件的大量信息,比如文件的属性、文件的创建时间、文件的访问时间、文件的修改时间、文件的大小等。可以查阅MSDN上对结构体的详细说明。下面我们就来罗列一下使用该函数能够实现哪些功能。
调用FindFirstFile时第一个参数传入某个文件的完整路径,能判断该文件是否存在,代码如下:
HANDLE hFile = FindFirstFile( strFilePath, &wfd );
if ( hFile == INVALID_HANDLE_VALUE )
{
return 0;
}
调用FindFirstFile时传入路径,可以获取该路径相关的文件属性信息(对应WIN32_FIND_DATA结构体中的dwFileAttributes字段)。可以拿该dwFileAttributes属性值,和FILE_ATTRIBUTE_DIRECTORY取与,判断该路径是文件夹还是文件。
还可以和FILE_ATTRIBUTE_HIDDEN、FILE_ATTRIBUTE_SYSTEM、FILE_ATTRIBUTE_READONLY等宏取余,依次判断文件是否是掩藏文件、是否是系统文件、是否是只读的等。相关代码如下:
WIN32_FIND_DATA wfd;
HANDLE hFindFile = FindFirstFile( strFindFileName.c_str(), &wfd );
if ( hFindFile == INVALID_HANDLE_VALUE )
{
return;
}
if ( wfd.cFileName[0] != _T('.') )
{// 非本级或上级目录
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // 目录
{
//...
}
else if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) // 系统文件,不处理
{
//...
}
else // 用户日志文件
{
//...
}
}
文件的最后修改时间对应WIN32_FIND_DATA结构体中的ftLastWriteTime字段。获取文件修改的最后修改时间的代码如下:
// 获取日志文件的最后修改时间
// 参数:strFilePath[in], sysTime[out]
time_t GetFileModifyTime( LPCTSTR strFilePath )
{
SYSTEMTIME sysTime;
HANDLE hFile = INVALID_HANDLE_VALUE;
FILETIME localFileTime;
WIN32_FIND_DATA wfd;
memset( &wfd, 0, sizeof(wfd) );
HANDLE hFile = FindFirstFile( strFilePath, &wfd );
if ( hFile == INVALID_HANDLE_VALUE )
{
return 0;
}
BOOL bRet = FileTimeToLocalFileTime( &wfd.ftLastWriteTime, &localFileTime );
if ( !bRet )
{
return 0;
}
memset( &sysTime, 0, sizeof(sysTime) );
bRet = FileTimeToSystemTime( &localFileTime, &sysTime );
if ( !bRet )
{
return 0;
}
FindClose( hFile );
// 老版本对tm结构体变量的初始化是不科学的,根据之前对本地时间和UTC时间及时区的研究,
// 将代码修改成如下的代码, by zzx 2016/04/07
struct tm tmFileTime;
memset( &tmFileTime, 0xFF, sizeof(tmFileTime) );
tmFileTime.tm_year = sysTime.wYear - 1900;
tmFileTime.tm_mon = sysTime.wMonth - 1;
tmFileTime.tm_mday = sysTime.wDay;
tmFileTime.tm_hour = sysTime.wHour;
tmFileTime.tm_min = sysTime.wMinute;
tmFileTime.tm_sec = sysTime.wSecond;
return mktime( &tmFileTime );
}
文件的大小对应WIN32_FIND_DATA结构体中的nFileSizeHigh和nFileSizeLow字段。考虑到文件比较大,考虑使用64位整型数据表示文件大小,nFileSizeHigh字段存放64位整数的高32位,nFileSizeLow字段存放64位整数的低32位。获取文件大小的代码如下:
// 计算单个文件的大小
BOOL CalcFileSize( CString strFilePath, u64& u64FileSzie )
{
u64FileSzie = 0;
WIN32_FIND_DATA wfd;
HANDLE hFindFile = FindFirstFile( strFilePath, &wfd );
if ( hFindFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
// 非本级或上级目录,且不是文件夹
if ( (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY )
{
u64 u64Temp = wfd.nFileSizeHigh;
u64FileSzie += ( u64Temp << 32 ) + wfd.nFileSizeLow;
}
FindClose( hFindFile );
return TRUE;
}
当给FindFirstFile传入一个文件夹路径时,该函数会获取到该文件夹下的第一个文件的信息,与FIndNextFile组合使用,完成对和一个文件夹下的所有文件进行遍历。遍历文件夹中所有文件的代码如下:
// 计算文件夹的大小,注意调用前请将n64FileSzie清零
BOOL CalcFileFolderSize( CString strFileDir, u64& u64FileSzie )
{
CString strFindFileName = strFileDir + _T("\\*.*");
WIN32_FIND_DATA wfd;
HANDLE hFindFile = FindFirstFile( strFindFileName, &wfd );
if ( hFindFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
while ( 1 )
{
// 将文件夹中的.和..过滤掉,之前仅判断wfd.cFileName[0]是否为‘.’是有问题的,
// 比如版本控制的代码的目录下包含掩藏的.svn文件夹,会导致文件夹大小计算有误
//(偏小)
if ( _tcscmp( wfd.cFileName, _T(".") ) != 0 &&
_tcscmp( wfd.cFileName, _T("..") ) != 0 )
{// 非本级或上级目录
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{// 目录
// 递归
CString strFilePath = strFileDir + _T('\\') + wfd.cFileName;
CalcFileFolderSize( strFilePath, u64FileSzie );
}
else
{// 文件
u64 u64Temp = wfd.nFileSizeHigh;
u64FileSzie += ( u64Temp << 32 ) + wfd.nFileSizeLow;
}
}
if ( !FindNextFile( hFindFile, &wfd ) )
{
break;
}
};
FindClose( hFindFile );
return TRUE;
}