SYSK 366: Tool to Automatically Set Internet Explorer Proxy
As a consultant, I frequently work at my customer sites, and at my home office. Needless to say, remembering the proxy configurations, and changing them every time I plug in is a chore I’d rather delegate to software running in the background and automatically detecting network connectivity changes, and setting the IE proxy based on pre-configured settings… This blog post is a set of code snapshots from the tool I wrote to do just that…
Since IE settings are stored in CurrentUser registry hive, the path of least resistance was to create a UI-less Windows user process (not a service) and run it at start up by putting it in the Startup folder.
My configuration file (app.config) looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="IEProxySettings" type="AutoProxy.ProxySettingsSection, AutoProxy" />
</configSections>
<IEProxySettings>
<proxyConfig>
<proxy dnsSuffix="[YOUR_DNS_HERE]" defaultGateway="[YOUR_DEFAULT_IP_HERE]" address="[YOUR_IE_PROXY]" port="[YOUR_PORT]" bypassLocal="[true/false]" />
</proxyConfig>
</IEProxySettings>
</configuration>
To get the dnsSuffix and defaultGateway, run the command prompt (Start -> Run -> cmd) and execute the ipconfig command.
You can have as many <proxy> settings as needed.
If the dnsSuffix/defaultGateway is matched for one of the networks with status = OperationalStatus.Up, then the IE proxy settings are changed accordingly. If there is no match, then the ‘Use a proxy server for your LAN’ setting in IE browser is unchecked.
To read the settings, I use the following ConfigSettings.cs file:
using System;
using System.Configuration;
namespace AutoProxy
{
public class ProxySettingsSection : ConfigurationSection
{
public static ProxyElement[] Proxies
{
get
{
ProxyElement[] result = null;
ProxySettingsSection data = System.Configuration.ConfigurationManager.GetSection("IEProxySettings") as ProxySettingsSection;
if (data == null)
throw new ApplicationException("Configuration file is missing 'IEProxySettings' section", null);
result = new ProxyElement[data.ProxyConfig.Count];
data.ProxyConfig.CopyTo(result, 0);
return result;
}
}
[ConfigurationProperty("proxyConfig", Options = ConfigurationPropertyOptions.IsRequired, IsDefaultCollection = true, IsKey = false)]
public ProxyConfigCollection ProxyConfig
{
get
{
return ((ProxyConfigCollection)base["proxyConfig"]);
}
}
}
[ConfigurationCollection(typeof(ProxyElement), AddItemName = "proxy", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public sealed class ProxyConfigCollection : ConfigurationElementCollection
{
public ProxyConfigCollection()
: base(StringComparer.OrdinalIgnoreCase)
{
}
protected override Object GetElementKey(ConfigurationElement element)
{
return ((ProxyElement)element).DNSSuffix;
}
protected override ConfigurationElement CreateNewElement()
{
return new ProxyElement();
}
public new ProxyElement this[string dnxSuffix]
{
get
{
// Force the get by key, not index
object key = dnxSuffix;
return (ProxyElement)base.BaseGet(key);
}
}
public ProxyElement this[int index]
{
get { return (ProxyElement)base.BaseGet(index); }
}
}
#region ProxyElement
public class ProxyElement : ConfigurationElement
{
public ProxyElement()
{
}
public ProxyElement(string dnsSuffix, string defaultGateway, string address, string port)
{
DNSSuffix = dnsSuffix;
DefaultGateway = defaultGateway;
Address = address;
Port = port;
}
[ConfigurationProperty("dnsSuffix", IsKey = true, IsRequired = true, DefaultValue = "")]
public string DNSSuffix
{
get { return (string)base["dnsSuffix"]; }
set { base["dnsSuffix"] = value; }
}
[ConfigurationProperty("defaultGateway", IsRequired = true, DefaultValue = "")]
public string DefaultGateway
{
get { return (string)base["defaultGateway"]; }
set { base["defaultGateway"] = value; }
}
[ConfigurationProperty("address", DefaultValue = "")]
public string Address
{
get { return (string)base["address"]; }
set { base["address"] = value; }
}
[ConfigurationProperty("port", DefaultValue = "")]
public string Port
{
get { return (string)base["port"]; }
set { base["port"] = value; }
}
[ConfigurationProperty("bypassLocal", DefaultValue = true)]
public bool BypassLocal
{
get { return (bool)base["bypassLocal"]; }
set { base["bypassLocal"] = value; }
}
}
#endregion
}
I went the “lazy-route”, and simply created a Windows exe application, set the Form1 to start in the minimized state and indicated that it should not appear in Windows taskbar by setting ShowInTaskBar property to false.
The Form1 has no added controls, and the entire code behind is listed below:
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Net.NetworkInformation;
namespace AutoProxy
{
public partial class Form1 : Form
{
ProxyElement[] _proxies = null;
EventLog _log = null;
public Form1()
{
InitializeComponent();
try
{
_log = new EventLog();
_log.Source = "AutoProxy";
_proxies = ProxySettingsSection.Proxies;
// Set it now...
NetworkChange_NetworkAddressChanged(null, null);
}
catch (Exception ex)
{
_log.WriteEntry("SmartIEProxy failed reading configuration file. " + ex.Message, EventLogEntryType.Error);
// Abort the service startup
throw;
}
try
{
// Do we have access to the configuration file?
// System.Configuration.ConfigurationSettings.
// No exception handling, so the service stops in case of an exception
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);
}
catch (Exception ex)
{
_log.WriteEntry("SmartIEProxy failed to subscribe to NetworkAddressChanged event. " + ex.Message, EventLogEntryType.Error);
// Abort the service startup
throw;
}
}
void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
try
{
IEProxy.UnsetProxy();
_log.WriteEntry(string.Format("IE proxy un-set by AutoProxy.exe"), EventLogEntryType.Information);
NetworkInterface[] networks = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface n in networks)
{
if (n.OperationalStatus == OperationalStatus.Up)
{
IPInterfaceProperties props = n.GetIPProperties();
string newProxy = null, exceptions = null;
foreach (ProxyElement proxy in _proxies)
{
if (string.Compare(props.DnsSuffix, proxy.DNSSuffix, true) == 0 &&
((props.GatewayAddresses.Count > 0) && props.GatewayAddresses[0].Address.ToString() == proxy.DefaultGateway))
{
newProxy = proxy.Address + ":" + proxy.Port;
exceptions = proxy.BypassLocal ? "<local>" : "";
break;
}
}
if (string.IsNullOrEmpty(newProxy) == false)
{
IEProxy.SetProxy(newProxy, exceptions);
_log.WriteEntry(string.Format("Proxy changed to {0} by AutoProxy.exe", newProxy), EventLogEntryType.Information);
break;
}
}
}
}
catch (Exception ex)
{
_log.WriteEntry(ex.ToString(), EventLogEntryType.Error);
}
}
}
}
The last piece of the puzzle is the code that actually sets the IE’s proxy settings. Create IEProxy.cs class and add the code below:
using System.Runtime.InteropServices;
using System;
using System.ComponentModel;
namespace AutoProxy
{
// Source: http://www.codeproject.com/KB/IP/Change\_IE7\_Proxy\_Setting.aspx
internal class IEProxy
{
public static bool UnsetProxy()
{
return SetProxy(null, null);
}
public static bool SetProxy(string strProxy)
{
return SetProxy(strProxy, null);
}
public static bool SetProxy(string strProxy, string exceptions)
{
InternetPerConnOptionList list = new InternetPerConnOptionList();
int optionCount = string.IsNullOrEmpty(strProxy) ? 1 : (string.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
// use THIS proxy server
if (optionCount > 1)
{
options[1].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
// except for these addresses ...
if (optionCount > 2)
{
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
list.szConnection = IntPtr.Zero;
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
{
IntPtr opt = new IntPtr(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem((Int32)list.dwSize);
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize) ? -1 : 0;
if (returnvalue == 0)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastWin32Error();
}
// FREE the data ASAP
Marshal.FreeCoTaskMem(optionsPtr);
Marshal.FreeCoTaskMem(ipcoListPtr);
if (returnvalue > 0)
{ // throw the error codes, they might be helpful
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (returnvalue < 0);
}
#region WinInet structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetPerConnOptionList
{
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
public IntPtr szConnection; // connection name to set/query options
public int dwOptionCount; // number of options to set/query
public int dwOptionError; // on error, which option failed
//[MarshalAs(UnmanagedType.)]
public IntPtr options;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
{
static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
static InternetConnectionOption()
{
InternetConnectionOption.Size = Marshal.SizeOf(typeof(InternetConnectionOption));
}
// Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public IntPtr m_StringPtr;
}
}
#endregion
#region WinInet enums
//
// options manifests for Internet{Query|Set}Option
//
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
#endregion
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
}
}
}
That’s all there is to it… Now, compile, and add a shortcut to the Startup folder for a specific user or all users, e.g. C:\Users\UserNameHere\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup.
Enjoy.
P.S. The binaries are posted as an attachment.
Anonymous
December 22, 2008
This is so useful! Welcome back, Irena. Back with the flag on top :) ThanksAnonymous
October 09, 2009
Thank you so much for posting this. I would never be able to figure this out on my own.