Introduction
I got a lot of help from CodeProject, now I want to post one small program to help others at the same time to make progress. I hope it's helpful to others.
Sometimes, we want to give our users the folders' size they select on the fly, the same like we click on a folder property shows the folder's size will increase continually. How do we get the folder/file/drive size. Of course, we can get some similar articles on CodeProject such as Hans Dietrich's project. His app can obtain any folder's capacity. But unfortunately, it can only get one folder's size at one time. In my demo project, I implement a method to get a collection of folders' size, but not only one, in a new thread. So it will not affect the main responsibility.
By the way, my project is provided to get a vector of folder's size, it can even get a drive's size. But you know, this process is time-consuming. If you just want to get a drive's capacity, I recommend you to use the Windows API function, such as GetDiskFreeSpace
instead.
In my project, I borrow a demo dialog CFolderTreeCtrl from Adrien Pinet. For his folder, tree control is very useful. To demo my project, I can select or unselect any folders in the tree control checkbox.
Implementation Details
CFolder
class is a very simple folder class to keep the necessary information to calculate a folder's size as follows:
class CFolder
{
public:
CString m_szPath; DWORD64 m_dw64Size; bool m_bSelected; bool m_bCalculated;
public:
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)
{
}
bool operator==(const CFolder& folder) const
{
return m_szPath==folder.m_szPath;
}
bool operator==(const CString& str) const
{
return m_szPath==str;
}
};
CFolderSize
class is the key to implement the calculation of the folders size:
class CFolderSize
{
public:
CFolderSize(HANDLE handle=NULL, bool bCalCompleted=false,
bool bToExit=false, DWORD64 dwSize=0) ;
~CFolderSize();
bool IsCompleted() const
{
return m_bCalCompleted;
}
bool IsCalledToExit() const
{
return m_bToExit;
}
DWORD64 GetTotalSize() const
{
return m_dw64TotalSize;
}
vector<CFolder>* GetAllFolder()
{
return &m_vFolder;
}
void SetPath(const vector<CString>& pathV);
static DWORD64 CalcThreadFunc(LPVOID pParam);
DWORD64 CalcFolderSize(CString szPath);
static CString TrimSizeToUnit(DWORD64 capacity);
private:
vector<CFolder> m_vFolder; DWORD64 m_dw64TotalSize; HANDLE m_hCalcThread; bool m_bToExit; bool m_bCalCompleted; };
The key to my app to implement calculation are the following three functions.
-
DWORD64 CFolderSize::CalcFolderSize(CString szPath)
The first can get a folder (and also its sub folders)'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;
do
{
if(m_bToExit) {
return 0; }
if( (0 == _tcscmp(fileinfo.cFileName, _T("."))) ||
(0 == _tcscmp(fileinfo.cFileName, _T(".."))) ||
fileinfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
continue; else if(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ CString szSubFolder = szPath;
szSubFolder.Delete(szSubFolder.GetLength()-1, 1); szSubFolder += fileinfo.cFileName;
szSubFolder += _T("\\");
CalcFolderSize(szSubFolder);
}
else { m_dw64TotalSize += (((DWORD64)fileinfo.nFileSizeHigh)
<<(sizeof(DWORD)*8)) + fileinfo.nFileSizeLow;
}
}while(FindNextFile(hFind, &fileinfo));
FindClose(hFind);
return m_dw64TotalSize;
}
-
DWORD64 CFolderSize::CalcThreadFunc(LPVOID pParam)
is a thread callback function. In the function, it will call DWORD64 CFolderSize::CalcFolderSize(CString szPath)
to get folders' capacity.
DWORD64 CFolderSize::CalcThreadFunc(LPVOID pParam)
{
CFolderSize* pClass=(CFolderSize*)pParam;
if(!pParam)
return 0;
vector<CFolder>* pVector=pClass->GetAllFolder();
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) {
return 0;
}
else if((it->m_bCalculated ==false) && it->m_bSelected)
{
DWORD64 dwOriginalSize = pClass->GetTotalSize();
CString str=it->m_szPath;
pClass->CalcFolderSize(str);
DWORD64 dwNewSize = pClass->GetTotalSize();
it->m_bCalculated=true;
it->m_dw64Size=dwNewSize-dwOriginalSize; }
else if(it->m_bCalculated && it->m_bSelected) pClass->m_dw64TotalSize+=it->m_dw64Size;
}
pClass->m_bCalCompleted=TRUE;
return pClass->m_dw64TotalSize;
}
-
void CFolderSize::SetPath(const vector<CString>& pathV)
This function is the main interface for user. Just give the member function a vector of paths, the function will start a new thread to work for you.
void CFolderSize::SetPath(const vector<CString>& pathV)
{
vector<CString> vszPaths = pathV;
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();)
{
std::vector<CString>::iterator iter_find =
std::find(vszPaths.begin(), vszPaths.end(), iter->m_szPath);
if(vszPaths.end() == iter_find)
{
iter = m_vFolder.erase(iter);
if(iter == m_vFolder.end())
break;
}
else
{
vszPaths.erase(iter_find);
iter ++;
}
}
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_hCalcThread = CreateThread(0, 0,
(LPTHREAD_START_ROUTINE)CalcThreadFunc, (LPVOID)this, 0, &dwThreadID);
}
How To Use
In fact, it's very simple to use these two class. You can do this in these three steps:
- You must add the source files to your project; then include the header file.
- Then define a
CFolderSize
member variable and pass it a vector of folders' path. Just like this:
CFolderSize folder;
std::vector<CString> FolderVec;
FolderVec.push_back(_T("C:\\Windows"));
folder.SetPath(FolderVec);
- When you want to display the total size, just call:
folder.GetTotalSize();
By the way, the unit of the folder's capacity is bytes. For example, if the total size is 1000000 bytes. It is not very friendly to show our user such a large number with such a small unit. Fortunately, I wrote a function to do this tiresome business. Just call the...
static CString TrimSizeToUnit(DWORD64 capacity);
... function, it will return a proper number and its unit.
History
- 2010-02-22 First release
- 2010-02-26 Take
FILE_ATTRIBUTE_REPARSE_POINT
into consideration, thanks to lusores' advice