检测 IE 代理设置并与 TIdHTTP 一起使用

Posted

技术标签:

【中文标题】检测 IE 代理设置并与 TIdHTTP 一起使用【英文标题】:Detecting IE Proxy settings and using with TIdHTTP 【发布时间】:2012-02-16 04:33:39 【问题描述】:

如何设置 TIdHTTP 以使用 IE 代理配置? 应该兼容XP/Vista/Win7,可靠。

【问题讨论】:

【参考方案1】:

Indy 不使用 Internet Explorer 的代理设置,因此您必须自己获取它,例如使用 InternetQueryOption 函数。

更新:

这是使用WinHTTP 的代码,它应该尝试从 IE 接收设置。如果它们可用并且设置了 自动检测代理设置自动配置脚本 URL 选项,则将执行代理检测。当 IE 设置不可用时,也会执行自动检测。

免责声明:

以下代码仅针对最简单的情况进行了测试,即 IE 设置可用且代理设置未配置为自动检测(没有环境)。另请注意,本单元中的一些函数、结构和常量是附加的。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
     Private declarations 
  public
     Public declarations 
  end;

type
  HINTERNET = Pointer;
  $EXTERNALSYM HINTERNET
  INTERNET_PORT = Word;
  $EXTERNALSYM INTERNET_PORT

  PWinHTTPProxyInfo = ^TWinHTTPProxyInfo;
  WINHTTP_PROXY_INFO = record
    dwAccessType: DWORD;
    lpszProxy: LPWSTR;
    lpszProxyBypass: LPWSTR;
  end;
  $EXTERNALSYM WINHTTP_PROXY_INFO
  TWinHTTPProxyInfo = WINHTTP_PROXY_INFO;
  LPWINHTTP_PROXY_INFO = PWinHTTPProxyInfo;
  $EXTERNALSYM LPWINHTTP_PROXY_INFO

  PWinHTTPAutoProxyOptions = ^TWinHTTPAutoProxyOptions;
  WINHTTP_AUTOPROXY_OPTIONS = record
    dwFlags: DWORD;
    dwAutoDetectFlags: DWORD;
    lpszAutoConfigUrl: LPCWSTR;
    lpvReserved: Pointer;
    dwReserved: DWORD;
    fAutoLogonIfChallenged: BOOL;
  end;
  $EXTERNALSYM WINHTTP_AUTOPROXY_OPTIONS
  TWinHTTPAutoProxyOptions = WINHTTP_AUTOPROXY_OPTIONS;
  LPWINHTTP_AUTOPROXY_OPTIONS = PWinHTTPAutoProxyOptions;
  $EXTERNALSYM LPWINHTTP_AUTOPROXY_OPTIONS

  PWinHTTPCurrentUserIEProxyConfig = ^TWinHTTPCurrentUserIEProxyConfig;
  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG = record
    fAutoDetect: BOOL;
    lpszAutoConfigUrl: LPWSTR;
    lpszProxy: LPWSTR;
    lpszProxyBypass: LPWSTR;
  end;
  $EXTERNALSYM WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
  TWinHTTPCurrentUserIEProxyConfig = WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
  LPWINHTTP_CURRENT_USER_IE_PROXY_CONFIG = PWinHTTPCurrentUserIEProxyConfig;
  $EXTERNALSYM LPWINHTTP_CURRENT_USER_IE_PROXY_CONFIG

  function WinHttpOpen(pwszUserAgent: LPCWSTR; dwAccessType: DWORD;
    pwszProxyName, pwszProxyBypass: LPCWSTR; dwFlags: DWORD): HINTERNET; stdcall;
    external 'winhttp.dll' name 'WinHttpOpen';
  $EXTERNALSYM WinHttpOpen
  function WinHttpConnect(hSession: HINTERNET; pswzServerName: LPCWSTR;
    nServerPort: INTERNET_PORT; dwReserved: DWORD): HINTERNET; stdcall;
    external 'winhttp.dll' name 'WinHttpConnect';
  $EXTERNALSYM WinHttpConnect
  function WinHttpOpenRequest(hConnect: HINTERNET; pwszVerb: LPCWSTR;
    pwszObjectName: LPCWSTR; pwszVersion: LPCWSTR; pwszReferer: LPCWSTR;
    ppwszAcceptTypes: PLPWSTR; dwFlags: DWORD): HINTERNET; stdcall;
    external 'winhttp.dll' name 'WinHttpOpenRequest';
  $EXTERNALSYM WinHttpOpenRequest
  function WinHttpQueryOption(hInet: HINTERNET; dwOption: DWORD;
    lpBuffer: Pointer; var lpdwBufferLength: DWORD): BOOL; stdcall;
    external 'winhttp.dll' name 'WinHttpQueryOption';
  $EXTERNALSYM WinHttpQueryOption
  function WinHttpGetProxyForUrl(hSession: HINTERNET; lpcwszUrl: LPCWSTR;
    pAutoProxyOptions: LPWINHTTP_AUTOPROXY_OPTIONS;
    var pProxyInfo: WINHTTP_PROXY_INFO): BOOL; stdcall;
    external 'winhttp.dll' name 'WinHttpGetProxyForUrl';
  $EXTERNALSYM WinHttpGetProxyForUrl
  function WinHttpGetIEProxyConfigForCurrentUser(
    var pProxyInfo: WINHTTP_CURRENT_USER_IE_PROXY_CONFIG): BOOL; stdcall;
    external 'winhttp.dll' name 'WinHttpGetIEProxyConfigForCurrentUser';
  $EXTERNALSYM WinHttpGetIEProxyConfigForCurrentUser
  function WinHttpCloseHandle(hInternet: HINTERNET): BOOL; stdcall;
    external 'winhttp.dll' name 'WinHttpCloseHandle';
  $EXTERNALSYM WinHttpCloseHandle

const
  WINHTTP_NO_REFERER = nil;
  $EXTERNALSYM WINHTTP_NO_REFERER
  WINHTTP_NO_PROXY_NAME = nil;
  $EXTERNALSYM WINHTTP_NO_PROXY_NAME
  WINHTTP_NO_PROXY_BYPASS = nil;
  $EXTERNALSYM WINHTTP_NO_PROXY_BYPASS
  WINHTTP_DEFAULT_ACCEPT_TYPES = nil;
  $EXTERNALSYM WINHTTP_DEFAULT_ACCEPT_TYPES
  WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0;
  $EXTERNALSYM WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
  WINHTTP_ACCESS_TYPE_NO_PROXY = 1;
  $EXTERNALSYM WINHTTP_ACCESS_TYPE_NO_PROXY
  WINHTTP_OPTION_PROXY = 38;
  $EXTERNALSYM WINHTTP_OPTION_PROXY
  WINHTTP_OPTION_PROXY_USERNAME = $1002;
  $EXTERNALSYM WINHTTP_OPTION_PROXY_USERNAME
  WINHTTP_OPTION_PROXY_PASSWORD = $1003;
  $EXTERNALSYM WINHTTP_OPTION_PROXY_PASSWORD  
  WINHTTP_AUTOPROXY_AUTO_DETECT = $00000001;
  $EXTERNALSYM WINHTTP_AUTOPROXY_AUTO_DETECT
  WINHTTP_AUTOPROXY_CONFIG_URL = $00000002;
  $EXTERNALSYM WINHTTP_AUTOPROXY_CONFIG_URL
  WINHTTP_AUTO_DETECT_TYPE_DHCP = $00000001;
  $EXTERNALSYM WINHTTP_AUTO_DETECT_TYPE_DHCP
  WINHTTP_AUTO_DETECT_TYPE_DNS_A = $00000002;
  $EXTERNALSYM WINHTTP_AUTO_DETECT_TYPE_DNS_A
  WINHTTP_FLAG_BYPASS_PROXY_CACHE = $00000100;
  $EXTERNALSYM WINHTTP_FLAG_BYPASS_PROXY_CACHE
  WINHTTP_FLAG_REFRESH = WINHTTP_FLAG_BYPASS_PROXY_CACHE;
  $EXTERNALSYM WINHTTP_FLAG_REFRESH

var
  Form1: TForm1;

implementation

$R *.dfm

type
  TProxyInfo = record
    ProxyURL: WideString;
    ProxyBypass: WideString;
    ProxyAutoDetected: Boolean;    
  end;

function GetProxyInfo(const AURL: WideString; var AProxyInfo: TProxyInfo): DWORD;
var
  Session: HINTERNET;
  AutoDetectProxy: Boolean;
  WinHttpProxyInfo: TWinHTTPProxyInfo;
  AutoProxyOptions: TWinHTTPAutoProxyOptions;
  IEProxyConfig: TWinHTTPCurrentUserIEProxyConfig;
begin
  // initialize the result
  Result := 0;
  // initialize auto-detection to off
  AutoDetectProxy := False;
  // initialize the result structure
  AProxyInfo.ProxyURL := '';
  AProxyInfo.ProxyBypass := '';
  AProxyInfo.ProxyAutoDetected := False;
  // initialize the auto-proxy options
  FillChar(AutoProxyOptions, SizeOf(AutoProxyOptions), 0);

  // check if the Internet Explorer's proxy configuration is
  // available and if so, check its settings for auto-detect
  // proxy settings and auto-config script URL options
  if WinHttpGetIEProxyConfigForCurrentUser(IEProxyConfig) then
  begin
    // if the Internet Explorer is configured to auto-detect
    // proxy settings then we try to detect them later on
    if IEProxyConfig.fAutoDetect then
    begin
      AutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
      AutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or
        WINHTTP_AUTO_DETECT_TYPE_DNS_A;
      AutoDetectProxy := True;
    end;
    // if the Internet Explorer is configured to use the proxy
    // auto-config script then we try to use it
    if IEProxyConfig.lpszAutoConfigURL <> '' then
    begin
      AutoProxyOptions.dwFlags := AutoProxyOptions.dwFlags or
        WINHTTP_AUTOPROXY_CONFIG_URL;
      AutoProxyOptions.lpszAutoConfigUrl := IEProxyConfig.lpszAutoConfigUrl;
      AutoDetectProxy := True;
    end;
    // if IE don't have auto-detect or auto-config set, we are
    // done here and we can fill the AProxyInfo with the IE settings
    if not AutoDetectProxy then
    begin
      AProxyInfo.ProxyURL := IEProxyConfig.lpszProxy;
      AProxyInfo.ProxyBypass := IEProxyConfig.lpszProxyBypass;
      AProxyInfo.ProxyAutoDetected := False;
    end;   
  end
  else
  begin
    // if the Internet Explorer's proxy configuration is not
    // available, then try to auto-detect it
    AutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
    AutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or
      WINHTTP_AUTO_DETECT_TYPE_DNS_A;
    AutoDetectProxy := True;
  end;

  // if the IE proxy settings are not available or IE has
  // configured auto-config script or auto-detect proxy settings
  if AutoDetectProxy then
  begin
    // create a temporary WinHttp session to allow the WinHTTP
    // auto-detect proxy settings if possible
    Session := WinHttpOpen(nil, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
      WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    // if the WinHttp session has been created then try to 
    // get the proxy data for the specified URL else we assign 
    // the last error code to the function result
    if Assigned(Session) then
    try
      // get the proxy data for the specified URL with the
      // auto-proxy options specified, if succeed then we can
      // fill the AProxyInfo with the retrieved settings else
      // we assign the last error code to the function result
      if WinHttpGetProxyForUrl(Session, LPCWSTR(AURL),
        @AutoProxyOptions, WinHttpProxyInfo) then
      begin
        AProxyInfo.ProxyURL := WinHttpProxyInfo.lpszProxy;
        AProxyInfo.ProxyBypass := WinHttpProxyInfo.lpszProxyBypass;
        AProxyInfo.ProxyAutoDetected := True;
      end
      else
        Result := GetLastError;
    finally
      WinHttpCloseHandle(Session);
    end
    else
      Result := GetLastError;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Result: DWORD;
  ProxyInfo: TProxyInfo;
begin
  Result := GetProxyInfo('http://www.example.com', ProxyInfo);
  case Result of
    0: 
      ShowMessage(
        'Proxy URL: ' + ProxyInfo.ProxyURL + sLineBreak +
        'Proxy bypass: ' + ProxyInfo.ProxyBypass + sLineBreak +
        'Proxy autodetected: ' + BoolToStr(ProxyInfo.ProxyAutoDetected, True));
    12166: ShowMessage('Error in proxy auto-config script code');
    12167: ShowMessage('Unable to download proxy auto-config script');
    12180: ShowMessage('WPAD detection failed');
  else
    ShowMessage('Last error: ' + IntToStr(Result));
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True;
end;

end.

对于替代的 Delphi 代码,您可以检查例如this tip.

下面是一个如何使用获得的代理设置设置TIdHTTP 的示例(实际上您只需解析获得的代理URL 并将其传递给ProxyServerProxyPort 属性):

uses
  IdGlobal;

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  Result: DWORD;
  ProxyInfo: TProxyInfo;
begin
  Result := GetProxyInfo('http://www.example.com', ProxyInfo);

  if Result <> 0 then
    IdHTTP1.ProxyParams.Clear
  else
  begin
    S := ProxyInfo.ProxyURL;
    IdHTTP1.ProxyParams.ProxyServer := Fetch(S, ':');
    IdHTTP1.ProxyParams.ProxyPort := StrToInt(S);
  end;
end;

【讨论】:

“旧”delphi 代码是什么意思?还会支持Web Proxy Autodiscovery Protocol吗? @zigi70,Yahia 添加的链接注释很可能意味着代码示例已有 7 年历史。对于是否支持 WPAD 的问题,是的,但您可能会遇到诸如 this 之类的问题。 InternetQueryOption 函数用于定位 Internet Explorer 最近检测到的代理配置脚本,因此如果您启用了 WPAD 并且您已连接,那么您可能会获得之前自动检测到的最新设置。 @TLama,我应该使用 InternetQueryOption 还是 WinHttpGetIEProxyConfigForCurrentUser? @zigi70,现在我正在查看它(以前没有代理自动检测的经验),但似乎更多人更喜欢使用 WinHttpGetProxyForUrl 的解决方案,您可以在其中指定目标 URL ,因此代理服务器可能会为您提供正确的自动设置和WinHttpGetIEProxyConfigForCurrentUser 功能,如果之前失败(如this answer 推荐)。 @TLamaת 如果代理有用户名+密码会发生什么。如何检索此信息。看来我迷路了……【参考方案2】:

您也可以通过 Windows 注册表获取它:

var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_CURRENT_USER;
  Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Internet Settings',false);
  Caption :=Reg.ReadString('ProxyServer');
  Reg.Free;
end;

【讨论】:

以上是关于检测 IE 代理设置并与 TIdHTTP 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

win7怎样设置代理网络

Websockets 代理并与 IIS Web 服务器同时使用端口 443

javascript 检测用户代理IE

win7 系统里怎样设置 http 代理

解决 Microsoft Store 无法打开的问题

ie怎么设置socket代理