等待URLDownloadToFile()结束

时间:2011-03-13 18:28:18

标签: c++ urlmon

我想制作从互联网下载页面并对其进行一些解析的程序。第二部分很容易,问题是第一部分。

我想使用URLDownloadToFile()函数。但默认情况下,它不会等待完成下载。 MSDN说最后一个参数是一种回调函数,但是我找不到任何关于如何使用它的信息(当它被调用时它必须做什么,甚至它是什么类型的函数)。有人可以解释我最后一个参数是什么以及如何使用它(在C ++中)让我的应用程序等待?

5 个答案:

答案 0 :(得分:9)

由于错误,可能会立即返回函数。

如果将 LPBINDSTATUSCALLBACK lpfnCB 设置为 NULL ,则URLDownloadToFile()绝对是同步函数。

它是如此“同步”,即使网络连接失败并阻止你的线程,它的下载完成之前它将永远不会结束。使用TerminateThread()函数正在进行的URLDownloadToFile()杀死线程将导致资源泄漏,并且系统dl​​l的子调用未完成,经过几次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)