93 lines
4.0 KiB
C++
93 lines
4.0 KiB
C++
#pragma once
|
|
#include <windows.h>
|
|
#include <winhttp.h>
|
|
#include <atomic>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <cstdio>
|
|
#include <algorithm>
|
|
#pragma comment(lib, "winhttp.lib")
|
|
|
|
#define WM_APP_DLPROGRESS (WM_USER + 7)
|
|
|
|
struct Downloader {
|
|
std::atomic<bool> active{false};
|
|
std::atomic<bool> cancel{false};
|
|
int itemIndex = -1;
|
|
std::thread th;
|
|
|
|
void start(HWND notify, int index, std::wstring url, std::wstring dest);
|
|
void requestCancel() { cancel = true; }
|
|
void join() { if (th.joinable()) th.join(); }
|
|
};
|
|
|
|
static void DlThread(HWND notify, int index, std::wstring url, std::wstring dest, Downloader* dl) {
|
|
std::wstring tmp = dest + L".part";
|
|
int result = -1;
|
|
HINTERNET hSes = nullptr, hCon = nullptr, hReq = nullptr;
|
|
FILE* f = nullptr;
|
|
do {
|
|
URL_COMPONENTS uc{}; uc.dwStructSize = sizeof(uc);
|
|
wchar_t host[256] = {0}, path[2048] = {0};
|
|
uc.lpszHostName = host; uc.dwHostNameLength = _countof(host);
|
|
uc.lpszUrlPath = path; uc.dwUrlPathLength = _countof(path);
|
|
if (!WinHttpCrackUrl(url.c_str(), 0, 0, &uc)) break;
|
|
hSes = WinHttpOpen(L"win-dictation/1.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
|
|
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
if (!hSes) break;
|
|
hCon = WinHttpConnect(hSes, host, uc.nPort, 0);
|
|
if (!hCon) break;
|
|
hReq = WinHttpOpenRequest(hCon, L"GET", path, nullptr, WINHTTP_NO_REFERER,
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
(uc.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0);
|
|
if (!hReq) break;
|
|
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
|
WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) break;
|
|
if (!WinHttpReceiveResponse(hReq, nullptr)) break;
|
|
DWORD status = 0, sz = sizeof(status);
|
|
WinHttpQueryHeaders(hReq, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
|
WINHTTP_HEADER_NAME_BY_INDEX, &status, &sz, WINHTTP_NO_HEADER_INDEX);
|
|
if (status != 200) break;
|
|
ULONGLONG total = 0;
|
|
{
|
|
wchar_t cl[40]; DWORD cls = sizeof(cl);
|
|
if (WinHttpQueryHeaders(hReq, WINHTTP_QUERY_CONTENT_LENGTH,
|
|
WINHTTP_HEADER_NAME_BY_INDEX, cl, &cls, WINHTTP_NO_HEADER_INDEX))
|
|
total = (ULONGLONG)_wtoi64(cl);
|
|
}
|
|
if (_wfopen_s(&f, tmp.c_str(), L"wb") != 0 || !f) break;
|
|
std::vector<char> buf(64 * 1024);
|
|
ULONGLONG got = 0; int lastPct = -1; bool ioOk = true;
|
|
for (;;) {
|
|
if (dl->cancel.load()) { result = -2; ioOk = false; break; }
|
|
DWORD avail = 0;
|
|
if (!WinHttpQueryDataAvailable(hReq, &avail)) { ioOk = false; break; }
|
|
if (avail == 0) break;
|
|
DWORD toRead = std::min<DWORD>(avail, (DWORD)buf.size()), rd = 0;
|
|
if (!WinHttpReadData(hReq, buf.data(), toRead, &rd) || rd == 0) { ioOk = false; break; }
|
|
if (fwrite(buf.data(), 1, rd, f) != rd) { ioOk = false; break; }
|
|
got += rd;
|
|
int pct = total ? (int)(got * 100 / total) : 0;
|
|
if (pct != lastPct) { lastPct = pct; PostMessage(notify, WM_APP_DLPROGRESS, index, pct); }
|
|
}
|
|
fclose(f); f = nullptr;
|
|
if (ioOk && (total == 0 || got == total))
|
|
if (MoveFileExW(tmp.c_str(), dest.c_str(), MOVEFILE_REPLACE_EXISTING))
|
|
result = 101;
|
|
} while (false);
|
|
if (f) fclose(f);
|
|
if (result != 101) DeleteFileW(tmp.c_str());
|
|
if (hReq) WinHttpCloseHandle(hReq);
|
|
if (hCon) WinHttpCloseHandle(hCon);
|
|
if (hSes) WinHttpCloseHandle(hSes);
|
|
PostMessage(notify, WM_APP_DLPROGRESS, index, result);
|
|
}
|
|
|
|
inline void Downloader::start(HWND notify, int index, std::wstring url, std::wstring dest) {
|
|
if (active.exchange(true)) return;
|
|
cancel = false; itemIndex = index;
|
|
if (th.joinable()) th.join();
|
|
th = std::thread(DlThread, notify, index, std::move(url), std::move(dest), this);
|
|
}
|