#pragma once #include #include #include #include #include #include #include #include #pragma comment(lib, "winhttp.lib") #define WM_APP_DLPROGRESS (WM_USER + 7) struct Downloader { std::atomic active{false}; std::atomic 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 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(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); }