URLDownloadToFile下载文件文章两篇
2014-07-30 10:16
555 查看
From:http://hi.baidu.com/qazssaeooiciqsr/item/eaeb813a284ef0677c034b85
最近使用 MFC的CInternetSession类下载文件时遇到一些问题, CInternetSession::OpenUrl 这个方法在使用个别代理的网络环境或者网速慢得要死的情况下,它会抛一个连接超时的异常,虽然catch 这个异常可以避免runtime error ,但下载也中止了。不过MSDN中提到SetOption可以设置超时时间,结果很悲剧的一次又一次的依然超时,后来才看到网上说SetOption无法如帮助文档中说的那样可以设置超时时间是MS已经确认的bug了,官方的解决方案是使用wininet系列函数或者将Openurl这部分代码放到一个线程里,如果线程超时就释放资源,关闭句柄,但这个也只能是防止内存泄露,对已经中断的下载依然于事无补。
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类,类的声明:
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; }
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; }
};
只需实现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);
return S_OK;
}
调用URLDownloadToFile下载即可
void CUrlDownloadToFileCallbackTestDlg::DownloadThread()
{
CBindCallback cbc;
cbc.m_pdlg = this;
CString szURL;
m_url.GetWindowText(szURL);
m_url.EnableWindow(FALSE);
(this->GetDlgItem(IDC_START))->EnableWindow(FALSE);
//在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。
CString szRand;
szRand.Format(_T("?skq=%d"),GetTickCount());
szUrl += szRand;
if(S_OK == URLDownloadToFile(NULL,szURL,szPath,0,&cbc))
MessageBox("finished");
}
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的设置,完成下载,不用考虑代理情况。
From:/article/9686361.html
函数定义 :
[cpp] view
plaincopyprint?
HRESULT URLDownloadToFile(
LPUNKNOWN pCaller,
LPCSTR szURL,
LPCSTR szFileName,
DWORD dwReserved,
LPBINDSTATUSCALLBACK lpfnCB
);
参数说明:
pCaller : 仅当调用者是一个ActiveX对象才使用,对于一个非ActiveX对象的应用程序这个参数应该为NULL
szURL : 为要下载的绝对URL 文件名,这个参数不能为空。
szFileName : 包含创建的目标文件名
dwReserved : 必须为零
lpfnCB : 一个指向 IBindStatusCallback 接口的指针, IE通过它向你通知下载的进度。
利用URLDownloadToFile()下载文件可以分下面几个步骤:
1. 提供一个要下载的URL文件名。
2. 构造一个目标创建文件的完整路径含文件名。
3. 创建一个IBindStatusCallback派生类,编写OnProgress()重载函数。
4. 派生一个类对象的实例。
5. 调用URLDownLoad 函数,由于这个函数调用是同步的,因此你最好在一个工作者线程中调用这个函数。
6. 在OnProgress函数中,提供任何你需要的进度指示信息和其它界面,函数返回值告诉IE是继续下载或者是放弃下载。
使用IbindStatusCallback
IBindStatusCallback 回调接口共有8个方法,但是你只需关心OnProgress(). 其它方法只需返回E_NOTIMPL。
OnProgress 实现的规范是:
[cpp] view
plaincopyprint?
HRESULT OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR szStatusText
);
ulProgress
到目前为止已经下载的字节数。
ulProgressMax
要下载的文件大小,0表示大小未知,需要注意的是,这个值仅在OnProgress调用期间变化,所以你不能把它保存到一个静态变量中,你应该在每次调用时检查这个值。
ulStatusCode
状态标志,这个值可以为下列一些值:
BINDSTATUS_BEGINDOWNLOADCOMPONENTS
BINDSTATUS_INSTALLINGCOMPONENTS
BINDSTATUS_ENDDOWNLOADCOMPONENTS
szStatusText
图形界面中使用的字符串,由IE 提供,这个变量可能为NULL,在使用前应该检查这个变量。
OnProgress()返回 S_OK 告诉IE要继续下载, E_ABORT则表示放弃下载。
以下是IBindStatusCallback的派生类写法 :
//头文件
[cpp] view
plaincopyprint?
//---------------------------------------------------------------------------
#ifndef CallbackH
#define CallbackH
#include <Urlmon.h>
#include "DownThread.h"
//---------------------------------------------------------------------------
class TCallback : public IBindStatusCallback
{
DWORD m_cRef;
//IBinding *m_pbinding;
private:
STDMETHODIMP QueryInterface(REFIID riid,void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo);
STDMETHODIMP GetPriority(LONG *nPriority);
STDMETHODIMP OnDataAvailable(DWORD grfBSCF,DWORD dwSize,
FORMATETC *formatetc,STGMEDIUM *stgmed);
STDMETHODIMP OnLowResource(DWORD reserved);
STDMETHODIMP OnObjectAvailable(REFIID iid,IUnknown *punk);
STDMETHODIMP OnStartBinding(DWORD dwReserved,IBinding *pib);
STDMETHODIMP OnStopBinding(HRESULT hresult,LPCWSTR szError);
STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR szStatusText);
public:
TDownload *D;
TCallback() {m_cRef = 1;/*m_pbinding = NULL*/};
//~TCallback() {if (m_pbinding) m_pbinding->Release();};
};
#endif
//CPP文件
[cpp] view
plaincopyprint?
//---------------------------------------------------------------------------
#include "Callback.h"
//---------------------------------------------------------------------------
STDMETHODIMP TCallback::QueryInterface(REFIID riid,void **ppv)
{
*ppv = NULL;
if (riid==IID_IUnknown || riid==IID_IBindStatusCallback) {
*ppv = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) TCallback::AddRef()
{
return m_cRef++;
}
STDMETHODIMP_(ULONG) TCallback::Release()
{
if(--m_cRef==0) {
delete this;
return 0;
}
return m_cRef;
}
STDMETHODIMP TCallback::GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::GetPriority(LONG *nPriority)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnDataAvailable(DWORD grfBSCF,DWORD dwSize,
FORMATETC *formatetc,STGMEDIUM *stgmed)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnLowResource(DWORD reserved)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnObjectAvailable(REFIID iid,IUnknown *punk)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnStartBinding(DWORD dwReserved,IBinding *pib)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnStopBinding(HRESULT hresult,LPCWSTR szError)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR szStatusText)
{
AnsiString Status;
switch (ulStatusCode)
{
case BINDSTATUS_FINDINGRESOURCE : Status = "Finding resource " + AnsiString(szStatusText); break;
case BINDSTATUS_CONNECTING : Status = "Connecting to " + AnsiString(szStatusText); break;
case BINDSTATUS_REDIRECTING : Status = "Redirecting..."; break;
case BINDSTATUS_BEGINDOWNLOADDATA : Status = "Start to download " + AnsiString(szStatusText); break;
case BINDSTATUS_DOWNLOADINGDATA : Status = "Downloading..."; break;
case BINDSTATUS_ENDDOWNLOADDATA : Status = "Complete downloading " + AnsiString(szStatusText); break;
case BINDSTATUS_BEGINDOWNLOADCOMPONENTS : Status = "Start to download components"; break;
case BINDSTATUS_INSTALLINGCOMPONENTS : Status = "Installing components..." ; break;
case BINDSTATUS_ENDDOWNLOADCOMPONENTS : Status = "Complete downloading components"; break;
case BINDSTATUS_USINGCACHEDCOPY : Status = "Copying form buffer..."; break;
case BINDSTATUS_SENDINGREQUEST : Status = "Sending request..."; break;
case BINDSTATUS_CLASSIDAVAILABLE : Status = "Class ID is available"; break;
case BINDSTATUS_MIMETYPEAVAILABLE : Status = "MIME type is available"; break;
case BINDSTATUS_CACHEFILENAMEAVAILABLE : Status = "Cache file name is available"; break;
case BINDSTATUS_BEGINSYNCOPERATION : Status = "Start sync operation"; break;
case BINDSTATUS_ENDSYNCOPERATION : Status = "Complete sync operation"; break;
case BINDSTATUS_BEGINUPLOADDATA : Status = "Start to upload data"; break;
case BINDSTATUS_UPLOADINGDATA : Status = "Uploading data"; break;
case BINDSTATUS_ENDUPLOADDATA : Status = "Complete Uploading data"; break;
case BINDSTATUS_PROTOCOLCLASSID : Status = "Protocol class ID is available"; break;
case BINDSTATUS_ENCODING : Status = "Encoding..."; break;
case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : Status = "Verified MIME is available"; break;
case BINDSTATUS_CLASSINSTALLLOCATION : Status = "Class install location"; break;
case BINDSTATUS_DECODING : Status = "Decoding..."; break;
case BINDSTATUS_LOADINGMIMEHANDLER : Status = "Loading MIME handler"; break;
case BINDSTATUS_CONTENTDISPOSITIONATTACH : Status = "Content disposition attach"; break;
case BINDSTATUS_FILTERREPORTMIMETYPE : Status = "Filter report MIME type"; break;
case BINDSTATUS_CLSIDCANINSTANTIATE : Status = "Clsid can instantiate"; break;
case BINDSTATUS_IUNKNOWNAVAILABLE : Status = "Unknown available"; break;
case BINDSTATUS_DIRECTBIND : Status = "Direct bind"; break;
case BINDSTATUS_RAWMIMETYPE : Status = "MIME type of the resource, before any code sniffing is done"; break;
case BINDSTATUS_PROXYDETECTING : Status = "Detecting proxy..."; break;
case BINDSTATUS_ACCEPTRANGES : Status = "Valid types of range requests for a resource"; break;
default : Status = "";
}
//在这里填入显示进度的代码。
//如果要实现中断下载,最好在一个线程中调用URLDownloadToFile函数,
//如下面代码中的 D 既是一个线程,设定DoCancel变量来决定是否取消下载。
//DoProgress是线程中显示进度的函数。
if (!Status.IsEmpty()) D->ShowMsg(Status);
D->DoProgress(ulProgress,ulProgressMax,ulStatusCode);
if (D->DoCancel) return E_ABORT;
else return S_OK;
}
使用范例:
[cpp] view
plaincopyprint?
#include <Urlmon.h> //还须加入urlmon.lib
...
TCallback Status; //回调类实例
URLDownloadToFile(NULL,"http://...","C://???",0,&Status);
最近使用 MFC的CInternetSession类下载文件时遇到一些问题, CInternetSession::OpenUrl 这个方法在使用个别代理的网络环境或者网速慢得要死的情况下,它会抛一个连接超时的异常,虽然catch 这个异常可以避免runtime error ,但下载也中止了。不过MSDN中提到SetOption可以设置超时时间,结果很悲剧的一次又一次的依然超时,后来才看到网上说SetOption无法如帮助文档中说的那样可以设置超时时间是MS已经确认的bug了,官方的解决方案是使用wininet系列函数或者将Openurl这部分代码放到一个线程里,如果线程超时就释放资源,关闭句柄,但这个也只能是防止内存泄露,对已经中断的下载依然于事无补。
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类,类的声明:
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; }
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; }
};
只需实现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);
return S_OK;
}
调用URLDownloadToFile下载即可
void CUrlDownloadToFileCallbackTestDlg::DownloadThread()
{
CBindCallback cbc;
cbc.m_pdlg = this;
CString szURL;
m_url.GetWindowText(szURL);
m_url.EnableWindow(FALSE);
(this->GetDlgItem(IDC_START))->EnableWindow(FALSE);
//在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。
CString szRand;
szRand.Format(_T("?skq=%d"),GetTickCount());
szUrl += szRand;
if(S_OK == URLDownloadToFile(NULL,szURL,szPath,0,&cbc))
MessageBox("finished");
}
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的设置,完成下载,不用考虑代理情况。
From:/article/9686361.html
函数定义 :
[cpp] view
plaincopyprint?
HRESULT URLDownloadToFile(
LPUNKNOWN pCaller,
LPCSTR szURL,
LPCSTR szFileName,
DWORD dwReserved,
LPBINDSTATUSCALLBACK lpfnCB
);
参数说明:
pCaller : 仅当调用者是一个ActiveX对象才使用,对于一个非ActiveX对象的应用程序这个参数应该为NULL
szURL : 为要下载的绝对URL 文件名,这个参数不能为空。
szFileName : 包含创建的目标文件名
dwReserved : 必须为零
lpfnCB : 一个指向 IBindStatusCallback 接口的指针, IE通过它向你通知下载的进度。
利用URLDownloadToFile()下载文件可以分下面几个步骤:
1. 提供一个要下载的URL文件名。
2. 构造一个目标创建文件的完整路径含文件名。
3. 创建一个IBindStatusCallback派生类,编写OnProgress()重载函数。
4. 派生一个类对象的实例。
5. 调用URLDownLoad 函数,由于这个函数调用是同步的,因此你最好在一个工作者线程中调用这个函数。
6. 在OnProgress函数中,提供任何你需要的进度指示信息和其它界面,函数返回值告诉IE是继续下载或者是放弃下载。
使用IbindStatusCallback
IBindStatusCallback 回调接口共有8个方法,但是你只需关心OnProgress(). 其它方法只需返回E_NOTIMPL。
OnProgress 实现的规范是:
[cpp] view
plaincopyprint?
HRESULT OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR szStatusText
);
ulProgress
到目前为止已经下载的字节数。
ulProgressMax
要下载的文件大小,0表示大小未知,需要注意的是,这个值仅在OnProgress调用期间变化,所以你不能把它保存到一个静态变量中,你应该在每次调用时检查这个值。
ulStatusCode
状态标志,这个值可以为下列一些值:
BINDSTATUS_BEGINDOWNLOADCOMPONENTS
BINDSTATUS_INSTALLINGCOMPONENTS
BINDSTATUS_ENDDOWNLOADCOMPONENTS
szStatusText
图形界面中使用的字符串,由IE 提供,这个变量可能为NULL,在使用前应该检查这个变量。
OnProgress()返回 S_OK 告诉IE要继续下载, E_ABORT则表示放弃下载。
以下是IBindStatusCallback的派生类写法 :
//头文件
[cpp] view
plaincopyprint?
//---------------------------------------------------------------------------
#ifndef CallbackH
#define CallbackH
#include <Urlmon.h>
#include "DownThread.h"
//---------------------------------------------------------------------------
class TCallback : public IBindStatusCallback
{
DWORD m_cRef;
//IBinding *m_pbinding;
private:
STDMETHODIMP QueryInterface(REFIID riid,void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo);
STDMETHODIMP GetPriority(LONG *nPriority);
STDMETHODIMP OnDataAvailable(DWORD grfBSCF,DWORD dwSize,
FORMATETC *formatetc,STGMEDIUM *stgmed);
STDMETHODIMP OnLowResource(DWORD reserved);
STDMETHODIMP OnObjectAvailable(REFIID iid,IUnknown *punk);
STDMETHODIMP OnStartBinding(DWORD dwReserved,IBinding *pib);
STDMETHODIMP OnStopBinding(HRESULT hresult,LPCWSTR szError);
STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR szStatusText);
public:
TDownload *D;
TCallback() {m_cRef = 1;/*m_pbinding = NULL*/};
//~TCallback() {if (m_pbinding) m_pbinding->Release();};
};
#endif
//CPP文件
[cpp] view
plaincopyprint?
//---------------------------------------------------------------------------
#include "Callback.h"
//---------------------------------------------------------------------------
STDMETHODIMP TCallback::QueryInterface(REFIID riid,void **ppv)
{
*ppv = NULL;
if (riid==IID_IUnknown || riid==IID_IBindStatusCallback) {
*ppv = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) TCallback::AddRef()
{
return m_cRef++;
}
STDMETHODIMP_(ULONG) TCallback::Release()
{
if(--m_cRef==0) {
delete this;
return 0;
}
return m_cRef;
}
STDMETHODIMP TCallback::GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::GetPriority(LONG *nPriority)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnDataAvailable(DWORD grfBSCF,DWORD dwSize,
FORMATETC *formatetc,STGMEDIUM *stgmed)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnLowResource(DWORD reserved)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnObjectAvailable(REFIID iid,IUnknown *punk)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnStartBinding(DWORD dwReserved,IBinding *pib)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnStopBinding(HRESULT hresult,LPCWSTR szError)
{
return E_NOTIMPL;
}
STDMETHODIMP TCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR szStatusText)
{
AnsiString Status;
switch (ulStatusCode)
{
case BINDSTATUS_FINDINGRESOURCE : Status = "Finding resource " + AnsiString(szStatusText); break;
case BINDSTATUS_CONNECTING : Status = "Connecting to " + AnsiString(szStatusText); break;
case BINDSTATUS_REDIRECTING : Status = "Redirecting..."; break;
case BINDSTATUS_BEGINDOWNLOADDATA : Status = "Start to download " + AnsiString(szStatusText); break;
case BINDSTATUS_DOWNLOADINGDATA : Status = "Downloading..."; break;
case BINDSTATUS_ENDDOWNLOADDATA : Status = "Complete downloading " + AnsiString(szStatusText); break;
case BINDSTATUS_BEGINDOWNLOADCOMPONENTS : Status = "Start to download components"; break;
case BINDSTATUS_INSTALLINGCOMPONENTS : Status = "Installing components..." ; break;
case BINDSTATUS_ENDDOWNLOADCOMPONENTS : Status = "Complete downloading components"; break;
case BINDSTATUS_USINGCACHEDCOPY : Status = "Copying form buffer..."; break;
case BINDSTATUS_SENDINGREQUEST : Status = "Sending request..."; break;
case BINDSTATUS_CLASSIDAVAILABLE : Status = "Class ID is available"; break;
case BINDSTATUS_MIMETYPEAVAILABLE : Status = "MIME type is available"; break;
case BINDSTATUS_CACHEFILENAMEAVAILABLE : Status = "Cache file name is available"; break;
case BINDSTATUS_BEGINSYNCOPERATION : Status = "Start sync operation"; break;
case BINDSTATUS_ENDSYNCOPERATION : Status = "Complete sync operation"; break;
case BINDSTATUS_BEGINUPLOADDATA : Status = "Start to upload data"; break;
case BINDSTATUS_UPLOADINGDATA : Status = "Uploading data"; break;
case BINDSTATUS_ENDUPLOADDATA : Status = "Complete Uploading data"; break;
case BINDSTATUS_PROTOCOLCLASSID : Status = "Protocol class ID is available"; break;
case BINDSTATUS_ENCODING : Status = "Encoding..."; break;
case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : Status = "Verified MIME is available"; break;
case BINDSTATUS_CLASSINSTALLLOCATION : Status = "Class install location"; break;
case BINDSTATUS_DECODING : Status = "Decoding..."; break;
case BINDSTATUS_LOADINGMIMEHANDLER : Status = "Loading MIME handler"; break;
case BINDSTATUS_CONTENTDISPOSITIONATTACH : Status = "Content disposition attach"; break;
case BINDSTATUS_FILTERREPORTMIMETYPE : Status = "Filter report MIME type"; break;
case BINDSTATUS_CLSIDCANINSTANTIATE : Status = "Clsid can instantiate"; break;
case BINDSTATUS_IUNKNOWNAVAILABLE : Status = "Unknown available"; break;
case BINDSTATUS_DIRECTBIND : Status = "Direct bind"; break;
case BINDSTATUS_RAWMIMETYPE : Status = "MIME type of the resource, before any code sniffing is done"; break;
case BINDSTATUS_PROXYDETECTING : Status = "Detecting proxy..."; break;
case BINDSTATUS_ACCEPTRANGES : Status = "Valid types of range requests for a resource"; break;
default : Status = "";
}
//在这里填入显示进度的代码。
//如果要实现中断下载,最好在一个线程中调用URLDownloadToFile函数,
//如下面代码中的 D 既是一个线程,设定DoCancel变量来决定是否取消下载。
//DoProgress是线程中显示进度的函数。
if (!Status.IsEmpty()) D->ShowMsg(Status);
D->DoProgress(ulProgress,ulProgressMax,ulStatusCode);
if (D->DoCancel) return E_ABORT;
else return S_OK;
}
使用范例:
[cpp] view
plaincopyprint?
#include <Urlmon.h> //还须加入urlmon.lib
...
TCallback Status; //回调类实例
URLDownloadToFile(NULL,"http://...","C://???",0,&Status);
相关文章推荐
- VC URLDownloadToFile 不能下载中文路径文件的解决办法
- URLDownloadToCacheFile,下载文件到缓存中
- 一个使用URLDownloadToFile实现文件下载的类
- 转:delphi用URLDownloadToFile下载文件,用进度条跟踪下载进度
- (转)关于URLDownloadToFile下载文件
- 一个使用URLDownloadToFile实现文件下载的类
- 用URLDownloadToFile下载文件
- Windows客户端开发--URLDownloadToFile下载文件进度条
- UrlDownloadtoFile文件下载,进度条,下载暂停,停止的简单实现
- Windows客户端开发--URLDownloadToFile下载文件进度条
- 使用URLDownloadToFile下载文件,有进度
- URLDownloadToFile 直接下载文件
- C++Builder利用URLDownloadToFile下载文件并显示进度
- nsis使用URLDownloadToFile下载文件
- vb urldownloadtofile 下载 及 容易发生的问题
- UrlDownloadFile, 线程下载文件, 带进度条
- Delphi编写下载程序:UrlDownloadToFile的进度提示
- vb URLDownloadToFile下载函数
- UrlDownloadFile, 线程下载文件, 带进度条
- UrlDownloadFile, 线程下载文件, 带进度条