C++ 下载文件 WinInet - 0kb 写入文件

标签 c++ winapi wininet

有人可以告诉我我的代码有什么问题吗?

我正在尝试使用 WinInet 从 Internet 下载文件。该函数可以很好地连接到目标站点,我不明白为什么这段代码不起作用。谁能帮帮我吗?

这是我的代码:

HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
if (hFile != INVALID_HANDLE_VALUE || GetLastError() == ERROR_ALREADY_EXISTS)
{
  CHAR Buffer[2048];
  DWORD BytesRead=0, BytesToRead=0;
  DWORD BytesWritten=0, BytesToWrite=0;
  SetFilePointer(hFile, 0, 0, FILE_BEGIN);
  do
  {
    if (BytesRead)
    {
      WriteFile(hFile, Buffer, BytesWritten, &BytesToWrite, FALSE);
    }
  }
  while
  (InternetReadFile(hRequest, (LPVOID)Buffer, BytesToRead, &BytesRead) != FALSE);
}
CloseHandle(hFile);
}

hRequest 被传递给函数,它是来自 HttpOpenRequestA 的 HINTERNET 句柄。

最佳答案

您的代码存在一些逻辑问题。

  1. 您在调用 CreateFileW() 时误用了 GetLastError()。无论文件是否已存在,如果成功创建/打开文件,CreateFileW() 都不会返回 INVALID_HANDLE。这就是您需要检查的全部内容(仅当 CreateFileW() 失败并且您想找出原因时才调用 GetLastError())。此外,根本不需要调用 SetFilePointer(),因为 CREATE_ALWAYS 确保打开的文件为空,如果文件已经存在并且其中包含数据,则会截断该文件。

  2. 您的 do..while 循环应该改为 while 循环,以便首先调用 InternetReadFile() 。在第一次循环迭代时跳过 WriteFile() 是没有意义的。如果您使用 do..while 循环,则不应将 InternetReadFile() 用作循环条件。

  3. 更重要的是,只有当 InternetReadFile() 因错误而失败时,您才会中断循环。您期望它在到达响应末尾时失败,但它实际上返回 TRUE 并将 BytesRead 设置为 0。这是记录的行为,您根本没有处理:

    InternetReadFile function

    InternetReadFile operates much like the base ReadFile function, with a few exceptions. Typically, InternetReadFile retrieves data from an HINTERNET handle as a sequential stream of bytes. The amount of data to be read for each call to InternetReadFile is specified by the dwNumberOfBytesToRead parameter and the data is returned in the lpBuffer parameter. A normal read retrieves the specified dwNumberOfBytesToRead for each call to InternetReadFile until the end of the file is reached. To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero. This is especially important if the requested data is written to the cache, because otherwise the cache will not be properly updated and the file downloaded will not be committed to the cache. Note that caching happens automatically unless the original request to open the data stream set the INTERNET_FLAG_NO_CACHE_WRITE flag.

    ReadFile function

    When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets *lpNumberOfBytesRead to zero.

  4. 调用 WriteFile() 时,您将 BytesWritten 传递给 nNumberOfBytesToWrite 参数,但 BytesWritten > 永远不会设置为 0 以外的任何值,因此不会将任何内容写入文件。您需要改为传递 BytesRead

话虽如此,使用更像这样的东西:

HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
    // handle error as needed...
}
else
{
    BYTE Buffer[2048];
    DWORD BytesRead, BytesWritten;

    do
    {
        if (!InternetReadFile(hRequest, Buffer, sizeof(Buffer), &BytesRead))
        {
            // handle error as needed...
            break;
        }

        if (!BytesRead)
            break;

        if (!WriteFile(hFile, Buffer, BytesRead, &BytesWritten, FALSE))
        {
            // handle error as needed...
            break;
        }
    }
    while (true);

    CloseHandle(hFile);
}

MSDN 甚至提供了如何使用 InternetReadFile() 的完整示例:

HOWTO: Using InternetReadFile To Get File

BOOL GetFile (HINTERNET IN hOpen, // Handle from InternetOpen()
              CHAR *szUrl,        // Full URL
              CHAR *szFileName)   // Local file name
{
   DWORD dwSize;
   CHAR   szHead[] = "Accept: */*\r\n\r\n";
   VOID * szTemp[25];
   HINTERNET  hConnect;
   FILE * pFile;

   if ( !(hConnect = InternetOpenUrl ( hOpen, szUrl, szHead,
         lstrlen (szHead), INTERNET_FLAG_DONT_CACHE, 0)))
   {
      cerr << "Error !" << endl;
      return 0;
   }

   if  ( !(pFile = fopen (szFileName, "wb" ) ) )
   {
      cerr << "Error !" << endl;
      return FALSE;
   }
   do
   {
      // Keep coping in 25 bytes chunks, while file has any data left.
      // Note: bigger buffer will greatly improve performance.
      if (!InternetReadFile (hConnect, szTemp, 50,  &dwSize) )
      {
         fclose (pFile);
         cerr << "Error !" << endl;
         return FALSE;
      }
      if (!dwSize)
         break;  // Condition of dwSize=0 indicate EOF. Stop.
      else
         fwrite(szTemp, sizeof (char), dwSize , pFile);
   }   // do
   while (TRUE);
   fflush (pFile);
   fclose (pFile);
   return TRUE;
}

关于C++ 下载文件 WinInet - 0kb 写入文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32509678/

相关文章:

c++ - 为什么编译器不再使用严格的别名来优化此UB

c++ - 初始化 char 数组以保存非 null 终止的字符串

c++ - 访问 COM 对象中的不透明数据成员

c - "."真的是 _wfopen_s/_wfindfirst 允许的通配符吗?

c - Visual-C Win32 API 分层窗口闪烁

caching - 如果 FindNextUrlCacheEntry() 失败,我如何重新检索失败条目的信息?

c++ - 访问冲突读取位置 : Linked List c++

winapi - 为什么我不能绑定(bind)到 winproc?

c++ - WinInet 和 InternetOpen

c++ - 为什么在不同的连接(新的 InternetOpen 和新的 InternetConnect)上两次调用 HttpSendRequest 会带来不同的结果?