Skip to content

Instantly share code, notes, and snippets.

@t-mat
Created September 3, 2024 12:11
Show Gist options
  • Save t-mat/46266c1d272c60b6ef016c31f507f245 to your computer and use it in GitHub Desktop.
Save t-mat/46266c1d272c60b6ef016c31f507f245 to your computer and use it in GitHub Desktop.
[Win32] URLDownloadToFile
// Should I use URLDownloadToFile?
// Answered by Mike Caron : https://stackoverflow.com/a/5185008
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Urlmon.h>
#include <string>
#include <functional>
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Urlmon.lib")
namespace Win32HttpDownloader
{
// status : See [BINDSTATUS]
// progress : [0.0f, 1.0f]
//
// Return: true=continue, false=cancel
//
// [BINDSTATUS]:
// https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms775133(v=vs.85)
using CallbackFunc = std::function<bool(ULONG status, double progress)>;
namespace Internal
{
class CallbackHandler : public IBindStatusCallback
{
public:
using CallbackFunc0 = std::function<bool(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode)>;
CallbackHandler(const CallbackFunc0 &callbackFunc0) : callbackFunc0{ callbackFunc0 } {};
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
{
if (IsEqualIID(IID_IBindStatusCallback, riid) || IsEqualIID(IID_IUnknown, riid)) {
*ppvObject = reinterpret_cast<void *>(this);
return S_OK;
}
return E_NOINTERFACE;
};
ULONG STDMETHODCALLTYPE AddRef() { return 2UL; };
ULONG STDMETHODCALLTYPE Release() { return 1UL; };
// IBindStatusCallback
HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD, IBinding *) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE GetPriority(LONG *) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE OnLowResource(DWORD) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR)
{
return callbackFunc0(ulProgress, ulProgressMax, ulStatusCode) ? S_OK : E_ABORT;
};
HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT, LPCWSTR) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *, BINDINFO *) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *) { return E_NOTIMPL; };
HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID, IUnknown *) { return E_NOTIMPL; };
private:
const CallbackFunc0 callbackFunc0;
};
inline HRESULT UrlDownloadToFile_(const std::wstring &outputPath,
const std::wstring &url,
IBindStatusCallback *pBindStatusCallback)
{
return URLDownloadToFileW(NULL, url.c_str(), outputPath.c_str(), 0, pBindStatusCallback);
}
inline HRESULT
UrlDownloadToFile0(const std::wstring &outputPath, const std::wstring &url, const CallbackFunc &callbackFunc)
{
const auto callbackFunc0 = [&](ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode) -> bool {
const double p =
static_cast<double>(ulProgress) / static_cast<double>(ulProgressMax == 0 ? 1 : ulProgressMax);
return callbackFunc(ulStatusCode, p);
};
CallbackHandler callbackHandler{ callbackFunc0 };
IBindStatusCallback *pBindStatusCallback = NULL;
callbackHandler.QueryInterface(IID_IBindStatusCallback, reinterpret_cast<void **>(&pBindStatusCallback));
return UrlDownloadToFile_(url, outputPath, pBindStatusCallback);
}
} // namespace Internal
//
inline HRESULT
UrlDownloadToFile(std::wstring_view outputPath, std::wstring_view url, const CallbackFunc &callbackFunc)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile0(wOutputPath, wUrl, callbackFunc);
}
inline HRESULT
UrlDownloadToFile(std::string_view outputPath, std::wstring_view url, const CallbackFunc &callbackFunc)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile0(wOutputPath, wUrl, callbackFunc);
}
inline HRESULT
UrlDownloadToFile(std::wstring_view outputPath, std::string_view url, const CallbackFunc &callbackFunc)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile0(wOutputPath, wUrl, callbackFunc);
}
inline HRESULT
UrlDownloadToFile(std::string_view outputPath, std::string_view url, const CallbackFunc &callbackFunc)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile0(wOutputPath, wUrl, callbackFunc);
}
inline HRESULT UrlDownloadToFile(std::wstring_view outputPath, std::wstring_view url)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile_(wOutputPath, wUrl, nullptr);
}
inline HRESULT UrlDownloadToFile(std::string_view outputPath, std::wstring_view url)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile_(wOutputPath, wUrl, nullptr);
}
inline HRESULT UrlDownloadToFile(std::wstring_view outputPath, std::string_view url)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile_(wOutputPath, wUrl, nullptr);
}
inline HRESULT UrlDownloadToFile(std::string_view outputPath, std::string_view url)
{
const auto wOutputPath = std::wstring(outputPath.begin(), outputPath.end());
const auto wUrl = std::wstring(url.begin(), url.end());
return Internal::UrlDownloadToFile_(wOutputPath, wUrl, nullptr);
}
} // namespace Win32HttpDownloader
int main(int argc, char *argv[])
{
printf("URLDownloadToFile test function.\n");
wchar_t url[] = L"http://google.com";
wchar_t currentDir[MAX_PATH];
GetCurrentDirectoryW(MAX_PATH, currentDir);
wchar_t path[MAX_PATH];
wsprintfW(path, L"%s\\index.html", currentDir);
wprintf(L"url=%s\n", url);
wprintf(L"path=%s\n", path);
const auto callbackFunc = [&](ULONG status, double progress) -> bool {
printf("status = %3d, progress = %6.2f%%\n", (int)status, progress * 100.0);
return true;
};
const HRESULT res = Win32HttpDownloader::UrlDownloadToFile(path, url, callbackFunc);
switch (res) {
case S_OK:
wprintf(L"Ok\n");
break;
case E_OUTOFMEMORY:
wprintf(L"Buffer length invalid, or insufficient memory\n");
break;
case INET_E_DOWNLOAD_FAILURE:
wprintf(L"URL is invalid\n");
break;
default:
wprintf(L"Other error: 0x%08x\n", (int)res);
break;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment