Created
September 3, 2024 12:11
-
-
Save t-mat/46266c1d272c60b6ef016c31f507f245 to your computer and use it in GitHub Desktop.
[Win32] URLDownloadToFile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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