[C++] 纯文本查看 复制代码
#include <windows.h>
#include <commctrl.h> // 包含Common Controls库的头文件
#include <wininet.h> // 包含WinInet库的头文件
#include <tchar.h>
#include <shlobj.h> // 包含文件对话框相关的头文件
#include <shlwapi.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "shell32.lib") // 添加shell32.lib以支持SHGetFolderPath
// 定义按钮和进度条的ID
#define ID_BUTTON 1
#define ID_EDIT_URL 2
#define ID_PROGRESSBAR 3
#define ID_STATUS_LABEL_TOTAL 4
#define ID_STATUS_LABEL_SPEED 5
#define ID_STATUS_LABEL_REMAINING 6
// 定义 ThreadParams 结构体
struct ThreadParams {
HWND hWnd;
const TCHAR* url;
const TCHAR* filePath;
volatile bool* isDownloading;
LARGE_INTEGER startTime; // 下载开始时间
LARGE_INTEGER frequency; // 性能计数器频率
};
// 全局变量
HWND hwndProgressBar = NULL;
HWND hwndStatusLabelTotal = NULL;
HWND hwndStatusLabelSpeed = NULL;
HWND hwndStatusLabelRemaining = NULL;
HWND hwndEditUrl = NULL;
HANDLE hDownloadThread = NULL;
DWORD downloadThreadId = 0;
volatile bool isDownloading = false;
// 线程函数声明
DWORD WINAPI DownloadThread(LPVOID lpParam);
// 窗口过程函数声明
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const wchar_t* CLASS_NAME = L"Sample Window Class";
// 初始化Common Controls库
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_BAR_CLASSES; // 初始化进度条控件
InitCommonControlsEx(&icex);
// 注册窗口类
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = CLASS_NAME;
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建窗口
HWND hwnd = CreateWindowEx(
0, // 扩展样式
CLASS_NAME, // 窗口类名
L"Learn to Program Windows", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // 窗口位置
544, 375, // 窗口大小
NULL, // 父窗口
NULL, // 菜单
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwnd == NULL) {
MessageBox(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建编辑框
hwndEditUrl = CreateWindowEx(
WS_EX_CLIENTEDGE, // 扩展样式
L"EDIT", // 控件类名
L"", // 初始文本为空
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, // 样式
10, // x位置
10, // y位置
300, // 宽度
20, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_EDIT_URL, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndEditUrl == NULL) {
MessageBox(NULL, L"Edit Box Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建按钮
HWND hwndButton = CreateWindowEx(
0, // 扩展样式
L"BUTTON", // 控件类名
L"Start Download", // 按钮文本
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // 样式
320, // x位置
10, // y位置
100, // 宽度
30, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_BUTTON, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndButton == NULL) {
MessageBox(NULL, L"Button Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建进度条
hwndProgressBar = CreateWindowEx(
0, // 扩展样式
PROGRESS_CLASS, // 控件类名
NULL, // 进度条没有文本
WS_VISIBLE | WS_CHILD | PBS_SMOOTH, // 样式,添加 PBS_SMOOTH
10, // x位置
50, // y位置
520, // 宽度
20, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_PROGRESSBAR, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndProgressBar == NULL) {
MessageBox(NULL, L"Progress Bar Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建总进度标签
hwndStatusLabelTotal = CreateWindowEx(
0, // 扩展样式
L"STATIC", // 控件类名
L"", // 初始文本为空
WS_VISIBLE | WS_CHILD, // 样式
10, // x位置
80, // y位置
520, // 宽度
20, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_STATUS_LABEL_TOTAL, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndStatusLabelTotal == NULL) {
MessageBox(NULL, L"Status Label Total Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建速度标签
hwndStatusLabelSpeed = CreateWindowEx(
0, // 扩展样式
L"STATIC", // 控件类名
L"", // 初始文本为空
WS_VISIBLE | WS_CHILD, // 样式
10, // x位置
110, // y位置
520, // 宽度
20, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_STATUS_LABEL_SPEED, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndStatusLabelSpeed == NULL) {
MessageBox(NULL, L"Status Label Speed Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建剩余时间标签
hwndStatusLabelRemaining = CreateWindowEx(
0, // 扩展样式
L"STATIC", // 控件类名
L"", // 初始文本为空
WS_VISIBLE | WS_CHILD, // 样式
10, // x位置
140, // y位置
520, // 宽度
20, // 高度
hwnd, // 父窗口句柄
(HMENU)ID_STATUS_LABEL_REMAINING, // 控件ID
hInstance, // 实例句柄
NULL // 附加应用数据
);
if (hwndStatusLabelRemaining == NULL) {
MessageBox(NULL, L"Status Label Remaining Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 设置进度条的范围和初始位置
SendMessage(hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); // 设置范围为0到100
SendMessage(hwndProgressBar, PBM_SETPOS, 0, 0); // 设置当前位置为0
ShowWindow(hwnd, nCmdShow);
// 消息循环
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// 定义 StartDownload 函数
void StartDownload(HWND hWnd, const TCHAR* url, const TCHAR* filePath, volatile bool& isDownloading) {
ThreadParams* params = new ThreadParams{ hWnd, url, filePath, &isDownloading };
QueryPerformanceFrequency(¶ms->frequency); // 初始化性能计数器频率
LARGE_INTEGER startCounter;
QueryPerformanceCounter(&startCounter); // 记录开始时间
params->startTime.QuadPart = startCounter.QuadPart;
hDownloadThread = CreateThread(NULL, 0, DownloadThread, params, 0, &downloadThreadId);
if (hDownloadThread == NULL) {
MessageBox(NULL, L"CreateThread failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
isDownloading = false;
delete params; // 释放内存
}
}
// 线程函数定义
DWORD WINAPI DownloadThread(LPVOID lpParam) {
ThreadParams* params = static_cast<ThreadParams*>(lpParam);
HWND hWnd = params->hWnd;
const TCHAR* url = params->url;
const TCHAR* filePath = params->filePath;
volatile bool* isDownloading = params->isDownloading;
LARGE_INTEGER frequency = params->frequency;
HINTERNET hInternet = InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL) {
MessageBox(hWnd, L"InternetOpen failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
*isDownloading = false;
delete params;
return 1;
}
HINTERNET hUrl = InternetOpenUrl(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (hUrl == NULL) {
InternetCloseHandle(hInternet);
MessageBox(hWnd, L"InternetOpenUrl failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
*isDownloading = false;
delete params;
return 1;
}
DWORD fileSize = 0;
DWORD size = sizeof(fileSize);
// 获取文件大小
if (!HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &fileSize, &size, 0)) {
fileSize = 0; // 如果无法获取文件大小,则设为0
}
HANDLE hFile = CreateFile(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
InternetCloseHandle(hUrl);
InternetCloseHandle(hInternet);
MessageBox(hWnd, L"CreateFile failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
*isDownloading = false;
delete params;
return 1;
}
DWORD bytesRead = 0;
DWORD totalBytesRead = 0;
char buffer[4096];
LARGE_INTEGER previousTime;
LARGE_INTEGER currentTime;
double elapsedTime;
double downloadSpeed;
double remainingTime;
TCHAR statusTextTotal[256];
TCHAR statusTextSpeed[256];
TCHAR statusTextRemaining[256];
QueryPerformanceCounter(&previousTime); // 初始化上一次时间
while (InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
WriteFile(hFile, buffer, bytesRead, &bytesRead, NULL); // bytesRead 用于接收实际写入的字节数
totalBytesRead += bytesRead; // 累加实际写入的字节数
if (fileSize > 0) {
int progress = (int)((static_cast<double>(totalBytesRead) / fileSize) * 100); // 计算进度百分比
PostMessage(hWnd, WM_USER + 1, progress, 0); // 更新进度条位置
QueryPerformanceCounter(¤tTime);
elapsedTime = (currentTime.QuadPart - previousTime.QuadPart) / (double)frequency.QuadPart; // 计算经过的时间(秒)
if (elapsedTime > 0.001) { // 避免除零错误
downloadSpeed = bytesRead / elapsedTime; // 计算瞬时下载速度(字节/秒)
remainingTime = (fileSize - totalBytesRead) / downloadSpeed; // 计算剩余时间(秒)
// 将字节转换为MB
double totalBytesReadMB = totalBytesRead / 1024.0 / 1024.0;
double fileSizeMB = fileSize / 1024.0 / 1024.0;
double downloadSpeedMBps = downloadSpeed / 1024.0 / 1024.0;
_stprintf_s(statusTextTotal, 256, _T("%.2fMB/%.2fMB"), totalBytesReadMB, fileSizeMB);
_stprintf_s(statusTextSpeed, 256, _T("速度 %.2fMB/s"), downloadSpeedMBps);
_stprintf_s(statusTextRemaining, 256, _T("剩余 %.2f秒"), remainingTime);
PostMessage(hWnd, WM_USER + 2, 0, (LPARAM)statusTextTotal); // 发送总进度文本
PostMessage(hWnd, WM_USER + 3, 0, (LPARAM)statusTextSpeed); // 发送速度文本
PostMessage(hWnd, WM_USER + 4, 0, (LPARAM)statusTextRemaining); // 发送剩余时间文本
previousTime = currentTime; // 更新上一次时间
}
}
else {
// 如果无法获取文件大小,直接显示已读取的字节数
double totalBytesReadMB = totalBytesRead / 1024.0 / 1024.0;
_stprintf_s(statusTextTotal, 256, _T("%.2fMB"), totalBytesReadMB);
PostMessage(hWnd, WM_USER + 2, 0, (LPARAM)statusTextTotal); // 发送总进度文本
}
}
CloseHandle(hFile);
InternetCloseHandle(hUrl);
InternetCloseHandle(hInternet);
*isDownloading = false;
PostMessage(hWnd, WM_USER + 1, 100, 0);
delete params;
return 0;
}
// 示例按钮点击事件处理函数
void OnDownloadButtonClick(HWND hWnd) {
TCHAR url[1024];
GetWindowText(hwndEditUrl, url, 1024); // 从编辑框中获取URL
if (_tcslen(url) == 0) {
MessageBox(hWnd, L"Please enter a valid URL.", L"Information", MB_OK | MB_ICONINFORMATION);
return;
}
if (isDownloading) {
MessageBox(hWnd, L"Download is already in progress.", L"Information", MB_OK | MB_ICONINFORMATION);
return;
}
// 使用文件对话框选择保存位置
OPENFILENAME ofn;
TCHAR szFile[MAX_PATH] = { 0 };
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = L"All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFile;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
// 解析URL以获取文件名
LPCTSTR pszFileName = PathFindFileName(url);
if (pszFileName && _tcslen(pszFileName) > 0) {
lstrcpy(szFile, pszFileName);
}
else {
lstrcpy(szFile, _T("downloaded_file")); // 默认文件名
}
// 提取文件扩展名
LPCTSTR pszFileExt = PathFindExtension(pszFileName);
if (pszFileExt && _tcslen(pszFileExt) > 0) {
// 如果URL中包含扩展名,则直接使用
lstrcat(szFile, pszFileExt);
}
else {
// 如果URL中不包含扩展名,则尝试从HTTP响应头中获取
HINTERNET hInternet = InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL) {
HINTERNET hUrl = InternetOpenUrl(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (hUrl != NULL) {
TCHAR contentType[256];
DWORD contentTypeSize = sizeof(contentType);
if (HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_DISPOSITION | HTTP_QUERY_FLAG_REQUEST_HEADERS, contentType, &contentTypeSize, 0)) {
// 解析Content-Disposition头以获取文件名
TCHAR* pszFilename = PathFindFileName(contentType);
if (pszFilename && _tcslen(pszFilename) > 0) {
LPCTSTR pszFilenameExt = PathFindExtension(pszFilename);
if (pszFilenameExt && _tcslen(pszFilenameExt) > 0) {
lstrcpy(szFile, pszFilename);
}
}
}
InternetCloseHandle(hUrl);
}
InternetCloseHandle(hInternet);
}
}
// 获取桌面路径作为默认路径
TCHAR desktopPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, desktopPath);
PathCombine(szFile, desktopPath, szFile);
if (GetSaveFileName(&ofn)) {
isDownloading = true;
StartDownload(hWnd, url, szFile, isDownloading);
}
}
// 窗口过程函数定义
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND:
if (LOWORD(wParam) == ID_BUTTON) {
OnDownloadButtonClick(hwnd);
}
break;
case WM_USER + 1:
SendMessage(hwndProgressBar, PBM_SETPOS, wParam, 0); // 更新进度条位置
break;
case WM_USER + 2:
SetWindowText(hwndStatusLabelTotal, (LPCTSTR)lParam); // 更新总进度标签文本
break;
case WM_USER + 3:
SetWindowText(hwndStatusLabelSpeed, (LPCTSTR)lParam); // 更新速度标签文本
break;
case WM_USER + 4:
SetWindowText(hwndStatusLabelRemaining, (LPCTSTR)lParam); // 更新剩余时间标签文本
break;
case WM_DESTROY:
if (hDownloadThread != NULL) {
WaitForSingleObject(hDownloadThread, INFINITE);
CloseHandle(hDownloadThread);
}
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}