You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于WinHttp的C++代理下载器:进程无关适配需求问询

实现进程无关且支持代理的WinHttp下载函数

针对你需要让下载函数在用户上下文或服务中运行、适配有/无代理场景的需求,以下是优化后的代码及关键改进说明:

优化后的完整代码

#include <windows.h>
#include <winhttp.h>
#include <string>
#include <vector>
#include <cstdio>

// 链接WinHttp库
#pragma comment(lib, "winhttp.lib")

std::string Downloader() {
    std::string response;
    HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
    BOOL bResults = FALSE;
    DWORD dwError = 0;

    // 初始化WinHttp会话,配置为自动代理检测,适配用户/服务上下文
    hSession = WinHttpOpen(
        L"WinHttp Downloader/1.0",
        WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS,
        0
    );

    if (!hSession) {
        dwError = GetLastError();
        printf("WinHttpOpen失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 配置自动代理(处理WPAD和自动代理脚本)
    WINHTTP_AUTOPROXY_OPTIONS autoProxyOpts = {0};
    WINHTTP_PROXY_INFO proxyInfo = {0};
    autoProxyOpts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
    autoProxyOpts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;

    bResults = WinHttpSetOption(
        hSession,
        WINHTTP_OPTION_AUTOPROXY_CONFIGURATION,
        &autoProxyOpts,
        sizeof(autoProxyOpts)
    );

    if (!bResults) {
        dwError = GetLastError();
        printf("设置自动代理失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 连接目标服务器:分离地址和端口,避免冲突
    if (hSession) {
        hConnect = WinHttpConnect(
            hSession,
            L"10.0.2.4",
            8000, // 明确指定端口,替代原代码的冲突写法
            0
        );
    }

    if (!hConnect) {
        dwError = GetLastError();
        printf("WinHttpConnect失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 创建GET请求
    hRequest = WinHttpOpenRequest(
        hConnect,
        L"GET",
        L"hello.txt",
        NULL,
        WINHTTP_NO_REFERER,
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        0
    );

    if (!hRequest) {
        dwError = GetLastError();
        printf("WinHttpOpenRequest失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 发送请求
    bResults = WinHttpSendRequest(
        hRequest,
        WINHTTP_NO_ADDITIONAL_HEADERS,
        0,
        WINHTTP_NO_REQUEST_DATA,
        0,
        0,
        0
    );

    if (!bResults) {
        dwError = GetLastError();
        printf("WinHttpSendRequest失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 接收响应
    bResults = WinHttpReceiveResponse(hRequest, NULL);
    if (!bResults) {
        dwError = GetLastError();
        printf("WinHttpReceiveResponse失败,错误码: %d\n", dwError);
        goto Cleanup;
    }

    // 循环读取响应数据(处理分块或异步返回的情况)
    DWORD bytesAvailable = 0;
    while (WinHttpQueryDataAvailable(hRequest, &bytesAvailable) && bytesAvailable > 0) {
        std::vector<char> buffer(bytesAvailable);
        DWORD bytesRead = 0;

        if (WinHttpReadData(hRequest, buffer.data(), bytesAvailable, &bytesRead) && bytesRead > 0) {
            response.append(buffer.data(), bytesRead);
        } else {
            dwError = GetLastError();
            printf("WinHttpReadData失败,错误码: %d\n", dwError);
            break;
        }
    }

    if (!response.empty()) {
        printf("响应内容:\n%s\n", response.c_str());
    }

Cleanup:
    // 确保所有句柄都被正确关闭
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

    return response;
}

关键改进说明

  • 代理适配优化:

    • 改用WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY替代原代码的默认代理,自动检测WPAD(Web Proxy Auto-Discovery)或DHCP/DNS中的代理配置,兼容用户上下文(使用IE代理)和服务上下文(无用户代理配置)的场景。
    • 通过WinHttpSetOption配置自动代理参数,确保代理检测逻辑生效。
  • 错误处理完善:

    • 每个WinHttp API调用后都添加错误检查,记录错误码并提前跳转到资源清理流程,避免空指针访问和未处理的失败场景。
    • 统一使用goto Cleanup进行资源释放,确保所有打开的句柄都能被正确关闭,即使中间步骤出错。
  • 修复端口冲突问题:

    • 原代码中WinHttpConnect的地址参数包含端口:8000,同时又传入INTERNET_DEFAULT_HTTP_PORT(即80),导致实际连接到80端口而非目标的8000端口。优化后分离地址和端口参数,明确指定目标端口。
  • 可靠的数据读取:

    • 原代码仅读取一次可用数据,可能遗漏分块传输的后续内容。优化后通过循环调用WinHttpQueryDataAvailable和WinHttpReadData,确保读取完整的响应内容。
    • 使用std::vector作为临时缓冲区,结合std::string动态拼接数据,避免缓冲区溢出风险。
  • 进程无关的会话配置:

    • WinHttpOpen的第一个参数指定自定义用户代理,避免依赖系统默认值;同时不使用依赖用户上下文的代理选项,确保服务进程中也能正常运行。

内容的提问来源于stack exchange,提问作者hungli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 19:25:27