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

URLDownloadToFile 實作檔案下載:COM 單體模式回呼介面完整範例

     編輯:關於C++

簡介

URLDownloadToFile (在 urlmon.h/urlmon.lib) 是 Windows 提供的高層下載 API,支援 HTTP/HTTPS/FTP、Proxy、快取、重導向、斷點續傳 (需搭配 IBindStatusCallback)。本範例示範最小化 COM 物件實作 IBindStatusCallback 以接收進度通知。

完整原始碼 (單一 .cpp 可直接編譯)


// URLDownloadDemo.cpp
#define _WIN32_DCOM
#include 
#include 
#include 
#include 

#pragma comment(lib, "urlmon.lib")

//=============================================================================
// IBindStatusCallback 實作
//=============================================================================
class CDownloadCallback : public IBindStatusCallback
{
    long m_cRef;
public:
    CDownloadCallback() : m_cRef(1) {}
    ~CDownloadCallback() {}

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
        *ppv = NULL;
        if (riid == IID_IUnknown || riid == IID_IBindStatusCallback) {
            *ppv = static_cast(this);
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_cRef); }
    STDMETHODIMP_(ULONG) Release() {
        ULONG c = InterlockedDecrement(&m_cRef);
        if (c == 0) delete this;
        return c;
    }

    // IBindStatusCallback - 僅實作必要方法,其餘回傳 E_NOTIMPL
    STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,
                            ULONG ulStatusCode, LPCWSTR szStatusText) {
        if (ulProgressMax > 0) {
            int percent = (int)(ulProgress * 100 / ulProgressMax);
            _tprintf(_T("\r下載進度: %d%% (%lu / %lu bytes)"), percent, ulProgress, ulProgressMax);
        }
        return S_OK;
    }
    STDMETHODIMP OnStopBinding(HRESULT hresult, LPCWSTR szError) {
        if (SUCCEEDED(hresult)) _tprintf(_T("\n下載完成。\n"));
        else _tprintf(_T("\n下載失敗: 0x%08X %s\n"), hresult, szError);
        PostThreadMessage(GetCurrentThreadId(), WM_QUIT, 0, 0); // 結束訊息迴圈
        return S_OK;
    }
    // 其餘方法簡單實作
    STDMETHODIMP OnStartBinding(DWORD, IBinding*) { return S_OK; }
    STDMETHODIMP GetPriority(LONG*) { return E_NOTIMPL; }
    STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC*, STGMEDIUM*) { return S_OK; }
    STDMETHODIMP OnObjectAvailable(REFIID, IUnknown*) { return S_OK; }
    STDMETHODIMP OnLowResource(DWORD) { return S_OK; }
    STDMETHODIMP GetBindInfo(DWORD*, BINDINFO*) { return E_NOTIMPL; }
};

//=============================================================================
// 主程式
//=============================================================================
int _tmain(int argc, _TCHAR* argv[])
{
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    LPCTSTR url = _T("https://127.0.0.1:30000/download/icon.zip");
    LPCTSTR dest = _T("d:\\temp\\icon.zip");

    CDownloadCallback* pCallback = new CDownloadCallback();
    HRESULT hr = URLDownloadToFile(NULL, url, dest, 0, pCallback);
    pCallback->Release();

    if (hr == S_OK) {
        // 等待回呼發送 WM_QUIT (OnStopBinding)
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        _tprintf(_T("檔案已儲存至 %s\n"), dest);
    } else {
        _tprintf(_T("URLDownloadToFile 失敗: 0x%08X\n"), hr);
    }

    CoUninitialize();
    return 0;
}

關鍵點解析

項目說明
URLDownloadToFile 參數pCaller (IUnknown*, 通常 NULL)、szURL、szFileName、dwReserved (0)、pCB (IBindStatusCallback*)
非同步本質函式立即返回,實際下載在背景執行緒進行,透過回呼通知進度/完成/錯誤。
OnProgress定期呼叫,ulProgress/ulProgressMax 為位元組數,可計算百分比、速度。
OnStopBinding下載結束 (成功/失敗/取消) 呼叫,hresult 指示結果。
訊息迴圈主執行緒必須跑 GetMessage/DispatchMessage 迴圈,否則 COM 回呼無法送達 (STA 模式)。
斷點續傳需在 GetBindInfo 設定 BINDINFO.dwBindVerb = BINDVERB_GET、grfBINDF = BINDF_GETNEWESTVERSION | BINDF_RESYNCHRONIZE,並在 OnProgress 中處理 BSCF_DATAFULLY_AVAILABLE 等旗標,配合 IBinding::Abort/Suspend 實作較複雜,建議改用 WinHTTP/WinInet/libcurl 手動控制 Range 標頭。

進階:GUI 版本 (MFC/VCL/Win32)

  • 將 OnProgress 中 PostMessage(hwnd, WM_USER+1, percent, 0) 通知主視窗更新進度條。
  • 主視窗 OnDownloadProgress 處理訊息,m_Progress.SetPos(percent)。
  • 取消按鈕呼叫 pBinding->Abort() (需在 OnStartBinding 保存 IBinding*)。

現代替代方案

API/庫優點
WinHTTP (WinHttpOpen/WinHttpOpenRequest/WinHttpSendRequest/WinHttpReceiveResponse)系統級、支援 HTTP/1.1/2.0、非同步、伺服器端適用、無 COM 開銷
WinInet (InternetOpen/InternetOpenUrl/InternetReadFile)高層、支援快取/Proxy/Cookie、適合用戶端 GUI
IXMLHTTPRequest2現代非同步、支援 HTTP/2、Stream-based、UWP/桌面通用
libcurl跨平台、功能最全、支援所有協議/認證/代理/HTTP2/3
C++ REST SDK (Casablanca)現代 C++、PPL tasks/await、JSON 整合

URLDownloadToFile 適合極簡下載需求、不想引入額外依賴 的場合。複雜下載管理 (多執行緒、佇列、限速、斷點續傳、驗證) 請使用上述現代庫。

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