Files
win-dictate/src/downloader.h
T

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);
}