c++のurlmon實現下載文件並進度回調


主文件:
#include "stdafx.h"
#include <UrlMon.h>
#pragma comment(lib, "urlmon.lib")
#include <tchar.h>
#include "cbindCallBack.h"
#include "iostream"
#include <CString>

int main()
{

    //在url后添加隨機數,防止從IE緩存中讀取。url后加隨機數不會影響下載的。  
    //如果想要從緩存中提取那么就把下面的注釋掉  

    DWORD rand= GetTickCount();
    
    CBindCallback cbc;
    HRESULT hr=URLDownloadToFile(NULL, _T("http://dldir1.qq.com/qqfile/qq/QQ8.9/19983/QQ8.9.exe"), _T("e:\\download\\qq8.9.exe"), NULL, &cbc);
    if (hr==S_OK)
    {
        std::cout << "下載完成!" << std::endl;
        system("e:\\download\\qq8.9.exe");
    }
    else if (hr== E_OUTOFMEMORY)
    {
        std::cout << "緩沖區長度無效,或內存不足,無法完成操作!" << std::endl;
    }
    else if (hr== E_OUTOFMEMORY)
    {
        std::cout << "指定的資源或回調接口無效!" << std::endl;
    }

    getchar();
    return 0;
}


cbindCallback.h

#pragma once
#include "stdafx.h"
#include <UrlMon.h>
#pragma comment(lib, "urlmon.lib")
#include <tchar.h>
class CBindCallback : public IBindStatusCallback
{
public:
        CBindCallback();
        virtual ~CBindCallback();
        
        //接受顯示進度窗口的句柄  
        //CUrlDownloadToFileCallbackTestDlg* m_pdlg;
        
        //IBindStatusCallback的方法。除了OnProgress     外的其他方法都返回E_NOTIMPL           
        STDMETHOD(OnStartBinding)
        (DWORD dwReserved,
            IBinding __RPC_FAR *pib)
        { return E_NOTIMPL; }
        
        STDMETHOD(GetPriority)
        (LONG __RPC_FAR *pnPriority)
        { return E_NOTIMPL; }
    
        STDMETHOD(OnLowResource)
        (DWORD reserved)
        { return E_NOTIMPL; }
        
        //OnProgress在這里  
        STDMETHOD(OnProgress)
        (ULONG ulProgress,
            ULONG ulProgressMax,
            ULONG ulStatusCode,
            LPCWSTR wszStatusText);
        
        STDMETHOD(OnStopBinding)
        (HRESULT hresult,
            LPCWSTR szError)
        { return E_NOTIMPL; }
        
        STDMETHOD(GetBindInfo)
        (DWORD __RPC_FAR *grfBINDF,
            BINDINFO __RPC_FAR *pbindinfo)
        { return E_NOTIMPL; }
    
        STDMETHOD(OnDataAvailable)
        (DWORD grfBSCF,
            DWORD dwSize,
            FORMATETC __RPC_FAR *pformatetc,
            STGMEDIUM __RPC_FAR *pstgmed)
        { return E_NOTIMPL; }
        
        STDMETHOD(OnObjectAvailable)
        (REFIID riid,
        IUnknown __RPC_FAR *punk)
        { return E_NOTIMPL; }
        
        // IUnknown方法.IE 不會調用這些方法的  
            
        STDMETHOD_(ULONG, AddRef)()
        { return 0; }
        
        STDMETHOD_(ULONG, Release)()
        { return 0; }
        
        STDMETHOD(QueryInterface)
        (REFIID riid,
        void __RPC_FAR *__RPC_FAR *ppvObject)
        { return E_NOTIMPL; }
};


cbindCallBack.cpp

#include "stdafx.h"
#include "cbindCallBack.h"
#include "iostream"
using namespace std;

//只需實現OnProgress方法,類的實現:  
CBindCallback::CBindCallback()
{

}

CBindCallback::~CBindCallback()
{

}
//////僅實現OnProgress成員即可  
LRESULT CBindCallback::OnProgress(ULONG ulProgress,
    ULONG ulProgressMax,
    ULONG ulSatusCode,
    LPCWSTR szStatusText)
{
    /*CProgressCtrl* m_prg = (CProgressCtrl*)m_pdlg->GetDlgItem(IDC_PROGRESS);
    m_prg->SetRange32(0, ulProgressMax);
    m_prg->SetPos(ulProgress);

    CString szText;
    szText.Format("已下載%d%%", (int)(ulProgress * 100.0 / ulProgressMax));
    (m_pdlg->GetDlgItem(IDC_STATUS))->SetWindowText(szText);*/
    cout << "文件大小為:" << ulProgressMax /1024/1024 << "MB"<<endl;
    cout << ulProgress/1024/1024<<"MB" << endl;
    cout << "已下載:" << ulProgressMax*100.0/ulProgressMax << "%" << endl;
    return S_OK;
}

注意事項:

1、下載代碼最好放到一個線程里,否則URLDownloadToFile下載過程中等待返回時會阻塞,使UI失去響應。 
2、OnProgress返回S_OK表示正常,還可以通過返回E_ABORT使下載中斷,所以可以設置個超時時間,如果超時的話,就讓OnProgress返回E_ABORT。另外下次再開始從同一個url下載同一個文件時會直接由IE緩存中讀取已下載的部分,達到“斷點續傳”的效果。
3、實際測試過程中發現URLDownloadToFile讀IE緩存中已經下載的文件會有很大的安全隱患,如果哪次下載的文件發生問題,那么在不清除緩存的情況下,這個函數以后會一直讀取損壞的文件而不重新下載。網上搜了一下解決方案,大概有三種:
      a.下載前用FindFirstUrlCacheEntry,FindNextUrlCacheEntry,DeleteUrlCacheEntry清除cache,這個代碼網上很多。
      b.重載IBindStatusCallback的GetBindInfo方法,指定BINDF_GETNEWESTVERSION和BINDF_NOWRITECACHE屬性,但是我測試發現即使指定這兩個屬性UrlDownloadToFile還是會很執着的讀緩存,郁悶。
      c.還有一種方法比較猥瑣,在要下載的文件地址后加一個隨機字符串,這樣既不會影響正常下載(下載時會被指向正確的地址)而且由於每次傳給URLDownloadToFile的url都不同,在cache中沒有地址匹配的文件,所以會重新下載。上面的代碼就使用了這種方法,個人感覺比較省事而且經測試有效。 
4、CBindCallback有個成員變量用來傳遞進度條所在的窗口句柄m_pdlg,當然這個也可以用其他方式實現。 
5、URLDownloadToFile的好處在於它會自動使用IE的設置,完成下載,不用考慮代理情況。

 

 

 

關閉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM