计算文件夹集合大小的方法






4.09/5 (6投票s)
使用 VC++ 计算文件夹集合大小的方法。
引言
我从 CodeProject 得到了很多帮助,现在我想发布一个小程序来帮助他人,同时也能提升自己。希望对大家有所帮助。
有时,我们希望向用户动态显示他们选择的文件夹的大小,就像点击文件夹属性时,文件夹大小会不断增加一样。我们该如何获取文件夹/文件/驱动器的大小呢?当然,我们可以在 CodeProject 上找到一些类似的的文章,例如 Hans Dietrich 的项目。他的应用程序可以获取任何文件夹的容量。但不幸的是,它一次只能获取一个文件夹的大小。在我的演示项目中,我实现了一种方法来获取多个文件夹的大小,而不仅仅是一个,并在新线程中运行。这样就不会影响主线程的职责。
顺便说一下,我的项目提供的是获取文件夹大小的向量,甚至可以获取驱动器的大小。但是你知道,这个过程是耗时的。如果你只想获取驱动器的容量,我建议你使用 Windows API 函数,例如 GetDiskFreeSpace
。
在我的项目中,我借用了演示对话框 CFolderTreeCtrl 来自 Adrien Pinet。他的文件夹树控件非常有用。为了演示我的项目,我可以在树控件复选框中选择或取消选择任何文件夹。
实现细节
CFolder
类是一个非常简单的文件夹类,用于保存计算文件夹大小所需的必要信息,如下所示
//////////////////////////////////////////////////////////////////////////
// CFolder class
// A very basic folder class with only a few
// properties to simulating a real folder
//////////////////////////////////////////////////////////////////////////
class CFolder
{
// make all class members public for direct access, for it's a basic and small class
public:
CString m_szPath; // full path of the folder
DWORD64 m_dw64Size; // DWORD64 value of the file size, in bytes
bool m_bSelected; // To mark if the folder is selected or not
bool m_bCalculated; // To mark if the folder size is calculated or not
public:
// Constructor function, all other members can access directly.
CFolder(CString szPath=_T(""), DWORD64 dwSize=0, bool bSel=true,bool bCal=false):\
m_szPath(szPath), m_dw64Size(dwSize), m_bSelected(bSel), m_bCalculated(bCal)
{
// constructor function
}
// overload this function to determine if two folder are the same folder
// if they path are the same, they are the same folder
bool operator==(const CFolder& folder) const
{
return m_szPath==folder.m_szPath;
}
bool operator==(const CString& str) const
{
return m_szPath==str;
}
};
CFolderSize
类是实现文件夹大小计算的关键
//////////////////////////////////////////////////////////////////////////
// CFolderSize class
// a class integrated the functions to calculate
// the folders size.
//////////////////////////////////////////////////////////////////////////
class CFolderSize
{
public:
//////////////////////////////////////////////////////////////////////////
// constructor and destructor
CFolderSize(HANDLE handle=NULL, bool bCalCompleted=false,
bool bToExit=false, DWORD64 dwSize=0) ;
~CFolderSize();
//////////////////////////////////////////////////////////////////////////
// Basic get functions of the members
bool IsCompleted() const
{
return m_bCalCompleted;
}
bool IsCalledToExit() const
{
return m_bToExit;
}
DWORD64 GetTotalSize() const
{
return m_dw64TotalSize;
}
//caution, it's pointer
vector<CFolder>* GetAllFolder()
{
return &m_vFolder;
}
//////////////////////////////////////////////////////////////////////////
//folders operations
// set a collection of folders to this class
// and then start calculation automatically
void SetPath(const vector<CString>& pathV);
//////////////////////////////////////////////////////////////////////////
// Calculation and its thread operations
// Thread functions
static DWORD64 CalcThreadFunc(LPVOID pParam);
//function to calculate a folder's capacity, return value in bytes
DWORD64 CalcFolderSize(CString szPath);
//////////////////////////////////////////////////////////////////////////
//other operations, for convenience use.
// Get a proper unit for the folder capacity, return a string
// contain capacity and its unit. for user's convenience
// for example,1024 bytes should be 1K
static CString TrimSizeToUnit(DWORD64 capacity);
private:
vector<CFolder> m_vFolder; // keep all the folders
DWORD64 m_dw64TotalSize; // value to keep all the folders' size,
// the same in bytes
HANDLE m_hCalcThread; // the calculation's thread' handle
bool m_bToExit; // set true to force the calculated thread to exit
bool m_bCalCompleted; // if the calculation is completed?
};
我的应用程序实现计算的关键是以下三个函数。
-
DWORD64 CFolderSize::CalcFolderSize(CString szPath)
第一个可以获取一个文件夹(及其子文件夹)的大小。
/////////////////////////////////////////////////////////////// // // CFolderSize::CalcFolderSize // // Purpose: recursive calculate the folder's size // // Parameters: CString szPath - fully qualified path to a folder // // // Returns: DWORD64 - Returns 64-bit folder's size. // DWORD64 CFolderSize::CalcFolderSize(CString szPath) { if(szPath.Right(1) != _T("\\")) szPath += _T("\\"); szPath += _T("*"); WIN32_FIND_DATA fileinfo; HANDLE hFind = FindFirstFile(szPath, &fileinfo); if(hFind == INVALID_HANDLE_VALUE) return 0; //recursive calculate the folder's size do { if(m_bToExit) //force to exit? { return 0; //exit the thread } // if the folders are the current or the upper level folder if( (0 == _tcscmp(fileinfo.cFileName, _T("."))) || (0 == _tcscmp(fileinfo.cFileName, _T(".."))) || fileinfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) continue; // thanks to lusores' advice about // FILE_ATTRIBUTE_REPARSE_POINT else if(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // directory, not a file, recursive calculate the folder size CString szSubFolder = szPath; szSubFolder.Delete(szSubFolder.GetLength()-1, 1);//delete // the asterisk szSubFolder += fileinfo.cFileName; szSubFolder += _T("\\"); CalcFolderSize(szSubFolder); } else // It's a file, add the file size { // here add all the size to the total size, // that will the risk to calculate the // folder size repeatedly // to avoid this situation, when we perform a calculation, // we must start a new calculation and set // total size to 0, the folder which is set // completed cache the size will // accelerate the calculation m_dw64TotalSize += (((DWORD64)fileinfo.nFileSizeHigh) <<(sizeof(DWORD)*8)) + fileinfo.nFileSizeLow; } }while(FindNextFile(hFind, &fileinfo)); FindClose(hFind); return m_dw64TotalSize; }
-
DWORD64 CFolderSize::CalcThreadFunc(LPVOID pParam)
是线程回调函数。在该函数中,它将调用
DWORD64 CFolderSize::CalcFolderSize(CString szPath)
来获取文件夹的容量。/////////////////////////////////////////////////////////////////// // // CFolderSize::CalcThreadFunc // // Purpose: provide this thread callback function for the thread to calculate // folder's size. // // Parameters: LPVOID pParam - provide a CFolderSize object, // and this object keep almost all the necessary information. // // Returns: DWORD64 -the total folder collection's size. // DWORD64 CFolderSize::CalcThreadFunc(LPVOID pParam) { CFolderSize* pClass=(CFolderSize*)pParam; if(!pParam) return 0; vector<CFolder>* pVector=pClass->GetAllFolder(); // Every Calculation thread start, reset the total size pClass->m_dw64TotalSize = 0; pClass->m_bCalCompleted=FALSE; for(vector<CFolder>::iterator it=pClass-> m_vFolder.begin();it!=pClass->m_vFolder.end();++it) { if(pClass->m_bToExit) // force to exit? { return 0; } else if((it->m_bCalculated ==false) && it->m_bSelected) { DWORD64 dwOriginalSize = pClass->GetTotalSize(); CString str=it->m_szPath; // In fact, if the path is a full drive, we can //call GetDiskFreeSpaceEx, it's must more fast. pClass->CalcFolderSize(str); DWORD64 dwNewSize = pClass->GetTotalSize(); it->m_bCalculated=true; it->m_dw64Size=dwNewSize-dwOriginalSize; //cache // folder size } else if(it->m_bCalculated && it->m_bSelected) // use the cached // folder size pClass->m_dw64TotalSize+=it->m_dw64Size; } pClass->m_bCalCompleted=TRUE; return pClass->m_dw64TotalSize; }
-
void CFolderSize::SetPath(const vector<CString>& pathV)
此函数是用户的主要接口。只需将路径向量传递给成员函数,该函数就会启动一个新线程来为您工作。
///////////////////////////////////////////////////////////////////// // // CFolderSize::SetPath // // Purpose: set a collection of folders' paths for the thread to start calculation // // Parameters: const vector<CString>& pathV - // a vector of fully qualified folders' paths // // // Returns: // void CFolderSize::SetPath(const vector<CString>& pathV) { vector<CString> vszPaths = pathV; // New calculation will be started, force the old thread (if any) to exit m_bToExit=true; DWORD dwExitCode = 0; GetExitCodeThread(m_hCalcThread, &dwExitCode); if(dwExitCode == STILL_ACTIVE) WaitForSingleObject(m_hCalcThread, INFINITE); if(m_hCalcThread) CloseHandle(m_hCalcThread); std::vector<CFolder>::iterator iter = m_vFolder.begin(); for(; iter != m_vFolder.end();) { // if there are any duplicated paths, we should remove them // find if any current class paths in the new set path vector // In this situation, all the path must be in the same format, // for example, all with or without backslash. std::vector<CString>::iterator iter_find = std::find(vszPaths.begin(), vszPaths.end(), iter->m_szPath); if(vszPaths.end() == iter_find) { // if not find, remove the current class path iter = m_vFolder.erase(iter); if(iter == m_vFolder.end()) break; } else { // If we find it, that is not need to add one more time, // just remove it in the new vector vszPaths.erase(iter_find); iter ++; } } // add new path that is not in the current class paths std::vector<CString>::iterator iter_path = vszPaths.begin(); for(; iter_path != vszPaths.end(); iter_path ++) { m_vFolder.push_back(CFolder(*iter_path, 0, true, false)); } DWORD dwThreadID = 0; m_bToExit = false; //m_dw64TotalSize = 0; //because inside the thread function call one. // start the thread to perform calculation. m_hCalcThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)CalcThreadFunc, (LPVOID)this, 0, &dwThreadID); }
如何使用
事实上,使用这两个类非常简单。您可以按照以下三个步骤操作
- 您必须将源文件添加到您的项目中;然后包含头文件。
- 然后定义一个
CFolderSize
成员变量,并将其传递一个文件夹路径的向量。就像这样CFolderSize folder; std::vector<CString> FolderVec; FolderVec.push_back(_T("C:\\Windows")); // ... other paths folder.SetPath(FolderVec);
- 当您想显示总大小时,只需调用
folder.GetTotalSize();
顺便说一下,文件夹容量的单位是字节。例如,如果总大小是 1000000 字节。向用户显示如此大的数字和如此小的单位并不友好。幸运的是,我编写了一个函数来完成这项繁琐的工作。只需调用...
static CString TrimSizeToUnit(DWORD64 capacity);
...函数,它将返回一个合适的数字及其单位。
历史
- 2010-02-22 首次发布
- 2010-02-26 考虑了
FILE_ATTRIBUTE_REPARSE_POINT
,感谢 lusores 的建议