2011-08-05 175 views
2

我目前正在編寫一個簡單的程序,我想分發給我的朋友。我試圖完成的任務是在啓動程序時將一些外部二進制文件寫入到互聯網的緩衝區中。爲此,我使用Windows Internet(wininet)。目前,我正在使用InternetReadFile將文件寫入稍後在程序中使用的緩衝區中。但是,文件並未完全讀取,因爲結果大小比服務器上的文件大小要小得多,因爲它應該是相同的。使用Wininet下載二進制文件

我想這樣做,而不使用任何外部庫。

任何想法可以解決我的問題?

感謝, 安德魯

+0

編輯我的答案,包括一個完整的工作程序。你可以試試看看它是否有效? –

回答

7

documentation作如下評論:

工作的InternetReadFile很像基地ReadFile函數,只有少數例外。通常,InternetReadFile從HINTERNET句柄中檢索數據作爲連續的字節流。每次調用InternetReadFile時要讀取的數據量由dwNumberOfBytesToRead參數指定,並且數據將返回到lpBuffer參數中。一個正常的讀取檢索指定的dwNumberOfBytesToRead每次調用InternetReadFile直到文件結束。 爲確保檢索所有數據,應用程序必須繼續調用InternetReadFile函數,直到函數返回TRUE並且lpdwNumberOfBytesRead參數等於零。

基本上,不能保證功能正好讀取dwNumberOfBytesToRead。查看使用lpdwNumberOfBytesRead參數實際讀取的字節數。

此外,只要總文件大小大於dwNumberOfBytesToRead,您將需要多次調用該調用。因爲它不能一次讀取超過dwNumberOfBytesToRead

如果你要提前總文件大小,環路採用以下形式:

::DWORD error = ERROR_SUCCESS; 
::BYTE data[SIZE]; // total file size. 
::DWORD size = 0; 
::DWORD read = 0; 
do { 
    ::BOOL result = ::InternetReadFile(stream, data+size, SIZE-size, &read); 
    if (result == FALSE) { 
     error = ::GetLastError(); 
    } 
} 
while ((error == ERROR_SUCCESS) && (read > 0) && ((size+=read) < SIZE)); 
    // check that `SIZE` was correct. 
if (size != SIZE) { 
} 

如果沒有,那麼你需要在緩衝區中的數據寫入到另一個文件,而不是積累吧。

EDIT(樣本測試程序)

這裏有一個完整的程序,獲取的StackOverflow的頭版。這將以1K塊的形式下載大約200K的HTML代碼,並檢索整個頁面。你可以運行這個,看看它是否工作?

#include <Windows.h> 
#include <Wininet.h> 
#include <iostream> 
#include <fstream> 

namespace { 

    ::HINTERNET netstart() 
    { 
     const ::HINTERNET handle = 
      ::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0); 
     if (handle == 0) 
     { 
      const ::DWORD error = ::GetLastError(); 
      std::cerr 
       << "InternetOpen(): " << error << "." 
       << std::endl; 
     } 
     return (handle); 
    } 

    void netclose (::HINTERNET object) 
    { 
     const ::BOOL result = ::InternetCloseHandle(object); 
     if (result == FALSE) 
     { 
      const ::DWORD error = ::GetLastError(); 
      std::cerr 
       << "InternetClose(): " << error << "." 
       << std::endl; 
     } 
    } 

    ::HINTERNET netopen (::HINTERNET session, ::LPCWSTR url) 
    { 
     const ::HINTERNET handle = 
      ::InternetOpenUrlW(session, url, 0, 0, 0, 0); 
     if (handle == 0) 
     { 
      const ::DWORD error = ::GetLastError(); 
      std::cerr 
       << "InternetOpenUrl(): " << error << "." 
       << std::endl; 
     } 
     return (handle); 
    } 

    void netfetch (::HINTERNET istream, std::ostream& ostream) 
    { 
     static const ::DWORD SIZE = 1024; 
     ::DWORD error = ERROR_SUCCESS; 
     ::BYTE data[SIZE]; 
     ::DWORD size = 0; 
     do { 
      ::BOOL result = ::InternetReadFile(istream, data, SIZE, &size); 
      if (result == FALSE) 
      { 
       error = ::GetLastError(); 
       std::cerr 
        << "InternetReadFile(): " << error << "." 
        << std::endl; 
      } 
      ostream.write((const char*)data, size); 
     } 
     while ((error == ERROR_SUCCESS) && (size > 0)); 
    } 

} 

int main (int, char **) 
{ 
    const ::WCHAR URL[] = L"http://stackoverflow.com/"; 
    const ::HINTERNET session = ::netstart(); 
    if (session != 0) 
    { 
     const ::HINTERNET istream = ::netopen(session, URL); 
     if (istream != 0) 
     { 
      std::ofstream ostream("output.txt", std::ios::binary); 
      if (ostream.is_open()) { 
       ::netfetch(istream, ostream); 
      } 
      else { 
       std::cerr << "Could not open 'output.txt'." << std::endl; 
      } 
      ::netclose(istream); 
     } 
     ::netclose(session); 
    } 
} 

#pragma comment (lib, "Wininet.lib") 
+0

謝謝你的回覆。我在while循環中調用了InternetReadFile,直到它返回true,但該文件仍然損壞並且大小不正確。 – Andrew

+0

@Andrew:再次閱讀文檔。對於任何「成功」條件都返回「TRUE」,包括「只讀取1個字節的數據」。它應該第一次返回「TRUE」,所以不要循環,直到它返回「TRUE」。 –

+0

@Andrew:你有沒有試過在任何地方複製數據,只計算函數返回的字節總數?確保這將返回適當的字節數以開始。 –