You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VC++2008中CStdioFile处理2GB+文件GetLength异常的解决方案问询

解决CStdioFile处理2GB以上文件时GetLength()抛出异常的问题

你遇到的问题确实是由于CStdioFile::GetLength()内部依赖32位的ftell()函数导致的——当文件大小超过2GB时,ftell()会因溢出返回-1触发异常,而微软出于向后兼容的考虑,一直没有更新这个方法来使用64位的_ftelli64()函数(即使VC++2017的源码依然保持这一实现)。

下面提供两种无需重写整个CStdioFile类的简易解决方案:

方案1:派生轻量级子类重写GetLength()

这是最贴合原有代码习惯的方案,只需要创建一个继承自CStdioFile的子类,重写GetLength()方法,替换为64位的文件操作函数:

class CStdioFile64 : public CStdioFile
{
public:
    // 重写GetLength方法,使用64位文件操作函数
    ULONGLONG GetLength() const override
    {
        ASSERT_VALID(this);
        ASSERT(m_pStream != nullptr);

        // 保存当前文件指针位置
        __int64 currentPos = _ftelli64(m_pStream);
        if (currentPos == -1)
        {
            AfxThrowFileException(CFileException::invalidFile, _doserrno, m_strFileName);
        }

        // 跳转到文件末尾
        if (_fseeki64(m_pStream, 0, SEEK_END) != 0)
        {
            AfxThrowFileException(CFileException::badSeek, _doserrno, m_strFileName);
        }

        // 获取文件长度(末尾位置)
        __int64 fileLength = _ftelli64(m_pStream);
        if (fileLength == -1)
        {
            AfxThrowFileException(CFileException::invalidFile, _doserrno, m_strFileName);
        }

        // 恢复原来的文件指针位置
        if (_fseeki64(m_pStream, currentPos, SEEK_SET) != 0)
        {
            AfxThrowFileException(CFileException::badSeek, _doserrno, m_strFileName);
        }

        return static_cast<ULONGLONG>(fileLength);
    }
};

使用时只需要把原代码中的CStdioFile替换为CStdioFile64即可,其他逻辑完全不变(顺便修正了你代码里循环条件的赋值错误):

void CTestCStdioFileDlg::OnBnClickedButton1() {
    // TODO: Add your control notification handler code here
    CStdioFile64 MyFile;
    CString strLine;
    ULONGLONG uLength;
    strLine = _T("This is a line.");
    if (MyFile.Open(_T("C:\\Temp\\MyTest.dat"), CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive | CFile::typeBinary)) {
        // 修正原代码的循环条件:将赋值改为比较
        for (UINT uIndex = 0; uIndex <= 200000000; uIndex ++) {
            MyFile.WriteString(strLine);
            uLength = MyFile.GetLength();
        }
        MyFile.Close();
    }
}

方案2:通过文件句柄直接获取大小(无需子类)

如果你不想创建子类,可以通过CStdioFile的FILE*指针获取系统文件句柄,再用Windows API直接获取文件大小:

ULONGLONG GetCStdioFileSize(const CStdioFile& file)
{
    ASSERT_VALID(&file);
    ASSERT(file.m_pStream != nullptr);

    // 从FILE*获取文件描述符
    int fileDescriptor = _fileno(file.m_pStream);
    if (fileDescriptor == -1)
    {
        AfxThrowFileException(CFileException::invalidFile, _doserrno, file.m_strFileName);
    }

    // 从文件描述符获取Windows句柄
    HANDLE hFile = reinterpret_cast<HANDLE>(_get_osfhandle(fileDescriptor));
    if (hFile == INVALID_HANDLE_VALUE)
    {
        AfxThrowFileException(CFileException::invalidFile, GetLastError(), file.m_strFileName);
    }

    // 获取文件大小
    LARGE_INTEGER fileSize;
    if (!GetFileSizeEx(hFile, &fileSize))
    {
        AfxThrowFileException(CFileException::badLength, GetLastError(), file.m_strFileName);
    }

    return fileSize.QuadPart;
}

使用时直接调用这个函数替代GetLength():

uLength = GetCStdioFileSize(MyFile);

方案说明

  • 两种方案都避免了重写整个CStdioFile类,仅针对GetLength()的问题做最小修改。
  • 方案1更符合MFC的类设计习惯,使用起来和原类无差异;方案2则更灵活,不需要修改原有变量类型。

内容的提问来源于stack exchange,提问作者alancc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:48:14