总有人间一两风,填我十万八千梦

C语言URLDownloadToFile获取文件下载进度

Windows C/C++ Zero、J 1477℃ 0评论

URLDownloadToFile是urlmon.dll提供的网络操作API,可以用于从指定的URL下载文件到本地,在C++中通过类很容易实现下载进度的获取,今天需要在C语言中使用到这个函数,这里记录一下使用方法。该函数的定义如下所示。

HRESULT URLDownloadToFile(
             LPUNKNOWN            pCaller,
             LPCTSTR              szURL,
             LPCTSTR              szFileName,
  _Reserved_ DWORD                dwReserved,
             LPBINDSTATUSCALLBACK lpfnCB
);

参数pCaller跟ActiveX和COM有关,如有兴趣参考这里,否则就设置为NULL

参数szURL为下载地址,地址的协议必须要是IE浏览器所支持的

参数szFileName为文件最终的保存路径,例如C:/a.txt

参数dwReserved保留,始终为0

参数lpfnCB是一个IBindStatusCallback指针。

在urlmon.h头文件的定义中,C语言风格的接口定义如下:

typedef struct IBindStatusCallbackVtbl
    {
        BEGIN_INTERFACE
        
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject);
        
        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            __RPC__in IBindStatusCallback * This);
        
        ULONG ( STDMETHODCALLTYPE *Release )( 
            __RPC__in IBindStatusCallback * This);
        
        HRESULT ( STDMETHODCALLTYPE *OnStartBinding )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ DWORD dwReserved,
            /* [in] */ __RPC__in_opt IBinding *pib);
        
        HRESULT ( STDMETHODCALLTYPE *GetPriority )( 
            __RPC__in IBindStatusCallback * This,
            /* [out] */ __RPC__out LONG *pnPriority);
        
        HRESULT ( STDMETHODCALLTYPE *OnLowResource )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ DWORD reserved);
        
        HRESULT ( STDMETHODCALLTYPE *OnProgress )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulStatusCode,
            /* [unique][in] */ __RPC__in_opt LPCWSTR szStatusText);
        
        HRESULT ( STDMETHODCALLTYPE *OnStopBinding )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ HRESULT hresult,
            /* [unique][in] */ __RPC__in_opt LPCWSTR szError);
        
        /* [local] */ HRESULT ( STDMETHODCALLTYPE *GetBindInfo )( 
            IBindStatusCallback * This,
            /* [out] */ DWORD *grfBINDF,
            /* [unique][out][in] */ BINDINFO *pbindinfo);
        
        /* [local] */ HRESULT ( STDMETHODCALLTYPE *OnDataAvailable )( 
            IBindStatusCallback * This,
            /* [in] */ DWORD grfBSCF,
            /* [in] */ DWORD dwSize,
            /* [in] */ FORMATETC *pformatetc,
            /* [in] */ STGMEDIUM *pstgmed);
        
        HRESULT ( STDMETHODCALLTYPE *OnObjectAvailable )( 
            __RPC__in IBindStatusCallback * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [iid_is][in] */ __RPC__in_opt IUnknown *punk);
        
        END_INTERFACE
    } IBindStatusCallbackVtbl;

    interface IBindStatusCallback
    {
        CONST_VTBL struct IBindStatusCallbackVtbl *lpVtbl;
    };

这里的interface在C语言中的定义为struct, IBindStatusCallback回调实际上就是一个结构体,结构体只有一个字段IBindStatusCallbackVtbl *指针,你需要做的就是对这个结构体内的IBindStatusCallbackVtbl 指针进行实例化。IBindStatusCallbackVtbl 结构中包含了一系列的函数指针,也就是用于URLDownloadToFile下载时的回调函数。若要实现下载进度的获取,只需要在OnProgress回调中处理即可。

控制台示例代码

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <urlmon.h>
#include <stdio.h>
#pragma comment(lib,"urlmon")

HRESULT (STDMETHODCALLTYPE QueryInterface)(
	IBindStatusCallback* This,
	/* [in] */ REFIID riid,
	/* [iid_is][out] */ void** ppvObject)
{
	return E_NOTIMPL;
}

ULONG (STDMETHODCALLTYPE AddRef)(
	IBindStatusCallback* This)
{
	return E_NOTIMPL;
}

ULONG (STDMETHODCALLTYPE Release)(
	IBindStatusCallback* This)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE OnStartBinding)(
	IBindStatusCallback* This,
	/* [in] */ DWORD dwReserved,
	/* [in] */ IBinding* pib)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE GetPriority)(
	IBindStatusCallback* This,
	/* [out] */ LONG* pnPriority)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE OnLowResource)(
	IBindStatusCallback* This,
	/* [in] */ DWORD reserved)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE OnProgress)(
	IBindStatusCallback* This,
	/* [in] */ ULONG ulProgress,
	/* [in] */ ULONG ulProgressMax,
	/* [in] */ ULONG ulStatusCode,
	/* [in] */ LPCWSTR szStatusText)
{
	if (ulProgress == 0) {
		return S_OK;
	}
	printf("%d / %d bytes , %.2f%%\n",
			ulProgress, ulProgressMax,100*(double)ulProgress/(double)ulProgressMax);

	return S_OK;
}

HRESULT (STDMETHODCALLTYPE OnStopBinding)(
	IBindStatusCallback* This,
	/* [in] */ HRESULT hresult,
	/* [unique][in] */ LPCWSTR szError)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE GetBindInfo)(
	IBindStatusCallback* This,
	/* [out] */ DWORD* grfBINDF,
	/* [unique][out][in] */ BINDINFO* pbindinfo)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE OnDataAvailable)(
	IBindStatusCallback* This,
	/* [in] */ DWORD grfBSCF,
	/* [in] */ DWORD dwSize,
	/* [in] */ FORMATETC* pformatetc,
	/* [in] */ STGMEDIUM* pstgmed)
{
	return E_NOTIMPL;
}

HRESULT (STDMETHODCALLTYPE OnObjectAvailable)(
	IBindStatusCallback* This,
	/* [in] */ REFIID riid,
	/* [iid_is][in] */ IUnknown* punk)
{
	return E_NOTIMPL;
}


int main(int argc,char * argv[]) {
	char path[MAX_PATH];
	char downloadURL[] ="https://down.qq.com/qqweb/PCQQ/PCQQ_EXE/PCQQ2020.exe";
	IBindStatusCallback callBack;
	IBindStatusCallbackVtbl vtbl;

	vtbl.QueryInterface = QueryInterface;
	vtbl.AddRef = AddRef;
	vtbl.Release = Release;
	vtbl.OnStartBinding = OnStartBinding;
	vtbl.GetPriority = GetPriority;
	vtbl.OnLowResource = OnLowResource;
	vtbl.OnProgress = OnProgress;//进度回调操作
	vtbl.OnStopBinding = OnStopBinding;
	vtbl.GetBindInfo = GetBindInfo;
	vtbl.OnDataAvailable = OnDataAvailable;
	vtbl.OnObjectAvailable = OnObjectAvailable;

	callBack.lpVtbl = &vtbl;
	GetTempPath(sizeof(path),path);
	strcat(path, "tmpqq.exe");
	printf("保存位置: %s\n", path);
	URLDownloadToFile(NULL,downloadURL, path,0,&callBack);

	return 0;
}

转载请注明:悠然品鉴 » C语言URLDownloadToFile获取文件下载进度

喜欢 (2)or分享 (0)
发表我的评论
取消评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址