Thread: GetFileSize() and directories

  1. #1
    Registered User aLiNuSh's Avatar
    Join Date
    Aug 2007
    Location
    127.0.0.1
    Posts
    7

    GetFileSize() and directories

    Hi,

    I'm trying to get the size of a directory and I came up with two ways...
    1. I used FindFirstFile() and FindNextFile() and searched the directory tree for files, adding the size of each file and finally getting the directory size.
    2. I opened the directory with CreateFile() using the FILE_FLAG_BACKUP_SEMANTICS flag and then called GetFileSize() with the handle returned from CreateFile(). Even though MSDN says that one can pass such handles to GetFileSize(), the function keeps returning 0 and GetLastError() returns 0 indicating no error has occurred.

    I can't really understand why GetFileSize() returns 0 instead of the actual directory size, so if anyone has some answers or any other methods of getting the size of a directory that would be great.

    Thanks.

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    And what exactly did you expect to get back - just because you can pass in a handle to a function and not receive an error doesn't mean that the function will necessarily do what you expect it to do - for example zero is a perfectly valid "size" for a directory in one way to view it - there is no other information in a directory than a list of files, and you don't "read" directories that way, and I expect that even BackupRead on a directory will return zero bytes read.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    FindFirst / FindNext is the way to do it.

    gg

  4. #4
    Registered User aLiNuSh's Avatar
    Join Date
    Aug 2007
    Location
    127.0.0.1
    Posts
    7
    I expected to get back the correct directory size, what else could I have expected to get back?
    just because you can pass in a handle to a function and not receive an error doesn't mean that the function will necessarily do what you expect it to do
    Well, right, not what I expect it to do but what it's supposed to do...
    So if GetFileSize() is supposed to retrieve the size of a directory, why would it return 0 bytes (that is, an incorrect size) and pretend not to fail? Maybe it's not supposed to return directory sizes after all...
    there is no other information in a directory than a list of files, and you don't "read" directories that way, and I expect that even BackupRead on a directory will return zero bytes read.
    I am aware of that, as I said, at first I successfully used the FindFirstFile() and FindNextFile() APIs to compute the size, but then I don't see why Windows would let you use GetFileSize() on directory handles (opened using CreateFile() and FILE_FLAG_BACKUP_SEMANTICS) knowing that it doesn't do anything.
    FindFirst / FindNext is the way to do it.
    That's how it worked for me but I thought there are other methods too...

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    If by the size of directory, you mean the size of the files IN the directory, the no, GetFileSize will not return that. It _MAY_ give you the size of the actual content in the directory, but as it turns out, it doesn't - it returns zero - which is a valid return value (e.g. it's the number of bytes of data that you can read out of the directory - it may return other values if you have ACL attributes or other "extra metadata" that is sort of part of the user-data belonging to a directory), but not particularly meaningful if you want to know the size of the files in the directory.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273
    This is also logical as far as file systems go - FAT/NTFS in particular do not store the size of a folder anywhere, so to call GetFileSize on one, assuming it wasn't handled by a special case (which it is, hence not a failure), would have Windows take one look at the file system and come back with "Nope, I can't see a size value there".

    It doesn't fail because of those situations where you're iterating the sizes of everything within a folder, which may include other folders. A badly-caught failure will mess up the running total you have. Otherwise you'll have the correct size of the files.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Two side notes:

    1) GetFileSize() doesn't return 0 on error.
    2) The GetFileSize() MSDN page mentions no less than three times that you shouldn't use it. You should use GetFileSizeEx() instead, which doesn't have a braindead interface.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Registered User aLiNuSh's Avatar
    Join Date
    Aug 2007
    Location
    127.0.0.1
    Posts
    7
    Thanks for the answers guys and here's how I implemented my GetDirectorySize() function...

    Code:
    BOOL MyGetFileSize (const TCHAR * szPath, ULONGLONG * ptrSize)
    {
    	LARGE_INTEGER size;
    	HANDLE hFile;
    	
    	hFile = CreateFile (szPath, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    	
    	if (hFile != INVALID_HANDLE_VALUE) {
    		if (GetFileSizeEx (hFile, &size)) {
    			*ptrSize = size.QuadPart;
    			CloseHandle (hFile);
    			return TRUE;
    		} else {
    			CloseHandle (hFile);
    			return FALSE;
    		}
    	}
    	
    	return FALSE;
    }
    
    // GetDirectorySize(), stores the directory size (in bytes) in 'ptrSize' and returns TRUE to indicate no error occurred 
    // or FALSE to indicate that something went wrong and the size might not be accurate
    // WARNING: You need to append a '\' to the directory name before calling the function
    BOOL GetDirectorySize (const TCHAR * szPath, ULONGLONG * ptrSize)
    {
    	WIN32_FIND_DATA find;
    	BOOL returnCode = TRUE;
    	HANDLE hFind;
    	TCHAR * szFullPath = (TCHAR *) malloc ((MAX_PATH + 1)* sizeof (TCHAR));
    
    	// WARNING: You need to append a '\' to the directory name before calling the function
    	wsprintf (szFullPath, TEXT("%s*"), szPath);
    	hFind = FindFirstFile (szFullPath, &find);
    	
    	if (hFind == INVALID_HANDLE_VALUE) {
    		return FALSE;
    	}
    	
    	// Are we dealing with a directory
    	if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    	{
    		if (lstrcmp (find.cFileName, TEXT(".")) && lstrcmp (find.cFileName, TEXT("..")))
    		{
    			size_t pos = lstrlen (find.cFileName) - 1;
    			if (find.cFileName[pos] != '\\') {
    				find.cFileName[pos+1] = '\\';
    				find.cFileName[pos+2] = 0;
    			}
    			
    			wsprintf (szFullPath, TEXT("%s%s"), szPath, find.cFileName);
    			returnCode &= GetDirectorySize (szFullPath, ptrSize);
    		}
    	}
    	else // ... or are we dealing with a file
    	{
    		ULONGLONG fileSize = 0;
    		wsprintf (szFullPath, TEXT("%s%s"), szPath, find.cFileName);
    
    		if (MyGetFileSize (szFullPath, &fileSize))
    			*ptrSize = (*ptrSize) + fileSize;
    		else
    			returnCode = FALSE;
    	}
    
    	while (FindNextFile (hFind, &find))
    	{
    		// Are we dealing with a directory
    		if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    		{
    			if (lstrcmp (find.cFileName, TEXT(".")) && lstrcmp (find.cFileName, TEXT("..")))
    			{
    				size_t pos = lstrlen (find.cFileName) - 1;
    				if (find.cFileName[pos] != L'\\') {
    					find.cFileName[pos+1] = L'\\';
    					find.cFileName[pos+2] = 0;
    				}
    			
    				wsprintf (szFullPath, TEXT("%s%s"), szPath, find.cFileName);
    				returnCode &= GetDirectorySize (szFullPath, ptrSize);
    			}
    		} 
    		else // ... or are we dealing with a file
    		{
    			ULONGLONG fileSize = 0;
    			wsprintf (szFullPath, TEXT("%s%s"), szPath, find.cFileName);
    			if (MyGetFileSize (szFullPath, &fileSize))
    				*ptrSize = (*ptrSize) + fileSize;
    			else
    				returnCode = FALSE;
    		}
    	}
    
    	if (GetLastError() != ERROR_NO_MORE_FILES)
    		returnCode = FALSE;
    
    
    	FindClose (hFind);
    	free (szFullPath);
    	return returnCode;
    }

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The only problem with the FindNextFile/FindFirstFile is that they won't return directories if you use any other pattern than *.*. Keep that in mind. Otherwise it seems correct.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed