Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Bug in MFC's CFileFind::GetLength64()

0.00/5 (No votes)
1 Mar 2002 1  
File size returned by CFileFind::GetLength64() is incorrect for files larger than 4Gb

Introduction

MFC provides the CFileFind class to encapsulate the ::FindFirstFile Win32 calls for searching the filesystem. The member function CFileFind::GetLength() returns the length in bytes of the current file, using a DWORD. Since a DWORD is 32bits long, the maximum file length this can cope with is 4 gigabytes.

The documentation states that for files larger than 4 gigabytes, there is the CFileFind::GetLength64() function that returns the size as an __int64.

However, in versions of MFC prior to the MFC7 that comes with Visual Studio.NET, this function does not return correct values for large files, due to a bug in the way the 64-bit arithmetic is done. The CFileFind::GetLength method in MFC7 now returns a ULONGLONG with the correct filesize, and the GetLength64() member function has been removed.

This is the offending code for earlier versions of MFC as used in Visual Studio 6.0 and earlier:

__int64 CFileFind::GetLength64() const
{
	ASSERT(m_hContext != NULL);
	ASSERT_VALID(this);

	if (m_pFoundInfo != NULL)
		return ((LPWIN32_FIND_DATA) m_pFoundInfo)->nFileSizeLow +
		(((LPWIN32_FIND_DATA) m_pFoundInfo)->nFileSizeHigh << 32);
	else
		return 0;
}

Whilst this appears fine at a glance, the problem lies in the shift operation for the high DWORD. It should have been cast to a 64-bit integer before the shift. As the code stands, it performs a 32-bit shift on a 32-bit value.

Whilst you may expect that to always be zero, in fact it gives the same value as before the shift. The compiler uses the x86 opcode shl, which shifts a 32-bit value a given number of places modulo 32.

Therefore the value returned for any file greater than 4 gigabytes is the value of the low 2 bytes added to the high 2 bytes, which is "more incorrect" than the value returned by GetLength().

Solution

The correct expression to get the filesize as a 64-bit integer from the WIN32_FIND_DATA structure pointed to by the m_pFoundInfo member is:

((LPWIN32_FIND_DATA)pFinder->m_pFoundInfo)->nFileSizeLow +
 ((unsigned __int64)((LPWIN32_FIND_DATA)pFinder->m_pFoundInfo)->nFileSizeHigh << 32);

However, this is a protected member, and altering MFC code is not an option, particularly if you are linking dynamically.

The solutions are:

  • Don't use CFileFind; only use the Win32 API functions ::FindFirstFile etc.
  • Create a derived class from CFileFind, implementing the GetLength64() member function correctly, and use this derived class instead of CFileFind.
  • Alter the Afx.h header file to make the m_pFoundInfo member public, so that you can perform the above calculation from the calling code, rather than calling GetLength64(). This is not recommended.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here