Recipe 16.10. 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 Example 16-13. 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. Example 16-13. 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 Example 16-14 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. Example 16-14. 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 Example 16-15 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. Example 16-15. 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 Example 16-16. 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. Example 16-16. 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. |