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
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);
if (m_pFoundInfo != NULL)
return ((LPWIN32_FIND_DATA) m_pFoundInfo)->nFileSizeLow +
(((LPWIN32_FIND_DATA) m_pFoundInfo)->nFileSizeHigh << 32);
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
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:
((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
- Create a derived class from
CFileFind, implementing the
GetLength64() member function correctly, and use this derived class instead of
- 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.