65.9K
CodeProject 正在变化。 阅读更多。
Home

MFC CFileFind::GetLength64() 中的错误

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2002年3月2日

2分钟阅读

viewsIcon

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()。不建议这样做。
© . All rights reserved.