InternetReadFile 填充缓冲区,但返回读取的零字节

Posted

技术标签:

【中文标题】InternetReadFile 填充缓冲区,但返回读取的零字节【英文标题】:InternetReadFile filling buffer, but returning zero bytes read 【发布时间】:2017-04-07 16:33:51 【问题描述】:

我在为 Windows Compact 2013 编写的 C++ 应用程序中尝试从 Internet 下载文件时遇到了一个非常奇怪的问题。

BOOL WWW::Read(char* buffer, DWORD buffer_size)

    memset(buffer, 0, buffer_size);
    m_dwBytesRead = 0;

    BOOL bResult = InternetReadFile(m_handle, buffer, buffer_size, &m_dwBytesRead);
    if (!bResult)
    
        DWORD dwLastError = GetLastError();
        TCHAR *err;

        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
            NULL, dwLastError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
            (LPTSTR)&err, 0, NULL))
        
            LOGMSG(1, (TEXT("InternetReadFile failed at - (%u) %s\r\n"), dwLastError, err));
            LocalFree(err);
        
    

    // FUDGE
    if (m_dwBytesRead == 0)
    
        DWORD dwZeros = countZeros(buffer, buffer_size);
        if (dwZeros < buffer_size)
        
            m_dwBytesRead = buffer_size;
        
    
    // END OF FUDGE

    return bResult;

我从另一个成员函数重复调用上述函数如下

DWORD dwWritten;

while (!(Read(buffer, DOWNLOAD_BUFFER_SIZE) && m_dwBytesRead == 0))

    WriteFile(m_hDownload, buffer, m_dwBytesRead, &dwWritten, NULL);
    m_dwActualSize += dwWritten;
    ++m_dwChunks;

    if (m_dwBytesRead > 0)
        m_dwInactivity = 0;
    else if (++m_dwInactivity > INACTIVITY_LIMIT)
        return WDS_INACTIVITY;

没有 FUDGE,此函数第一次失败,但在后续调用中正常工作。我在第一次通过此函数调用时遇到的错误是

InternetReadFile failed at - (112) There is not enough space on the disk.

我不明白为什么在 READ 操作期间会收到“磁盘空间不足”错误。我检查了缓冲区是否已分配、可用且与预期大小匹配。事实上,当我检查缓冲区的内容时,我发现它已经填充了预期的字节数,但是 m_dwBytesRead 变量的内容仍然设置为 0。

如您所见,我试图通过检查缓冲区的内容以查看它是否已被填充,然后伪造 m_dwBytesRead 变量来围绕这个特定情况编写代码,但这只是让我临时解决的问题过了这个错误,我真的需要了解为什么会出现这个问题。

这个错误的后果(没有我的软糖)是数据被丢弃,我最终得到一个文件,它缺少第一个块,但在其他方面完全正确。因此 MD5 检查失败,我丢失了文件的第一部分。

我只是碰巧知道文件总是比我正在使用的块大小大,所以我的软糖会起作用,但我不喜欢在代码中使用这些不应该需要的可怕变通方法.

如果有人能解释导致问题的原因,将不胜感激。

我正在使用 Visual Studio 2013 C++(本机 Windows 应用程序,而不是 MFC),目标是 32 位和 Unicode,在 Windows Compact 2013 上运行。

非常感谢, 安德鲁

【问题讨论】:

在“常规”Windows 上运行它会发生什么? 感谢您的建议。我还没有在普通窗口上尝试过。我当然可以尝试(在星期一),看看是否有不同的行为。 一个更好的解决方法(如果可行的话)可能是第一次读取,总是失败,零长度。 谢谢哈利,这是另一个值得考虑的好建议,但正如您所说,这只是一种解决方法。在寻求“最佳”解决方案时,我会牢记这一点。 【参考方案1】:

机器是否真的耗尽了磁盘空间? InternetReadFile 默认会在你背后写入磁盘:

为确保检索到所有数据,应用程序必须继续调用 InternetReadFile 函数,直到该函数返回 TRUE 并且 lpdwNumberOfBytesRead 参数为零。如果将请求的数据写入缓存,这一点尤其重要,因为否则缓存将无法正确更新,下载的文件也不会提交到缓存。 请注意,除非打开数据流的原始请求设置了 INTERNET_FLAG_NO_CACHE_WRITE 标志,否则缓存会自动发生

【讨论】:

我不认为存在空间问题。运行它的设备有一个 500MB 的闪存驱动器,其中 310MB 目前是空闲的。但是,您提出了一个很好的观点,我认为绝对值得为此操作禁用缓存,因为这些下载不需要缓存。感谢您的建议,我一定会(星期一)试试这个,看看这是否会有所作为。我会告诉你的 更新:我在InternetOpenUrl() 调用中添加了INTERNET_FLAG_NO_CACHE_WRITE 标志,但遗憾的是它没有任何区别。我仍然收到“磁盘空间不足”错误。暂时我坚持我的工作。如果我找到解决方案,我会在这里发帖。 在验证代码在“正常”Windows 上运行后,我建议调试该功能或联系 Microsoft... 就像软件开发人员生活中经常发生的那样,我从来没有时间回到这个问题并验证它在“正常”Windows 上的表现。当(如果?)机会出现时,我回到这个问题上并让你知道结果。感谢您的耐心等待

以上是关于InternetReadFile 填充缓冲区,但返回读取的零字节的主要内容,如果未能解决你的问题,请参考以下文章

添加到masm中的缓冲区?

异步使用 InternetReadFile() 的正确方法

InternetReadFile 因分块响应而失败

InternetReadFile() 似乎没有读取互联网数据

InternetReadFile 返回损坏的响应

InternetReadFile 问题(错误 87 - 参数不正确)