我想制作从互联网下载页面并对其进行一些解析的程序。第二部分很容易,问题是第一部分。
我想使用URLDownloadToFile()函数。但默认情况下,它不会等待完成下载。 MSDN说最后一个参数是一种回调函数,但是我找不到任何关于如何使用它的信息(当它被调用时它必须做什么,甚至它是什么类型的函数)。有人可以解释我最后一个参数是什么以及如何使用它(在C ++中)让我的应用程序等待?
答案 0 :(得分:9)
由于错误,可能会立即返回函数。
如果将 LPBINDSTATUSCALLBACK lpfnCB 设置为 NULL ,则URLDownloadToFile()绝对是同步函数。
它是如此“同步”,即使网络连接失败并阻止你的线程,它的下载完成之前它将永远不会结束。使用TerminateThread()函数正在进行的URLDownloadToFile()杀死线程将导致资源泄漏,并且系统dll的子调用未完成,经过几次URLDownloadToFile()将拒绝在当前进程的上下文中工作。
在没有回调函数的情况下可靠地使用URLDownloadToFile()的唯一方法是将单独的进程分叉到它并在下载停顿时终止该进程,这是资源消耗。
URLDownloadToFile()下载行为与IE完全相同,用户配置文件中的所有IE代理和网络设置在此功能运行的上下文中也适用于此功能。
即使使用回调函数,URLDownloadToFile()也不会立即返回。我考虑在单独的线程中启动URLDownloadToFile()以安全地控制和中止网络下载。
https://github.com/choptastic/OldCode-Public/blob/master/URLDownloadToFile/URLDownloadToFile.cpp
有回调函数的简单示例为了安全下载,您应该至少使用以下内容升级代码:
private:
int progress, filesize;
int AbortDownload;
public:
STDMETHOD(OnStartBinding)(
{
AbortDownload=0;
progress=0;
filesize=0;
return E_NOTIMPL; }
STDMETHOD(GetProgress)()
{ return progress; }
STDMETHOD(GetFileSize)()
{ return filesize; }
STDMETHOD(AbortDownl)()
{
AbortDownload=1;
return E_NOTIMPL; }
HRESULT DownloadStatus::OnProgress ( ULONG ulProgress, ULONG ulProgressMax,ULONG ulStatusCode, LPCWSTR wszStatusText )
{
progress=ulProgress;
filesize=ulProgressMax;
if (AbortDownload) return E_ABORT;
return S_OK;
}
因此您可以随时中止下载并检查下载进度。
即使已经通过URLDownloadToFile()函数返回的S_OK指示下载已完成,您也必须比较progress == filesize值,因为URLDownloadToFile()可能会错误地下载S_OK,例如,如果通过网桥进行连接由于某种原因,本地网络接口和桥接器已经崩溃。
此外,您必须注意与URLDownloadToFile()配对的DeleteUrlCacheEntry()函数以在下载后释放磁盘空间,因为默认情况下根据IE缓存策略将所有已下载的内容缓存在磁盘上。
答案 1 :(得分:7)
您必须创建一个实现IBindStatusCallback接口的类。您可以为大多数方法返回E_NOTIMPL。使用OnProgress()显示进度。这是一个完成这项工作的示例程序:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#pragma comment(lib, "urlmon.lib")
using namespace std;
class DownloadProgress : public IBindStatusCallback {
public:
HRESULT __stdcall QueryInterface(const IID &,void **) {
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef(void) {
return 1;
}
ULONG STDMETHODCALLTYPE Release(void) {
return 1;
}
HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, IBinding *pib) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetPriority(LONG *pnPriority) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, LPCWSTR szError) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *grfBINDF, BINDINFO *pbindinfo) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, IUnknown *punk) {
return E_NOTIMPL;
}
virtual HRESULT __stdcall OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
wcout << ulProgress << L" of " << ulProgressMax;
if (szStatusText) wcout << " " << szStatusText;
wcout << endl;
return S_OK;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
DownloadProgress progress;
HRESULT hr = URLDownloadToFile(0,
L"http://sstatic.net/stackoverflow/img/sprites.png?v=3",
L"c:/temp/test.png", 0,
static_cast<IBindStatusCallback*>(&progress));
return 0;
}
输出:
0 of 0 sstatic.net
0 of 0 64.34.119.12
0 of 0
0 of 0 image/x-png
3550 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
3550 of 16542 C:\Users\hpassant\AppData\Local\Microsoft\Windows\Temporary Inter
et Files\Content.IE5\NRPH4KHK\sprites[1].png
7330 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
8590 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
12370 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
13630 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
16542 of 16542 http://sstatic.net/stackoverflow/img/sprites.png?v=3
答案 2 :(得分:2)
如果您想同步下载文件,那么下面的示例就像下面的示例一样简单:
HRESULT hRez = URLDownloadToFile( NULL, _T(<url>), _T(<file>), 0, NULL );
if( hRez == 0 ){
// download ok
}
else{
// download failed
}
答案 3 :(得分:2)
文档说最后一个参数是指向“调用者的IBindStatusCallback接口”的指针。这意味着您作为调用者需要提供指向实现该接口的指针。您可以从这样的实现开始:
class CBindStatusCallback: public IBindStatusCallback
{
public:
STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR szStatusText)
{
// write your implementation here
}
// Override GetBindInfo and the other IBindStatusCallback methods
// by simply returning E_NOTIMPL, like this:
STDMETHODIMP GetBindInfo(DWORD* /*grfBINDF*/, BINDINFO* /*pbindinfo*/)
{
return E_NOTIMPL;
}
// Provide the usual implementations for these IUnknown methods.
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
};
创建一个实例,获取其IBindStatusCallback接口指针,并将其传递给API函数。像这样:
CBindStatusCallback* obj = new CBindStatusCallback;
IBindStatusCallback* callback = NULL;
HResult hr = obj->QueryInterface(IID_IBindStatusCallback, &callback);
obj = NULL;
hr = URLDownloadToFile(..., callback);
callback->Release();
callback = NULL;
您可能希望将某种信息传递给对象的构造函数,以便它知道如何通知程序的其余部分下载已终止。在程序收到该通知之前,您可以让它在消息泵中处于通常的空闲状态。
答案 4 :(得分:0)