VC++ 功能强大的API函数FindFirstFile使用介绍(附源码)

       在处理文件的相关代码中,会频繁使用到Windows系统API函数FindFirstFile,这个函数功能很强大,很多功能都不开它。本文就根据我们在项目中使用该函数的情况,来大概地梳理一下使用FindFirstFile都可以实现哪些常用的功能。

VC++ 功能强大的API函数FindFirstFile使用介绍(附源码)_第1张图片

1、FindFirstFile函数声明与WIN32_FIND_DATA结构体

        我们先来看一下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上对结构体的详细说明。下面我们就来罗列一下使用该函数能够实现哪些功能。

2、判断文件是否存在

        调用FindFirstFile时第一个参数传入某个文件的完整路径,能判断该文件是否存在,代码如下:

HANDLE hFile = FindFirstFile( strFilePath, &wfd );
if ( hFile == INVALID_HANDLE_VALUE )
{
	return 0;
}

3、判断文件的属性

        调用FindFirstFile时传入路径,可以获取该路径相关的文件属性信息(对应WIN32_FIND_DATA结构体中的dwFileAttributes字段)。可以拿该dwFileAttributes属性值,和FILE_ATTRIBUTE_DIRECTORY取与,判断该路径是文件夹还是文件。

        还可以和FILE_ATTRIBUTE_HIDDENFILE_ATTRIBUTE_SYSTEMFILE_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 // 用户日志文件
	{
		//...
	}
}

4、获取文件的最后修改时间

       文件的最后修改时间对应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 );
}

5、获取文件的大小

      文件的大小对应WIN32_FIND_DATA结构体中的nFileSizeHighnFileSizeLow字段。考虑到文件比较大,考虑使用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;
}  

6、遍历文件夹中的文件

       当给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;
}

你可能感兴趣的:(VC++常用功能代码封装,FindFirstFile,WIN32_FIND_DATA)