Detecting IE Proxy settings and using with TIdHTTP
Asked Answered
U

2

16

How do I set TIdHTTP to use IE proxy configuration?
It should be compatible with XP/Vista/Win7 and reliable.

Urial answered 22/1, 2012 at 13:2 Comment(0)
S
26

Indy doesn't use Internet Explorer's proxy settings and so you have to get it by yourself, for instance by using InternetQueryOption function.

Update:

Here is the code using WinHTTP which should try to receive the settings from IE. If they are available and auto-detect proxy settings or auto-config script URL options are set, then the proxy detection will be performed. Auto-detection will also be performed when the IE settings are not available.

Disclaimer:

The following code has been tested only for the easiest case, when the IE settings are available and the proxy settings are not configured to be detected automatically (don't have the environment). Also please note that some of the functions, structures and constant are additional in this unit.

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.

For an alternative Delphi code you can check e.g. this tip.

Here's an example of how to setup a TIdHTTP with obtained proxy settings (actually you just parse the obtained proxy URL and pass it to the ProxyServer and ProxyPort properties):

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;
Seeder answered 22/1, 2012 at 13:25 Comment(14)
What do you mean by "old" delphi code? Will it also support Web Proxy Autodiscovery Protocol?Urial
@zigi70, the note with the link added by Yahia means most probably that the code example is 7 years old. To the question if the WPAD will be supported, yes it will, but you might face to issues like this for instance. The InternetQueryOption function is used to locate the proxy configuration script most recently detected by Internet Explorer, so if you had the WPAD enabled and you have been connected, then you'll probably get the most recent settings automatically detected before.Seeder
@TLama, should I use InternetQueryOption or WinHttpGetIEProxyConfigForCurrentUser?Urial
@zigi70, now I'm looking on it (no previous experience with proxy auto-detection), but it seems that more people prefer the solution using the WinHttpGetProxyForUrl where you can specify the target URL, so the proxy server may give you the right auto settings and WinHttpGetIEProxyConfigForCurrentUser function if the previous fails (like this answer recommends).Seeder
@TLamaת What happens if the proxy has username+password. How to retrieve this information. It seems I'm lost...Urial
@zigi70, this you may get either by the InternetQueryOption with the INTERNET_OPTION_PROXY_USERNAME and then INTERNET_OPTION_PROXY_PASSWORD option flag. Or if you will follow the WinHttp way, then try WinHttpQueryOption with WINHTTP_OPTION_PROXY_USERNAME and then WINHTTP_OPTION_PROXY_PASSWORD option flag. Note, that you don't get the credentials when you are not logged in to the proxy.Seeder
@zigi70, I'll try in the evening, but I have no chance to test it.Seeder
@zigi70, try the code from the update, but please note that I can't test it because I don't have sufficient environment for it and don't have time to learn how to set it up. If you have some, please give here a feedback how it works. And about the credentials, it's more complicated than I thought so I'll try to search for some example. I'll also include the sources from which I've been inspired later on.Seeder
@TLama, Thanks. I didn't test your code yet. but I have a question: Is it possible that GetProxyInfo will get different proxy results for different URLs?Urial
@zigi70, as they stated here - Proxy auto-configuration (PAC): Specify the URL for a PAC file with a JavaScript function that determines the appropriate proxy for each URL., so yes each URL may have its own proxy configuration.Seeder
@zigi70, you're welcome. Here is the source from what I've get inspired. Still I'm wondering if the auto-detection and auto-config script getting works properly with this code. You would have to have configured proxy server to Web Proxy Auto-Discovery (WPAD) and test the following 1) set the auto-detect in IE, check auto-detection; 2) set the proxy auto-config script path (PAC file) in IE, check auto-detection; 3) set both settings, check auto-detection. It would be fine to test it :)Seeder
@TLama, I will test it as soon as I can. I still don't seem to fully understand why we need a URL to get Proxy settings. I will read it and try to understand :)Urial
Your code works pretty well. A couple of tweaks that you might want to add or for others that find this. The format for the proxyurl that is returned is one or more of ([<scheme>=][<scheme>"://"]<server>[":"<port>]) that can be separated by semicolons or whitespace (learn.microsoft.com/en-us/windows/win32/api/winhttp/…). In Windows 8.1 and later, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY is deprecated and people are supposed to use WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY.Haldane
As an example, if I install Fiddler, this is the ProxyURL that is returned http=127.0.0.1:8888;https=127.0.0.1:8888Haldane
H
9

You can get it also through Windows Registry as :

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;
Hematology answered 22/1, 2012 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.