首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++Builder 多執行緒下載與進度條追蹤實作 (WinInet/CInternetSession)

     編輯:VC 教學

架構概覽

  • 主執行緒:負責 UI 回應、啟動/取消下載、顯示進度。
  • 工作執行緒:執行實際下載迴圈、定期發送進度訊息至主執行緒。
  • 通訊機制:PostMessage、Synchronize 或佇列 + 定時器輪詢。

核心下載邏輯 (工作執行緒)

// 使用 MFC CInternetSession / CStdioFile (亦可用 WinInet API)
CInternetSession session("MyDownloader");
CStdioFile* pRemoteFile = session.OpenURL(url,
    1, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_RELOAD);

if (!pRemoteFile) return;  // 開啟失敗

// 取得檔案大小 (作為進度條最大值)
pRemoteFile->SeekToEnd();
DWORD dwFileSize = pRemoteFile->SeekToBegin();
m_ProgressCtrl.SetRange32(0, dwFileSize);

// 本地檔案
CFile localFile;
localFile.Open(localPath, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary);

const int BUF_SIZE = 8192;
BYTE buffer[BUF_SIZE];
DWORD dwTotalRead = 0;
COleDateTime startTime = COleDateTime::GetCurrentTime();

while (true) {
    UINT nRead = pRemoteFile->Read(buffer, BUF_SIZE);
    if (nRead == 0) break;

    localFile.Write(buffer, nRead);
    dwTotalRead += nRead;

    // 計算速度與進度
    COleDateTimeSpan elapsed = COleDateTime::GetCurrentTime() - startTime;
    double secs = elapsed.GetTotalSeconds();
    double kbSec = (dwTotalRead / 1024.0) / (secs > 0 ? secs : 1);
    int percent = (dwFileSize > 0) ? (dwTotalRead * 100 / dwFileSize) : 0;

    // 發送進度至 UI (自訂訊息 WM_USER+1, wParam=percent, lParam=(LPARAM)kbSec)
    ::PostMessage(hWndMain, WM_DOWNLOAD_PROGRESS, percent, (LPARAM)(kbSec * 100));

    // 檢查取消旗標
    if (m_bCancel) break;
}

pRemoteFile->Close();
localFile.Close();
delete pRemoteFile;

進度條 UI 更新 (主執行緒)

// 訊息對應
ON_MESSAGE(WM_DOWNLOAD_PROGRESS, OnDownloadProgress)

LRESULT CMainDlg::OnDownloadProgress(WPARAM wParam, LPARAM lParam) {
    int percent = (int)wParam;
    double kbSec = (double)lParam / 100.0;
    m_Progress.SetPos(percent);
    m_lblPercent.SetWindowText(Format("%d%%", percent));
    m_lblSpeed.SetWindowText(Format("%.1f KB/s", kbSec));
    return 0;
}

斷點續傳核心 (HTTP Range 標頭)

// 在 OpenURL 前設定 Range 標頭
CString strHeaders;
strHeaders.Format("Range: bytes=%lu-\r\n", dwAlreadyDownloaded);
pRemoteFile = session.OpenURL(url, 1,
    INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_RELOAD,
    strHeaders);  // 注意:OpenURL 的第 4 參數為 headers

// 伺服器回應 206 Partial Content,內容從指定位置開始
// 本地檔案以 modeNoTruncate 開啟並 SeekToEnd 追加寫入

多執行緒下載 (分段並行)

  1. 先用 HEAD 取得 Content-Length 並確認支援 Accept-Ranges: bytes。
  2. 將檔案分為 N 段 (如 4 段),每段建立一個下載執行緒,各自請求對應 Range。
  3. 各執行緒寫入各自暫存檔,全部完成後合併 (或預先分配大檔案,各執行緒 Seek 寫入對應位置,需檔案系統支援同步)。
  4. 合併策略:簡單可靠 → 各段下載完成後依序 CopyFile 追加到最終檔案。

錯誤處理與健壯性

  • 網路中斷:捕捉 CInternetException,記錄錯誤碼,支援重試 (指數退避)。
  • 伺服器不支援 Range:回退單執行緒完整下載。
  • 磁碟空間不足:GetDiskFreeSpaceEx 預檢。
  • UI 執行緒安全:所有控件操作必須在主執行緒 (用 PostMessage/Synchronize)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved