using System; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.InteropServices; using Vanara.PInvoke; using static Vanara.PInvoke.WinINet; using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; namespace Vanara.Network { /// Provides access to proxy settings for an internet connection. /// public class InternetProxyOptions : IDisposable { private SafeHINTERNET hInet = SafeHINTERNET.Null; /// /// Initializes a new instance of the class that provides access to system options for the local machine. /// public InternetProxyOptions() { } /// /// Initializes a new instance of the InternetProxyOptions class by creating a session with options. /// /// /// String that specifies the name of the application or entity calling the functions. This name is used as the user agent in the /// HTTP protocol. /// /// /// String that specifies the name of the proxy server(s) to use. Do not use an empty string, because it will be used as the proxy /// name. Only CERN type proxies (HTTP only) and the TIS FTP gateway (FTP only) are recognized. /// /// /// An optional list of host names or IP addresses, or both, that should not be routed through the proxy. The list can contain /// wildcards. Do not use an empty string, because it will be used as a proxy bypass. If this parameter specifies the /// "<local>" macro, the function bypasses the proxy for any host name that does not contain a period. /// /// By default, the proxy will bypass requests that use the host names "localhost", "loopback", "127.0.0.1", or "[::1]". This /// behavior exists because a remote proxy server typically will not resolve these addresses properly. /// /// /// /// If , does not make network requests. All entities are returned from the cache. If the requested item is /// not in the cache, a suitable error is returned. /// /// If , makes only asynchronous requests. public InternetProxyOptions(string agentName, string manualProxyUrl, string[] proxyBypassEntries = null, bool offline = false, bool asyncOnly = false) { InternetApiFlags flags = 0; if (offline) flags |= InternetApiFlags.INTERNET_FLAG_OFFLINE; if (asyncOnly) flags |= InternetApiFlags.INTERNET_FLAG_ASYNC; hInet = InternetOpen(agentName, InternetOpenType.INTERNET_OPEN_TYPE_PROXY, manualProxyUrl, proxyBypassEntries is null ? null : string.Join(";", proxyBypassEntries), flags); Win32Error.ThrowLastErrorIfInvalid(hInet); } //public int DisplayInternetControlPanel(HWND windowHandle) => LaunchInternetControlPanel(windowHandle); /// Gets or sets a value indicating whether the connection automatically detects settings. /// if the connection automatically detects settings; otherwise, . public bool AutomaticallyDetectSettings { get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT); set => SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT, value); } /// Gets a value indicating whether this session has any proxy set. /// if this session has a proxy set; otherwise, . public bool HasProxy => AutomaticallyDetectSettings || SetupScriptUrl != null || ManualProxyUrl != null; /// Gets or sets a string containing the proxy server. public string ManualProxyUrl { get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_PROXY) ? GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_SERVER) : null; set { SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_SERVER, value); SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_PROXY, value != null); } } /// Gets or sets an array of URLs that do not use the proxy server. public string[] ProxyBypassEntries { get => GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_BYPASS)?.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); set => SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_BYPASS, value is null ? null : string.Join(";", value)); } /// Gets or sets a string containing the URL to the automatic configuration script. public string SetupScriptUrl { get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL) ? GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_AUTOCONFIG_URL) : null; set { SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_AUTOCONFIG_URL, value); SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL, value != null); } } /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() => hInet?.Dispose(); /// /// Notifies the system that the registry settings have been changed so that it verifies the settings on the next call to InternetConnect. /// public void NotifyInternetOptionSettingsChanged() => Win32Error.ThrowLastErrorIfFalse(InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_SETTINGS_CHANGED)); /// Causes the proxy data to be reread from the registry for a handle. public bool RefreshProxyData() => InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_REFRESH); private T GetOption(INTERNET_PER_CONN_OPTION_ID pco) { var getType = typeof(T); if (getType.IsEnum) getType = typeof(uint); if (!CorrespondingTypeAttribute.CanGet(pco, getType)) throw new InvalidOperationException("Invalid output type specified."); var ico = new INTERNET_PER_CONN_OPTION { dwOption = pco }; using var pIco = new SafeCoTaskMemStruct(ico, Marshal.SizeOf(typeof(INTERNET_PER_CONN_OPTION)) + (Kernel32.MAX_PATH * Vanara.Extensions.StringHelper.GetCharSize())); var perConnOptList = INTERNET_PER_CONN_OPTION_LIST.Default; perConnOptList.dwOptionCount = 1; perConnOptList.pOptions = pIco; using var pPerConnOptList = SafeCoTaskMemHandle.CreateFromStructure(perConnOptList); int size = pPerConnOptList.Size + pIco.Size; Win32Error.ThrowLastErrorIfFalse(InternetQueryOption(hInet, InternetOptionFlags.INTERNET_OPTION_PER_CONNECTION_OPTION, pPerConnOptList, ref size)); ico = pIco.Value; if (getType == typeof(string)) return (T)(object)ico.Value.pszValue.ToString(); if (getType == typeof(FILETIME)) return (T)(object)ico.Value.ftValue; return (T)(object)ico.Value.dwValue; } private bool HasProxyFlag(PER_CONN_FLAGS proxyFlagToCheck) => GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS).IsFlagSet(proxyFlagToCheck); private void SetOptionFlag(PER_CONN_FLAGS flagToSet, bool enable) { var currentFlags = GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS); var option = new INTERNET_PER_CONN_OPTION { dwOption = INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS }; option.Value.dwValue = (uint)currentFlags.SetFlags(flagToSet, enable); SetOptions(new[] { option }); } private void SetOptions(INTERNET_PER_CONN_OPTION[] options) { using var pOptions = new PinnedObject(options); var optionList = new INTERNET_PER_CONN_OPTION_LIST { dwSize = (uint)Marshal.SizeOf(typeof(INTERNET_PER_CONN_OPTION_LIST)), dwOptionCount = (uint)options.Length, pOptions = pOptions }; Win32Error.ThrowLastErrorIfFalse(InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_PER_CONNECTION_OPTION, optionList)); NotifyInternetOptionSettingsChanged(); } private void SetOptionString(INTERNET_PER_CONN_OPTION_ID optionId, string value) { using var pProxy = new SafeCoTaskMemString(value, CharSet.Auto); var option = new INTERNET_PER_CONN_OPTION { dwOption = optionId }; option.Value.pszValue = (IntPtr)pProxy; SetOptions(new[] { option }); } } }