Use the Current Internet Connection Settings




Use the Current Internet Connection Settings

Problem

Your program wants to use the current Internet connection settings without forcing the user to add them to your application manually.

Solution

Read the current Internet connectivity settings with the InternetSettingsReader class provided for you in Figure. InternetSettingsReader calls some methods of the WinINet API via P/Invoke to retrieve current Internet connection information. The majority of the work is done in setting up the structures that WinINet uses and then marshaling the structure pointers correctly to retrieve the values.

InternetSettingsReader class

public class InternetSettingsReader
{
    #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
        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 used in INTERNET_PER_CONN_OPTON struct
    //
    public enum PerConnOption
    {
    // Sets or retrieves the connection type. The Value member will contain one
    // or more of the values from PerConnFlags
            INTERNET_PER_CONN_FLAGS = 1,
    // Sets or retrieves a string containing the proxy servers
        INTERNET_PER_CONN_PROXY_SERVER = 2,
    // Sets or retrieves a string containing the URLs that do not use the
    // proxy server
            INTERNET_PER_CONN_PROXY_BYPASS = 3,
    // Sets or retrieves a string containing the URL to the automatic
    // configuration script
        INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
    }

    //
    // 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
    }

    #region P/Invoke defs
    [DllImport("WinInet.dll", EntryPoint = "InternetQueryOption",
         SetLastError = true)]

    public static extern bool InternetQueryOption(
        IntPtr hInternet,
        int dwOption,
        ref InternetPerConnOptionList optionsList,
        ref int bufferLength
        );
    #endregion

    #region Private Members
    string _proxyAddr = "";
    int _proxyPort = -1;
    bool _bypassLocal = false;
    string _autoConfigAddr = "";
    string[] _proxyExceptions = null;
    PerConnFlags _flags;
    #endregion

    #region CTOR
    public InternetSettingsReader()
    {
    }
    #endregion

Each of the properties of InternetSettingsReader shown in Figure call into the GetInternetConnectionOption method, which returns an InternetConnectionOption. The InternetConnectionOption structure holds all of the pertinent data for the value being returned, and that value is then retrieved based on what type of value was asked for by the specific properties.

InternetSettingsReader properties

#region Properties
public string ProxyAddr
{
    get
    {
        InternetConnectionOption ico =
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_PROXY_SERVER);
        // Parse out the addr and port
        string proxyInfo = Marshal.PtrToStringUni(
                                ico.m_Value.m_StringPtr);
        ParseProxyInfo(proxyInfo);
        return _proxyAddr;
    }
}
public int ProxyPort
{
    get
    {
        InternetConnectionOption ico =
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_PROXY_SERVER);
        // Parse out the addr and port
        string proxyInfo = Marshal.PtrToStringUni(
                                ico.m_Value.m_StringPtr);
        ParseProxyInfo(proxyInfo);
        return _proxyPort;
    }
}
public bool BypassLocalAddresses
{
    get
    {
        InternetConnectionOption ico =
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS);
        // Bypass is listed as <local> in the exceptions list
        string exceptions =
            Marshal.PtrToStringUni(ico.m_Value.m_StringPtr);

        if (exceptions.IndexOf("<local>") != -1)
            _bypassLocal = true;
        else
            _bypassLocal = false; 
        return _bypassLocal;
    }
}
public string AutoConfigurationAddr
{
    get
    {
        InternetConnectionOption ico =
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL);
        // Get these straight
        _autoConfigAddr =
            Marshal.PtrToStringUni(ico.m_Value.m_StringPtr);
        if (_autoConfigAddr == null)
            _autoConfigAddr = "";
        return _autoConfigAddr;
    }
}
public string[] ProxyExceptions
{
    get
    {
        InternetConnectionOption ico =
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS);
        // Exceptions are separated by semicolon
        string exceptions =
            Marshal.PtrToStringUni(ico.m_Value.m_StringPtr);
        if (!string.IsNullOrEmpty(exceptions))
        {
            _proxyExceptions = exceptions.Split(';');
        }
        return _proxyExceptions;
    }
}
public PerConnFlags ConnectionType
{
    get
    {
        InternetConnectionOption ico = 
            GetInternetConnectionOption(
                PerConnOption.INTERNET_PER_CONN_FLAGS);
        _flags = (PerConnFlags)ico.m_Value.m_Int;

        return _flags;
    }
}

#endregion
private void ParseProxyInfo(string proxyInfo)
{
    if(!string.IsNullOrEmpty(proxyInfo))
    {
        string [] parts = proxyInfo.Split(':');
        if (parts.Length == 2)
        {
            _proxyAddr = parts[0];
            try
            {
                _proxyPort = Convert.ToInt32(parts[1]);
            }
            catch (FormatException)
            {
                // No port
                _proxyPort = -1;
            }
        }
        else
        {
            _proxyAddr = parts[0];
            _proxyPort = -1;
        }
    }
}

The GetInternetConnectionOption method shown in Figure does the heavy lifting as far as communicating with WinINet. First an InternetPerConnOptionList is created as well as an InternetConnectionOption structure to hold the returned value. The InternetConnectionOption structure is then pinned so that the garbage collector does not move the structure in memory and the PerConnOption value is assigned to determine what Internet option to retrieve. The InternetPerConnOptionList is initialized to hold the option values and then the WinINet function IntrenetQueryOption is called. The InternetConnectionOption is filled using the Marshal.PtrToStructure method and returned with the value.

GetInternetConnectionOption method

private InternetConnectionOption GetInternetConnectionOption(PerConnOption pco)
{
     // Allocate the list and option
     InternetPerConnOptionList perConnOptList = new InternetPerConnOptionList();
     InternetConnectionOption ico = new InternetConnectionOption();
     // Pin the option structure
     GCHandle gch = GCHandle.Alloc(ico, GCHandleType.Pinned);
     // Initialize the option for the data we want
     ico.m_Option = pco;
     //Initialize the option list for the default connection or LAN
     int listSize = Marshal.SizeOf(perConnOptList);
     perConnOptList.dwSize = listSize;
     perConnOptList.szConnection = IntPtr.Zero;
     perConnOptList.dwOptionCount = 1;
     perConnOptList.dwOptionError = 0;
     // Figure out sizes and offsets
     int icoSize = Marshal.SizeOf(ico);
     int optionTotalSize = icoSize;
     // Alloc enough memory for the option
     perConnOptList.options =
         Marshal.AllocCoTaskMem(icoSize);

    long icoOffset = (long)perConnOptList.options + (long)icoSize;
    // Make pointer from the structure
    IntPtr optionListPtr = perConnOptList.options;
    Marshal.StructureToPtr(ico, optionListPtr, false);

    // Make the query
    if (InternetQueryOption(
        IntPtr.Zero,
        75, //(int)InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
        ref perConnOptList,
        ref listSize) == true)
    {
        // Retrieve the value.
        ico =
(InternetConnectionOption)Marshal.PtrToStructure(perConnOptList.options,
                                        typeof(InternetConnectionOption));
    }
    // Free the COM memory
    Marshal.FreeCoTaskMem(perConnOptList.options);

    // Unpin the structs
    gch.Free();

    return ico;
}

Using the InternetSettingsReader is demonstrated in the GetInternetSettings method shown in Figure. The proxy information is retrieved and displayed to the console here, but could easily be stored in another program for use as proxy information when connecting. See Recipe 14.7 for details on setting up the proxy information for a WebRequest.

Using the InternetSettingsReader

public static void GetInternetSettings()
{
    InternetSettingsReader isr = new InternetSettingsReader();
    Console.WriteLine("Current Proxy Address: {0}",isr.ProxyAddr);
    Console.WriteLine("Current Proxy Port: {0}",isr.ProxyPort);
    Console.WriteLine("Current ByPass Local Address setting: {0}",
                             isr.BypassLocalAddresses);
    Console.WriteLine("Exception addresses for proxy (bypass):");
    if(isr.ProxyExceptions != null)
    {
        foreach(string addr in isr.ProxyExceptions)
        {
            Console.WriteLine("\t{0}",addr);
        }
    }
    Console.WriteLine("Proxy connection type: {0}",isr.ConnectionType.ToString());
}

Output for the Solution:

	Current Proxy Address: CORPORATEPROXY
	Current Proxy Port: 8080
	Current ByPass Local Address setting: True
	Exception addresses for proxy (bypass):
	    corporate.com
	    <local>
	Proxy connection type: PROXY_TYPE_DIRECT, PROXY_TYPE_PROXY

Discussion

The WinInet Windows Internet (WinInet) API is the unmanaged API for interacting with the FTP, HTTP, and Gopher protocols. This API can be used fill in where managed code leaves off, such as with the Internet configuration settings shown in the Solution. It can also be used for downloading files, working with cookies, and participating in Gopher sessions. You need to understand that WinInet is meant to be a client-side API and is not suited for server-side or service applications; issues could arise in your application from improper usage.

There is a huge amount of information available to the C# programmer directly through the FCLFramework class FCLibrary, but at times you still need to roll up your sleeves and talk to the Win32 API. Even in situations in which restricted privileges are the norm, it is not always out of bounds to create a small assembly that needs enhanced access to do P/Invoke. It can have its access locked down so as not to become a risk to the system.

See Also

See the "InternetQueryOption Function [WinInet]" topic in the MSDN documentation.