目录
背景
客户端应用程序在用户设置了系统代理后如何保证正常运行,方法一般有二:1)客户端支持用户手动配置代理;2)支持自动获取系统代理(类新版Chrome)。为在windows系统下实现方法2中的系统代理信息获取,特查了下windows官方文档,现将关键内容及测试代码整理如下。
官方文档地址
在 WinHTTP 中设置 WinINet 代理配置 - Win32 apps | Microsoft Learn
官方文档内容
在会话上设置代理配置
应用程序在用户帐户上运行
在创建会话之前,应用程序会调用 WinHttpGetIEProxyConfigForCurrentUser 来获取 IE 代理设置。 应用程序必须以用户帐户身份运行才能获取这些设置。 pProxyConfig 参数是指向WINHTTP_CURRENT_USER_IE_PROXY_CONFIG结构的指针,该结构包含代理名称 (lpszProxy) 和代理绕过 (lpszProxyBypass) 服务器。 然后使用 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG 结构的代理名称和代理绕过值来初始化 WinHTTP 会话。 通过使用从 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG 结构的lpszProxy 和 lpszProxyBypass 成员获取的 pwszProxyName 和 pwszProxyBypass 参数调用 WinHttpOpen 来初始化会话。
应用程序作为服务运行
应用程序必须确保在调用 WinHttpGetIEProxyConfigForCurrentUser 之前将单个用户的注册表设置加载到注册表中。 如果未将这些设置加载到注册表中, WinHttpGetIEProxyConfigForCurrentUser 将无法获取代理设置。 可以通过调用 LoadUserProfile 函数将单个用户的注册表设置加载到注册表中。 如果无法加载用户的注册表设置,应用程序可以使用 dwAcessType 参数中指定的WINHTTP_ACCESS_TYPE_DEFAULT_PROXY调用 WinHttpOpen。 在调用 WinHttpOpen 时指定默认代理会告知 WinHTTP API 使用 WinHTTP proxycfg.exe 实用工具检索代理配置集。 加载单个用户的注册表设置后,应用程序将按照 应用程序在用户帐户上运行 下概述的步骤来设置代理名称和代理绕过服务器。
在单个请求上设置代理配置
在创建会话之前,应用程序调用 WinHttpGetIEProxyConfigForCurrentUser 以确定 WinINet 和 IE 是否配置为使用 WPAD。 WinHttpGetIEProxyConfigForCurrentUser 返回包含 fAutoDetect 成员的 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG 结构。 此成员的值为 TRUE 表示使用了 WPAD,并且 lpszAutoConfigUrl 成员包含 WPAD URL。
使用自动代理配置
如果使用 WPAD,应用程序会调用 WinHttpGetProxyForUrl 来检索请求的代理。 lpwszUrl 参数包含请求要发送到的 URL,pAutoProxyOptions 参数包含指向包含 autoproxy 选项WINHTTP_AUTOPROXY_OPTIONS) 结构的指针 (。 应用程序使用调用 WinHttpGetIEProxyConfigForCurrentUser 时从 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG 结构返回的设置初始化 WINHTTP_AUTOPROXY_OPTIONS 结构。 WINHTTP_AUTOPROXY_CONFIG_URL 标志在 WINHTTP_AUTOPROXY_OPTIONS 结构的 dwFlags 成员中指定,lpszAutoconfigUrl 成员包含WINHTTP_CURRENT_USER_IE_PROXY_CONFIG结构的代理自动配置 URL。 WinHttpGetProxyForUrl 函数返回WINHTTP_PROXY_INFO结构的 lpszProxy 和 lpszProxyBypass 成员中的代理名称和代理绕过列表。
从 WinHttpGetProxyForUrl 获取请求的代理后,应用程序使用 WinHttpOpenRequest 创建请求。 然后调用 WinHttpSetOption ,通过在 hInternet 参数中指定请求句柄来设置请求的代理。 调用 WinHttpSetOption 中的 dwOption 参数应设置为 WINHTTP_OPTION_PROXYlpBuffer 参数是指向包含要用于请求的代理和代理旁路的WINHTTP_PROXY_INFO结构的指针。
不使用自动代理配置
如果对 WinHttpGetIEProxyConfigForCurrentUser 的 调用指示不使用 autoproxy,则应用程序只需使用 WinHttpOpenRequest 创建请求。 代理配置对整个会话都是相同的,并且不需要按请求进行更改。
示例代码
应用程序在用户帐户上运行:
#include "winhttp.h"
#pragma comment(lib, "Winhttp.lib")
void testGetIEProxy()
{
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG pProxyConfig;
pProxyConfig.fAutoDetect = TRUE;
WinHttpGetIEProxyConfigForCurrentUser(&pProxyConfig);
_tprintf(_T("fAutoDetect:%d\n"), pProxyConfig.fAutoDetect);
_tprintf(_T("lpszAutoConfigUrl:%ls\n"), pProxyConfig.lpszAutoConfigUrl);
_tprintf(_T("lpszProxy:%ls\n"), pProxyConfig.lpszProxy);
_tprintf(_T("lpszProxyBypass:%ls\n"), pProxyConfig.lpszProxyBypass);
}
应用程序作为服务运行,可参考上文说明,前置调用 LoadUserProfile 函数。
其他方法
基于 wininet 提供的方法也可实现同样的需求,可对照官网文档进行使用。
#include "wininet.h"
#pragma comment(lib, "Wininet.lib")
BOOL QueryProxy()
{
BOOL ret = FALSE;
INTERNET_PER_CONN_OPTION_LIST List;
INTERNET_PER_CONN_OPTION Option[5];
unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
Option[2].dwOption = INTERNET_PER_CONN_FLAGS;
Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
List.pszConnection = NULL;
List.dwOptionCount = 5;
List.dwOptionError = 0;
List.pOptions = Option;
if (!InternetQueryOption(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize))
_tprintf(_T("InternetQueryOption failed! (%d)\n"), GetLastError());
if (Option[0].Value.pszValue != NULL) _tprintf(_T("%s\n"), Option[0].Value.pszValue);
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) _tprintf(_T("PROXY_TYPE_AUTO_PROXY_URL\n"));
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) _tprintf(_T("PROXY_TYPE_AUTO_DETECT\n"));
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY)
{
_tprintf(_T("Proxy ENABLED!\n"));
ret = TRUE;
}
else
_tprintf(_T("Proxy DISABLED!\n"));
_tprintf(_T("Current proxy:%s\n"), Option[4].Value.pszValue);
INTERNET_VERSION_INFO Version;
nSize = sizeof(INTERNET_VERSION_INFO);
InternetQueryOption(NULL, INTERNET_OPTION_VERSION, &Version, &nSize);
if (Option[0].Value.pszValue != NULL) GlobalFree(Option[0].Value.pszValue);
if (Option[3].Value.pszValue != NULL) GlobalFree(Option[3].Value.pszValue);
if (Option[4].Value.pszValue != NULL) GlobalFree(Option[4].Value.pszValue);
return ret;
}