Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VC9.0

The Way to Calculate a Collection of Folders Size

4.09/5 (6 votes)
25 Feb 2010CPOL2 min read 1   513  
The way to calculate a collection of folders size with VC++

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:

C++
//////////////////////////////////////////////////////////////////////////
// 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 class is the key to implement the calculation of the folders size:

C++
//////////////////////////////////////////////////////////////////////////
// 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?
}; 

The key to my app to implement calculation are the following three functions.

  • C++
    DWORD64 CFolderSize::CalcFolderSize(CString szPath)

    The first can get a folder (and also its sub folders)'s size.

    C++
    ///////////////////////////////////////////////////////////////
    //
    // 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;
    }
  • C++
    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.

    C++
    ///////////////////////////////////////////////////////////////////
    //
    // 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;   
    }
  • C++
    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.

    C++
    /////////////////////////////////////////////////////////////////////
    //
    // 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);    
    }

How To Use

In fact, it's very simple to use these two class. You can do this in these three steps:

  1. You must add the source files to your project; then include the header file.
  2. Then define a CFolderSize member variable and pass it a vector of folders' path. Just like this:
    C++
    CFolderSize folder;
    std::vector<CString> FolderVec;
    FolderVec.push_back(_T("C:\\Windows"));
    // ... other paths
    folder.SetPath(FolderVec);
    
  3. When you want to display the total size, just call:
  4. C++
    folder.GetTotalSize();  

gradual.png

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...

C++
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)