为 URLDownloadToFile 函数实现进度条

URLDownloadToFile这个函数,我一开始只是用它来下载配置文件,主要考虑到如果下载较大的文件这个函数会阻塞很长时间,全部下载完毕后才返回,而且无法显示下载进度,今天才知道自己火星了,人家的最后一个参数提供了相关的接口(以前看都不看直接传NULL),查了些资料总算搞清楚怎么用了,记录一下。


1。创建一个IBindStatusCallback的派生类,声明IBindStatusCallback的8个方法。由于IBindStatusCallback继承自IUnknown,所以还要声明IUnknown的3个方法。下面的为个人使用.具体方法的原型声明可以参照MSDN,


2。可以控制显示进度条的是IBindStatusCallback::Onprogress,只要实现这个方法就行,IBindStatusCallback的其他7个方法IE是不会调用的,直接告诉IE这个我没实现,通通 return E_NOTIMPL    (not implemented)。另外IUnknown的AddRef 和 Release 分别是给调用接口增加引用计数 和 减少引用计数的,也用不到直接都返回0就可以了,IUnknown的另一个方法 QueryInterface也 return E_NOTIMPL。


3。派生类创建好之后,就很简单了,直接给URLDownloadToFile的最后一个参数传个指向派生类实例的指针就大功告成了


我定义了一个CBindCallback类,类的声明:


 
 
  1. class CBindCallback : public IBindStatusCallback
  2. {
  3. public:
  4. CBindCallback();
  5. virtual ~CBindCallback();
  6. //接受显示进度窗口的句柄
  7. CUrlDownloadToFileCallbackTestDlg* m_pdlg;
  8. //IBindStatusCallback的方法。除了OnProgress 外的其他方法都返回E_NOTIMPL
  9. STDMETHOD(OnStartBinding)
  10. ( DWORD dwReserved,
  11. IBinding __RPC_FAR *pib)
  12. { return E_NOTIMPL; }
  13. STDMETHOD(GetPriority)
  14. ( LONG __RPC_FAR *pnPriority)
  15. { return E_NOTIMPL; }
  16. STDMETHOD(OnLowResource)
  17. ( DWORD reserved)
  18. { return E_NOTIMPL; }
  19. //OnProgress在这里
  20. STDMETHOD(OnProgress)
  21. ( ULONG ulProgress,
  22. ULONG ulProgressMax,
  23. ULONG ulStatusCode,
  24. LPCWSTR wszStatusText);
  25. STDMETHOD(OnStopBinding)
  26. ( HRESULT hresult,
  27. LPCWSTR szError)
  28. { return E_NOTIMPL; }
  29. STDMETHOD(GetBindInfo)
  30. ( DWORD __RPC_FAR *grfBINDF,
  31. BINDINFO __RPC_FAR *pbindinfo)
  32. { return E_NOTIMPL; }
  33. STDMETHOD(OnDataAvailable)
  34. ( DWORD grfBSCF,
  35. DWORD dwSize,
  36. FORMATETC __RPC_FAR *pformatetc,
  37. STGMEDIUM __RPC_FAR pstgmed)
  38. { return E_NOTIMPL; }
  39. STDMETHOD(OnObjectAvailable)
  40. ( REFIID riid,
  41. IUnknown RPC_FAR *punk)
  42. { return E_NOTIMPL; }
  43. // IUnknown方法.IE 不会调用这些方法的
  44. STDMETHOD(ULONG,AddRef)()
  45. { return 0; }
  46. STDMETHOD(ULONG,Release)()
  47. { return 0; }
  48. STDMETHOD(QueryInterface)
  49. ( REFIID riid,
  50. void __RPC_FAR __RPC_FAR ppvObject)
  51. { return E_NOTIMPL; }
  52. };
  53. //只需实现OnProgress方法,类的实现:
  54. CBindCallback::CBindCallback()
  55. {
  56. }
  57. CBindCallback::~CBindCallback()
  58. {
  59. }
  60. //仅实现OnProgress成员即可
  61. LRESULT CBindCallback::OnProgress(ULONG ulProgress,
  62. ULONG ulProgressMax,
  63. ULONG ulSatusCode,
  64. LPCWSTR szStatusText)
  65. {
  66. CProgressCtrl m_prg = (CProgressCtrl)m_pdlg->GetDlgItem(IDC_PROGRESS);
  67. m_prg->SetRange32( 0,ulProgressMax);
  68. m_prg->SetPos(ulProgress);
  69. CString szText;
  70. szText.Format( “已下载%d%%”, ( int)(ulProgress * 100.0 / ulProgressMax));
  71. (m_pdlg->GetDlgItem(IDC_STATUS))->SetWindowText(szText);
  72. return S_OK;
  73. }
  74. class CBindCallback : public IBindStatusCallback
  75. {
  76. public:
  77. CBindCallback();
  78. virtual ~CBindCallback();
  79. //接受显示进度窗口的句柄
  80. CUrlDownloadToFileCallbackTestDlg m_pdlg;
  81. //IBindStatusCallback的方法。除了OnProgress 外的其他方法都返回E_NOTIMPL
  82. STDMETHOD(OnStartBinding)
  83. ( DWORD dwReserved,
  84. IBinding __RPC_FAR *pib)
  85. { return E_NOTIMPL; }
  86. STDMETHOD(GetPriority)
  87. ( LONG __RPC_FAR *pnPriority)
  88. { return E_NOTIMPL; }
  89. STDMETHOD(OnLowResource)
  90. ( DWORD reserved)
  91. { return E_NOTIMPL; }
  92. //OnProgress在这里
  93. STDMETHOD(OnProgress)
  94. ( ULONG ulProgress,
  95. ULONG ulProgressMax,
  96. ULONG ulStatusCode,
  97. LPCWSTR wszStatusText);
  98. STDMETHOD(OnStopBinding)
  99. ( HRESULT hresult,
  100. LPCWSTR szError)
  101. { return E_NOTIMPL; }
  102. STDMETHOD(GetBindInfo)
  103. ( DWORD __RPC_FAR *grfBINDF,
  104. BINDINFO __RPC_FAR *pbindinfo)
  105. { return E_NOTIMPL; }
  106. STDMETHOD(OnDataAvailable)
  107. ( DWORD grfBSCF,
  108. DWORD dwSize,
  109. FORMATETC __RPC_FAR *pformatetc,
  110. STGMEDIUM __RPC_FAR *pstgmed)
  111. { return E_NOTIMPL; }
  112. STDMETHOD(OnObjectAvailable)
  113. ( REFIID riid,
  114. IUnknown RPC_FAR *punk)
  115. { return E_NOTIMPL; }
  116. // IUnknown方法.IE 不会调用这些方法的
  117. STDMETHOD(ULONG,AddRef)()
  118. { return 0; }
  119. STDMETHOD(ULONG,Release)()
  120. { return 0; }
  121. STDMETHOD(QueryInterface)
  122. ( REFIID riid,
  123. void __RPC_FAR __RPC_FAR ppvObject)
  124. { return E_NOTIMPL; }
  125. };
  126. //只需实现OnProgress方法,类的实现:
  127. CBindCallback::CBindCallback()
  128. {
  129. }
  130. CBindCallback::~CBindCallback()
  131. {
  132. }
  133. //仅实现OnProgress成员即可
  134. LRESULT CBindCallback::OnProgress(ULONG ulProgress,
  135. ULONG ulProgressMax,
  136. ULONG ulSatusCode,
  137. LPCWSTR szStatusText)
  138. {
  139. CProgressCtrl m_prg = (CProgressCtrl)m_pdlg->GetDlgItem(IDC_PROGRESS);
  140. m_prg->SetRange32( 0,ulProgressMax);
  141. m_prg->SetPos(ulProgress);
  142. CString szText;
  143. szText.Format( “已下载%d%%”, ( int)(ulProgress * 100.0 / ulProgressMax));
  144. (m_pdlg->GetDlgItem(IDC_STATUS))->SetWindowText(szText);
  145. return S_OK;
  146. }



调用URLDownloadToFile下载即可

 
 
  1. void CUrlDownloadToFileCallbackTestDlg::DownloadButton() //下载按钮,也可以为线程
  2. {
  3. CBindCallback cbc;
  4. cbc.m_pdlg = this;
  5. ( this->GetDlgItem(IDC_START))->EnableWindow(FALSE); //禁用下载按钮
  6. //在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。
  7. //如果想要从缓存中提取那么就把下面的注释掉
  8. CString szRand;
  9. szRand.Format(_T( "?skq=%d"),GetTickCount());
  10. szUrl += szRand;
  11. if(S_OK == URLDownloadToFile( NULL,szURL,szPath, 0,&cbc)) //szURL,szPath为全局变量
  12. MessageBox( “finished”);
  13. }
  14. void CUrlDownloadToFileCallbackTestDlg::DownloadButton() //下载按钮,也可以为线程
  15. {
  16. CBindCallback cbc;
  17. cbc.m_pdlg = this;
  18. ( this->GetDlgItem(IDC_START))->EnableWindow(FALSE); //禁用下载按钮
  19. //在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。
  20. //如果想要从缓存中提取那么就把下面的注释掉
  21. CString szRand;
  22. szRand.Format(_T( "?skq=%d"),GetTickCount());
  23. szUrl += szRand;
  24. if(S_OK == URLDownloadToFile( NULL,szURL,szPath, 0,&cbc)) //szURL,szPath为全局变量
  25. MessageBox( “finished”);
  26. }


 

Some Tips:

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的设置,完成下载,不用考虑代理情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值