一般情况下,我们在遍历文件夹时,都采用递归方式,但在wince平台下测试发现,当递归深度达到46级时,出现stack overflow而导致程序崩溃。为了解决这个问题,将递归改进为循环,这样理论上可以做到无限,只要内存一直满足的前提下。下面的代码完整展现了Windows平台(含WinCE)下,改进的文件遍历算法——循环深度优先遍历。
头文件:
#ifndef CFILEFINDER_H_
#define CFILEFINDER_H_
#include <Windows.h>
#if 1
#include "hswstack.h"
#else
#include "hswqueue.h"
#endif
#ifndef FILE_STRING_STD
#define FILE_STRING_STD (260)
#endif
#ifndef CheckFilePath
#define CheckFilePathLength(x) \
if(wcslen(x) >= FILE_STRING_STD)\
return
#endif
#ifndef _TAG_FILE_FOLDER
#define _TAG_FILE_FOLDER
typedef struct tagFileFolder
{
WCHAR wcName[FILE_STRING_STD];
void operator()(LPCTSTR lpfile)
{
if (this)
{
CheckFilePathLength(lpfile);
wcscpy_s(wcName,FILE_STRING_STD,lpfile);
}
}
void operator=(const struct tagFileFolder _right)
{
if (this)
{
CheckFilePathLength(_right.wcName);
wcscpy_s(wcName,FILE_STRING_STD,_right.wcName);
}
}
bool operator==(LPCTSTR lpfile)const
{
if (this != NULL)
{
return !(wcsicmp(this->wcName,lpfile));
}
}
bool operator==(const struct tagFileFolder _right)const
{
if (this != NULL)
{
return !(wcsicmp(this->wcName,_right.wcName));
}
}
tagFileFolder()
{
memset(wcName,0,FILE_STRING_STD);
}
tagFileFolder(LPCTSTR lpfile)
{
CheckFilePathLength(lpfile);
wcscpy_s(wcName,FILE_STRING_STD,lpfile);
}
}tFileItem,*PFileItem;//此结构体可以根据不同的应用场景来扩展。可以增加文件的扩展名,创建日期,修改日期,大小等,可以增加文件的属性
#endif
class CFileFinder//采用深度优先算法遍历文件夹及其子文件夹,不采用递归方式,采用循环方式
{
public:
CFileFinder();
virtual ~CFileFinder();
public:
virtual BOOL Initialize() = 0;//初始化
virtual void UnInitialize() = 0;//反初始化
virtual BOOL UserFunction(LPCTSTR lpFolder, LPCTSTR lpFile) = 0;//遍历过程中,供派生类对象处理数据的接口
virtual BOOL ScanOver() = 0;//扫描结束后,供派生类对象处理扫描出来的文件的接口
protected:
virtual BOOL StartScan(LPCTSTR lpPath);
virtual void StopScan();
protected:
CRITICAL_SECTION m_CS;
__forceinline void Lock() {EnterCriticalSection(&m_CS);}
__forceinline void Unlock() {LeaveCriticalSection(&m_CS);}
virtual BOOL KillScanProc();
virtual BOOL CreateScanProc();
private:
volatile BOOL m_bAbort;//控制当前是否扫描文件
volatile BOOL m_bRunning;//控制扫描文件的线程退出与否
HANDLE m_hEvtScan;
HANDLE m_hThread;
static DWORD WINAPI FileFindProc(LPVOID lp);
#if 1
HswStack<tFileItem> m_list_folder;//用于存放文件夹的链表
#else
HswQueue<tFileItem> m_list_folder;
#endif
virtual BOOL ScanFiles();
};
#ifdef UNDER_CE
#include "ShlWapi.h"
#pragma comment(lib,"shlwapi.lib")
#else
#include <io.h>
#endif
#endif
源文件:
#include "FileFinder.h"
CFileFinder::CFileFinder()
{
m_bRunning = FALSE;
m_hThread = NULL;
m_hEvtScan = NULL;
m_bAbort = FALSE;
InitializeCriticalSection(&m_CS);
}
CFileFinder::~CFileFinder()
{
StopScan();
KillScanProc();
m_list_folder.free();
DeleteCriticalSection(&m_CS);
}
BOOL CFileFinder::StartScan(LPCTSTR lp)
{
BOOL b = FALSE;
#ifdef UNDER_CE
if (wcslen(lp) > 0 && PathFileExists(lp))
{
#else
WCHAR wPath[MAX_PATH];
wsprintf(wPath,L"%s\\",lp);
if(wcslen(lp) > 0 && (0 == _waccess(wPath,0)))
{
#endif
InterlockedExchange((LONG*)&m_bRunning, (LONG)TRUE);
InterlockedExchange((LONG*)&m_bAbort,(LONG)FALSE);
Lock();
m_list_folder.push(tFileItem(lp));
Unlock();
SetEvent(m_hEvtScan);
b = TRUE;
}
return b;
}
void CFileFinder::StopScan()
{
InterlockedExchange((LONG*)&m_bAbort,TRUE);
RETAILMSG(1,(L"%s,%d,.size %d\n",TEXT(__FUNCTION__),__LINE__,m_list_folder.size()));
}
BOOL CFileFinder::CreateScanProc()
{
if (NULL == m_hThread)
{
m_bRunning = TRUE;
m_bAbort = FALSE;
m_hEvtScan = ::CreateEvent(NULL,FALSE,FALSE,NULL);
m_hThread = ::CreateThread(NULL,0,FileFindProc,this,0,NULL);
#ifdef UNDER_CE
int iThread = ::CeGetThreadPriority(::GetCurrentThread());
::CeSetThreadPriority(m_hThread, iThread - 5);
#endif
}
return 1;
}
BOOL CFileFinder::KillScanProc()
{
if (m_hThread)
{
#ifdef UNDER_CE
::CeSetThreadPriority(m_hThread,CE_THREAD_PRIO_256_TIME_CRITICAL);
#endif
StopScan();
InterlockedExchange((LONG*)&m_bRunning, (LONG)FALSE);
WaitForSingleObject(m_hThread,INFINITE);
CloseHandle(m_hThread);
CloseHandle(m_hEvtScan);
m_hThread = NULL;
m_hEvtScan = NULL;
}
return TRUE;
}
DWORD CFileFinder::FileFindProc(LPVOID lp)
{
CFileFinder *pThis = (CFileFinder*)lp;
if (pThis != NULL)
{
DWORD dwWait;
for (;pThis->m_bRunning;)
{
dwWait = ::WaitForSingleObject(pThis->m_hEvtScan, 800);
switch(dwWait)
{
case WAIT_OBJECT_0:
{
pThis->Lock();
DWORD dwTick = GetTickCount();
BOOL ret = pThis->ScanFiles();//step1.扫描文件,并将文件存放在链表中
RETAILMSG(1,(L"scan time %d\n",GetTickCount()-dwTick));
if (ret)
{
//1、文件扫描完
pThis->ScanOver();
}
pThis->Unlock();
}
break;
}
}
}
return 56789;
}
BOOL CFileFinder::ScanFiles()
{
TCHAR cs_temp_cur[FILE_STRING_STD];
tFileItem folder;
WIN32_FIND_DATA filedata;
BOOL bRet = TRUE;
int itemp_folder_count = 0;
int itemp_file_count = 0;
while (!m_bAbort && m_list_folder.size() > 0 )
{
m_list_folder.pop(folder);//pop函数的实现方式直接决定了遍历的方式是深度优先遍历还是广度优先遍历
//如果链表的push和pop操作都在链表的同一端,则类似栈操作,实现为深度优先遍历
//如果链表的push和pop分别在链表的头和尾操作,则类型队列,实现为广度优先遍历
wcscpy_s(cs_temp_cur, FILE_STRING_STD, folder.wcName);
wcscat_s(cs_temp_cur, FILE_STRING_STD, L"\\*.*");
HANDLE hFind = FindFirstFile(cs_temp_cur, &filedata);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (filedata.cFileName[0] == L'.')
{
continue;
}
if (FILE_ATTRIBUTE_HIDDEN == (filedata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
{
continue;
}
wsprintf(cs_temp_cur,L"%s\\%s",folder.wcName,filedata.cFileName);
wcscpy_s(filedata.cFileName,MAX_PATH,cs_temp_cur);
if (FILE_ATTRIBUTE_DIRECTORY == (filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
{
itemp_folder_count++;
m_list_folder.push(tFileItem(cs_temp_cur));
}
else
{ itemp_file_count++;
UserFunction(folder.wcName,cs_temp_cur);}
//Sleep(0);
}while(!m_bAbort && FindNextFile(hFind, &filedata));
FindClose(hFind);
}
//Sleep(0);
}
int error = GetLastError();
if (m_bAbort ||
((error != 18 && error != 0) || 4319 == error || 1609 == error || 3 == error || 1617 == error))
{
bRet = FALSE;
}
m_list_folder.free();
printf("total file count %d,,,,,total folder count %d\n",itemp_file_count,itemp_folder_count);
return bRet;
}
hswstack.h文件可以在另一篇文章中找到。