MFC CFileFind::GetLength64() 中的错误





5.00/5 (4投票s)
2002年3月2日
2分钟阅读

80672
CFileFind::GetLength64() 返回的文件大小对于大于 4GB 的文件不正确
引言
MFC 提供了 CFileFind 类来封装 Win32 的 ::FindFirstFile
调用,用于搜索文件系统。成员函数 CFileFind::GetLength()
使用 DWORD
返回当前文件的大小(以字节为单位)。由于 DWORD
是 32 位,因此它可以处理的最大文件长度为 4GB。
文档指出,对于大于 4GB 的文件,可以使用 CFileFind::GetLength64()
函数,该函数以 __int64
的形式返回文件大小。
然而,在 Visual Studio.NET 附带的 MFC7 之前的 MFC 版本中,由于 64 位算术的执行方式存在错误,此函数无法为大文件返回正确的值。MFC7 中的 CFileFind::GetLength
方法现在返回一个带有正确文件大小的 ULONGLONG
,并且 GetLength64()
成员函数已被移除。
以下是早期 MFC 版本中(如 Visual Studio 6.0 及更早版本)使用的有问题的代码:
__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; }
乍一看这似乎没问题,但问题在于高 DWORD
的移位操作。在移位 之前,它应该被转换为 64 位整数。正如代码所示,它对一个 32 位值执行 32 位移位。
虽然你可能期望这始终为零,但实际上它与移位之前的相同值。编译器使用 x86 指令码 shl
,该指令码将一个 32 位值以给定的位数模 32 进行移位。
因此,对于任何大于 4GB 的文件,返回的值是低 2 个字节加上高 2 个字节的值,这比 GetLength()
返回的值“更不正确”。
解决方案
从 m_pFoundInfo
成员指向的 WIN32_FIND_DATA
结构中获取文件大小的正确表达式(作为 64 位整数)是:
((LPWIN32_FIND_DATA)pFinder->m_pFoundInfo)->nFileSizeLow + ((unsigned __int64)((LPWIN32_FIND_DATA)pFinder->m_pFoundInfo)->nFileSizeHigh << 32);
但是,这是一个受保护的成员,并且修改 MFC 代码不是一个选项,尤其是在动态链接时。
解决方案是:
- 不要使用
CFileFind
;仅使用 Win32 API 函数::FindFirstFile
等。 - 从
CFileFind
创建一个派生类,正确实现GetLength64()
成员函数,并使用该派生类代替CFileFind
。 - 修改 Afx.h 头文件,使
m_pFoundInfo
成员变为公共成员,以便你可以从调用代码执行上述计算,而不是调用GetLength64()
。不建议这样做。