4

インターネットからページをダウンロードして解析するプログラムを作りたいです。2番目の部分は簡単です、問題は最初です。

URLDownloadToFile()関数を使用したい。ただし、デフォルトでは、ダウンロードが完了するのを待ちません。MSDNによると、最後のパラメーターは一種のコールバック関数ですが、その使用方法に関する情報は見つかりません(呼び出されたときと実行する必要があること、関数の種類も)。誰かがその最後のパラメーターとは何か、そしてそれを(C ++で)使用してアプリを待機させる方法を説明できますか?

4

5 に答える 5

11

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
于 2011-03-13T20:47:34.203 に答える
9

おそらく関数はエラーのためにすぐに戻ります。

LPBINDSTATUSCALLBACK lpfnCBNULLに設定した場合、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によってダウンロードが完了したことが示された後でも、たとえばローカルネットワークのネットワークブリッジを介して接続されている場合、URLDownloadToFile()が誤ってS_OKとダウンロードをドロップする可能性があるため、progress==filesize値を比較する必要があります。インターフェイスとブリッジが何らかの理由でダウンしました。

また、ダウンロード後にディスク領域を解放するには、URLDownloadToFile()と組み合わせたDeleteUrlCacheEntry()関数に注意する必要があります。これは、ダウンロードされたすべてのコンテンツが、IEのキャッシュポリシーに従ってデフォルトでディスクにキャッシュされるためです。

于 2013-01-23T09:05:57.640 に答える
2

ファイルを同期的にダウンロードしたい場合は、以下のサンプルのような単純なものでうまくいくはずです。

HRESULT hRez = URLDownloadToFile( NULL, _T(<url>), _T(<file>), 0, NULL );
if( hRez == 0 ){
 // download ok
}
else{
 // download failed
}
于 2011-03-13T19:47:32.157 に答える
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;

ダウンロードが終了したことをプログラムの残りの部分に通知する方法をオブジェクトが認識できるように、オブジェクトのコンストラクターに何らかの情報を渡すことをお勧めします。プログラムがその通知を受信するまで、メッセージポンプで通常のアイドル状態のままにしておくことができます。

于 2011-03-13T20:12:01.447 に答える
0

これは役立つかもしれません。

InternetExplorerを使用してファイルをダウンロードする

于 2011-03-13T20:07:04.160 に答える