diff --git a/shadowsocks-csharp/Controller/System/SystemProxy.cs b/shadowsocks-csharp/Controller/System/SystemProxy.cs index 1d61dd86..56137e44 100644 --- a/shadowsocks-csharp/Controller/System/SystemProxy.cs +++ b/shadowsocks-csharp/Controller/System/SystemProxy.cs @@ -10,17 +10,12 @@ namespace Shadowsocks.Controller { private static Logger logger = LogManager.GetCurrentClassLogger(); - private static string GetTimestamp(DateTime value) - { - return value.ToString("yyyyMMddHHmmssfff"); - } - public static void Update(Configuration config, bool forceDisable, PACServer pacSrv, bool noRetry = false) { bool global = config.global; bool enabled = config.enabled; - if (forceDisable) + if (forceDisable || WinINet.operational) { enabled = false; } @@ -31,7 +26,7 @@ namespace Shadowsocks.Controller { if (global) { - Sysproxy.SetIEProxy(true, true, "localhost:" + config.localPort.ToString(), null); + WinINet.ProxyGlobal("localhost:" + config.localPort.ToString(), ""); } else { @@ -45,12 +40,12 @@ namespace Shadowsocks.Controller pacUrl = pacSrv.PacUrl; } - Sysproxy.SetIEProxy(true, false, null, pacUrl); + WinINet.ProxyPAC(pacUrl); } } else { - Sysproxy.SetIEProxy(false, false, null, null); + WinINet.Restore(); } } catch (ProxyException ex) @@ -61,7 +56,7 @@ namespace Shadowsocks.Controller var ret = MessageBox.Show(I18N.GetString("Error occured when process proxy setting, do you want reset current setting and retry?"), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (ret == DialogResult.Yes) { - Sysproxy.ResetIEProxy(); + WinINet.Reset(); Update(config, forceDisable, pacSrv, true); } } diff --git a/shadowsocks-csharp/Data/sysproxy.exe.gz b/shadowsocks-csharp/Data/sysproxy.exe.gz deleted file mode 100644 index 980d304b..00000000 Binary files a/shadowsocks-csharp/Data/sysproxy.exe.gz and /dev/null differ diff --git a/shadowsocks-csharp/Data/sysproxy64.exe.gz b/shadowsocks-csharp/Data/sysproxy64.exe.gz deleted file mode 100644 index c5ff36af..00000000 Binary files a/shadowsocks-csharp/Data/sysproxy64.exe.gz and /dev/null differ diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index 253c66c0..847eb7a7 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -151,12 +151,6 @@ ..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Data\sysproxy64.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Data\sysproxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\data\user-rule.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 diff --git a/shadowsocks-csharp/Util/SystemProxy/RAS.cs b/shadowsocks-csharp/Util/SystemProxy/RAS.cs new file mode 100644 index 00000000..3dd3ef9a --- /dev/null +++ b/shadowsocks-csharp/Util/SystemProxy/RAS.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Shadowsocks.Util.SystemProxy +{ + + enum RasFieldSizeConst + { + MaxEntryName = 256, + MaxPath = 260, + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct RasEntryName + { + public int dwSize; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS.MaxEntryName + 1)] + public string szEntryName; + + public int dwFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS.MaxPath + 1)] + public string szPhonebookPath; + } + class RAS + { + public const int MaxEntryName = 256; + public const int MaxPath = 260; + + const int ESuccess = 0; + const int RasBase = 600; + const int EBufferTooSmall = 603; + + [DllImport("rasapi32.dll", CharSet = CharSet.Auto)] + private static extern uint RasEnumEntries( + // reserved, must be NULL + string reserved, + // pointer to full path and file name of phone-book file + string lpszPhonebook, + // buffer to receive phone-book entries + [In, Out] RasEntryName[] lprasentryname, + // size in bytes of buffer + ref int lpcb, + // number of entries written to buffer + out int lpcEntries + ); + + public static string[] GetAllConnections() + { + int lpNames = 0; + int entryNameSize = 0; + int lpSize = 0; + uint retval = ESuccess; + RasEntryName[] names = null; + + entryNameSize = Marshal.SizeOf(typeof(RasEntryName)); + + // Windows Vista or later: To determine the required buffer size, call RasEnumEntries + // with lprasentryname set to NULL. The variable pointed to by lpcb should be set to zero. + // The function will return the required buffer size in lpcb and an error code of ERROR_BUFFER_TOO_SMALL. + retval = RasEnumEntries(null, null, null, ref lpSize, out lpNames); + if (retval == EBufferTooSmall) + { + names = new RasEntryName[lpNames]; + for (int i = 0; i < names.Length; i++) + { + names[i].dwSize = entryNameSize; + } + + retval = RasEnumEntries(null, null, names, ref lpSize, out lpNames); + } + + if (retval == ESuccess) + { + if (lpNames == 0) + { + // no entries found. + return Array.Empty(); + } + return names.Select(n => n.szEntryName).ToArray(); + } + else + { + throw new Exception(); + } + } + } +} diff --git a/shadowsocks-csharp/Util/SystemProxy/Sysproxy.cs b/shadowsocks-csharp/Util/SystemProxy/Sysproxy.cs deleted file mode 100644 index b1ffcac4..00000000 --- a/shadowsocks-csharp/Util/SystemProxy/Sysproxy.cs +++ /dev/null @@ -1,291 +0,0 @@ -using Newtonsoft.Json; -using NLog; -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; - -namespace Shadowsocks.Util.SystemProxy -{ - public static class Sysproxy - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - private const string _userWininetConfigFile = "user-wininet.json"; - - private readonly static string[] _lanIP = { - "", - "localhost", - "127.*", - "10.*", - "172.16.*", - "172.17.*", - "172.18.*", - "172.19.*", - "172.20.*", - "172.21.*", - "172.22.*", - "172.23.*", - "172.24.*", - "172.25.*", - "172.26.*", - "172.27.*", - "172.28.*", - "172.29.*", - "172.30.*", - "172.31.*", - "192.168.*" - }; - - - private static string _queryStr; - - // In general, this won't change - // format: - // - // - // - // - private static SysproxyConfig _userSettings = null; - - enum RET_ERRORS : int - { - RET_NO_ERROR = 0, - INVALID_FORMAT = 1, - NO_PERMISSION = 2, - SYSCALL_FAILED = 3, - NO_MEMORY = 4, - INVAILD_OPTION_COUNT = 5, - }; - - static Sysproxy() - { - try - { - FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"), - Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe); - } - catch (IOException e) - { - logger.LogUsefulException(e); - } - } - - public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL) - { - Read(); - - if (!_userSettings.UserSettingsRecorded) - { - // record user settings - ExecSysproxy("query"); - ParseQueryStr(_queryStr); - } - - string arguments; - if (enable) - { - string customBypassString = _userSettings.BypassList ?? ""; - List customBypassList = new List(customBypassString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); - customBypassList.AddRange(_lanIP); - string[] realBypassList = customBypassList.Distinct().ToArray(); - string realBypassString = string.Join(";", realBypassList); - - arguments = global - ? $"global {proxyServer} {realBypassString}" - : $"pac {pacURL}"; - } - else - { - // restore user settings - var flags = _userSettings.Flags; - var proxy_server = _userSettings.ProxyServer ?? "-"; - var bypass_list = _userSettings.BypassList ?? "-"; - var pac_url = _userSettings.PacUrl ?? "-"; - arguments = $"set {flags} {proxy_server} {bypass_list} {pac_url}"; - - // have to get new settings - _userSettings.UserSettingsRecorded = false; - } - - Save(); - ExecSysproxy(arguments); - } - - // set system proxy to 1 (null) (null) (null) - public static bool ResetIEProxy() - { - try - { - // clear user-wininet.json - _userSettings = new SysproxyConfig(); - Save(); - // clear system setting - ExecSysproxy("set 1 - - -"); - } - catch (Exception) - { - return false; - } - - return true; - } - - private static void ExecSysproxy(string arguments) - { - // using event to avoid hanging when redirect standard output/error - // ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why - // and http://blog.csdn.net/zhangweixing0/article/details/7356841 - using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) - using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) - { - using (var process = new Process()) - { - // Configure the process using the StartInfo properties. - process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe"); - process.StartInfo.Arguments = arguments; - process.StartInfo.WorkingDirectory = Utils.GetTempPath(); - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.RedirectStandardOutput = true; - - // Need to provide encoding info, or output/error strings we got will be wrong. - process.StartInfo.StandardOutputEncoding = Encoding.Unicode; - process.StartInfo.StandardErrorEncoding = Encoding.Unicode; - - process.StartInfo.CreateNoWindow = true; - - StringBuilder output = new StringBuilder(); - StringBuilder error = new StringBuilder(); - - process.OutputDataReceived += (sender, e) => - { - if (e.Data == null) - { - outputWaitHandle.Set(); - } - else - { - output.AppendLine(e.Data); - } - }; - process.ErrorDataReceived += (sender, e) => - { - if (e.Data == null) - { - errorWaitHandle.Set(); - } - else - { - error.AppendLine(e.Data); - } - }; - try - { - process.Start(); - - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - - process.WaitForExit(); - } - catch (System.ComponentModel.Win32Exception e) - { - // log the arguments - throw new ProxyException(ProxyExceptionType.FailToRun, process.StartInfo.Arguments, e); - } - var stderr = error.ToString(); - var stdout = output.ToString(); - - var exitCode = process.ExitCode; - if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) - { - throw new ProxyException(ProxyExceptionType.SysproxyExitError, stderr); - } - - if (arguments == "query") - { - if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) - { - // we cannot get user settings - throw new ProxyException(ProxyExceptionType.QueryReturnEmpty); - } - _queryStr = stdout; - } - } - } - } - - private static void Save() - { - try - { - using (StreamWriter sw = new StreamWriter(File.Open(Utils.GetTempPath(_userWininetConfigFile), FileMode.Create))) - { - string jsonString = JsonConvert.SerializeObject(_userSettings, Formatting.Indented); - sw.Write(jsonString); - sw.Flush(); - } - } - catch (IOException e) - { - logger.LogUsefulException(e); - } - } - - private static void Read() - { - try - { - string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile)); - _userSettings = JsonConvert.DeserializeObject(configContent); - } - catch (Exception) - { - // Suppress all exceptions. finally block will initialize new user config settings. - } - finally - { - if (_userSettings == null) _userSettings = new SysproxyConfig(); - } - } - - private static void ParseQueryStr(string str) - { - string[] userSettingsArr = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - - // sometimes sysproxy output in utf16le instead of ascii - // manually translate it - if (userSettingsArr.Length != 4) - { - byte[] strByte = Encoding.ASCII.GetBytes(str); - str = Encoding.Unicode.GetString(strByte); - userSettingsArr = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - // still fail, throw exception with string hexdump - if (userSettingsArr.Length != 4) - { - throw new ProxyException(ProxyExceptionType.QueryReturnMalformed, BitConverter.ToString(strByte)); - } - } - - _userSettings.Flags = userSettingsArr[0]; - - // handle output from WinINET - if (userSettingsArr[1] == "(null)") _userSettings.ProxyServer = null; - else _userSettings.ProxyServer = userSettingsArr[1]; - if (userSettingsArr[2] == "(null)") _userSettings.BypassList = null; - else _userSettings.BypassList = userSettingsArr[2]; - if (userSettingsArr[3] == "(null)") _userSettings.PacUrl = null; - else _userSettings.PacUrl = userSettingsArr[3]; - - _userSettings.UserSettingsRecorded = true; - } - } -} diff --git a/shadowsocks-csharp/Util/SystemProxy/WinINet.cs b/shadowsocks-csharp/Util/SystemProxy/WinINet.cs new file mode 100644 index 00000000..9814fadd --- /dev/null +++ b/shadowsocks-csharp/Util/SystemProxy/WinINet.cs @@ -0,0 +1,447 @@ +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Shadowsocks.Util.SystemProxy +{ + public enum InternetOptions + { + Refresh = 37, + SettingsChanged = 39, + PerConnectionOption = 75, + ProxySettingChanged = 95, + } + + public enum InternetPerConnectionOptionEnum + { + Flags = 1, + ProxyServer = 2, + ProxyBypass = 3, + AutoConfigUrl = 4, + AutoDiscovery = 5, + AutoConfigSecondaryUrl = 6, + AutoConfigReloadDelay = 7, + AutoConfigLastDetectTime = 8, + AutoConfigLastDetectUrl = 9, + FlagsUI = 10, + } + + [Flags] + public enum InternetPerConnectionFlags + { + Direct = 0x01, + Proxy = 0x02, + AutoProxyUrl = 0x04, + AutoDetect = 0x08, + } + + [StructLayout(LayoutKind.Explicit)] + public struct InternetPerConnectionOptionUnion : IDisposable + { + [FieldOffset(0)] + public int dwValue; + + [FieldOffset(0)] + public IntPtr pszValue; + + [FieldOffset(0)] + public System.Runtime.InteropServices.ComTypes.FILETIME ftValue; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + if (pszValue != IntPtr.Zero) + { + Marshal.FreeHGlobal(pszValue); + pszValue = IntPtr.Zero; + } + } + } + } + + + + [StructLayout(LayoutKind.Sequential)] + public struct InternetPerConnectionOption + { + public int dwOption; + public InternetPerConnectionOptionUnion Value; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct InternetPerConnectionOptionList : IDisposable + { + public int Size; + + // The connection to be set. NULL means LAN. + public IntPtr Connection; + + public int OptionCount; + public int OptionError; + + // List of INTERNET_PER_CONN_OPTIONs. + public IntPtr pOptions; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + private void Dispose(bool disposing) + { + if (disposing) + { + if (Connection != IntPtr.Zero) + { + Marshal.FreeHGlobal(Connection); + Connection = IntPtr.Zero; + } + + if (pOptions != IntPtr.Zero) + { + Marshal.FreeHGlobal(pOptions); + pOptions = IntPtr.Zero; + } + } + } + } + + public class WinINetSetting + { + public InternetPerConnectionFlags Flags = InternetPerConnectionFlags.Direct; + public string ProxyServer; + public string ProxyBypass; + public string AutoConfigUrl; + } + + public class WinINet + { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + private const string SettingFile = "wininet-setting.json"; + private static WinINetSetting initialSetting; + + public static bool operational { get; private set; } = true; + static WinINet() + { + try + { + Query(); + } + catch (DllNotFoundException) + { + operational = false; + // Not on windows + logger.Info("You are not running on Windows platform, system proxy will disable"); + } + catch (Win32Exception we) + { + if (we.NativeErrorCode == 12178) + { + logger.Warn("WPAD service is not running, system proxy will disable"); + // WPAD not running + } + else + { + throw we; + } + } + + Load(); + } + + public static void ProxyGlobal(string server, string bypass) + { + List options = new List + { + GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.Proxy|InternetPerConnectionFlags.Direct), + GetOption(InternetPerConnectionOptionEnum.ProxyServer,server), + GetOption(InternetPerConnectionOptionEnum.ProxyBypass,bypass), + }; + Exec(options); + } + public static void ProxyPAC(string url) + { + List options = new List + { + GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.AutoProxyUrl|InternetPerConnectionFlags.Direct), + GetOption(InternetPerConnectionOptionEnum.AutoConfigUrl,url), + }; + Exec(options); + } + public static void Direct() + { + List options = new List + { + GetOption(InternetPerConnectionOptionEnum.Flags,InternetPerConnectionFlags.Direct), + }; + Exec(options); + } + + private static void Load() + { + try + { + string configContent = File.ReadAllText(Utils.GetTempPath(SettingFile)); + initialSetting = JsonConvert.DeserializeObject(configContent); + } + catch (Exception) + { + // Suppress all exceptions. finally block will initialize new user config settings. + } + finally + { + initialSetting ??= new WinINetSetting(); + } + } + private static void Save() + { + try + { + using (StreamWriter sw = new StreamWriter(File.Open(Utils.GetTempPath(SettingFile), FileMode.Create))) + { + string jsonString = JsonConvert.SerializeObject(initialSetting, Formatting.Indented); + sw.Write(jsonString); + sw.Flush(); + } + } + catch (IOException) + { + // logger.LogUsefulException(e); + } + } + private static void Record() + { + initialSetting ??= Query(); + } + public static void Restore() + { + Set(initialSetting); + } + + public static void Set(WinINetSetting setting) + { + List options = new List + { + GetOption(InternetPerConnectionOptionEnum.Flags,setting.Flags), + GetOption(InternetPerConnectionOptionEnum.ProxyServer,setting.ProxyServer), + GetOption(InternetPerConnectionOptionEnum.ProxyBypass,setting.ProxyBypass), + GetOption(InternetPerConnectionOptionEnum.AutoConfigUrl,setting.AutoConfigUrl), + }; + Exec(options); + } + public static void Reset() + { + Set(new WinINetSetting + { + Flags = InternetPerConnectionFlags.Direct, + ProxyServer = "", + ProxyBypass = "", + AutoConfigUrl = "", + }); + } + + public static WinINetSetting Query() + { + if (!operational) + { + return new WinINetSetting(); + } + + List options = new List + { + new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.FlagsUI}, + new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.ProxyServer}, + new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.ProxyBypass}, + new InternetPerConnectionOption{dwOption = (int)InternetPerConnectionOptionEnum.AutoConfigUrl}, + }; + + (IntPtr unmanagedList, int listSize) = PrepareOptionList(options, null); + bool ok = InternetQueryOption(IntPtr.Zero, (int)InternetOptions.PerConnectionOption, unmanagedList, ref listSize); + if (!ok) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + WinINetSetting proxy = new WinINetSetting(); + + InternetPerConnectionOptionList ret = Marshal.PtrToStructure(unmanagedList); + IntPtr p = ret.pOptions; + int nOption = ret.OptionCount; + List outOptions = new List(); + for (int i = 0; i < nOption; i++) + { + InternetPerConnectionOption o = Marshal.PtrToStructure(p); + outOptions.Add(o); + p += Marshal.SizeOf(o); + } + + foreach (InternetPerConnectionOption o in outOptions) + { + switch ((InternetPerConnectionOptionEnum)o.dwOption) + { + case InternetPerConnectionOptionEnum.FlagsUI: + case InternetPerConnectionOptionEnum.Flags: + proxy.Flags = (InternetPerConnectionFlags)o.Value.dwValue; + break; + case InternetPerConnectionOptionEnum.AutoConfigUrl: + proxy.AutoConfigUrl = Marshal.PtrToStringAuto(o.Value.pszValue); + break; + case InternetPerConnectionOptionEnum.ProxyBypass: + proxy.ProxyBypass = Marshal.PtrToStringAuto(o.Value.pszValue); + break; + case InternetPerConnectionOptionEnum.ProxyServer: + proxy.ProxyServer = Marshal.PtrToStringAuto(o.Value.pszValue); + break; + default: + break; + } + } + return proxy; + } + + private static InternetPerConnectionOption GetOption( + InternetPerConnectionOptionEnum option, + InternetPerConnectionFlags flag + ) + { + return new InternetPerConnectionOption + { + dwOption = (int)option, + Value = + { + dwValue = (int)flag, + } + }; + } + + private static InternetPerConnectionOption GetOption( + InternetPerConnectionOptionEnum option, + string param + ) + { + return new InternetPerConnectionOption + { + dwOption = (int)option, + Value = + { + pszValue = Marshal.StringToCoTaskMemAuto(param), + } + }; + } + + private static (IntPtr, int) PrepareOptionList(List options, string connName) + { + int len = options.Sum(o => Marshal.SizeOf(o)); + + IntPtr buf = Marshal.AllocCoTaskMem(len); + IntPtr cur = buf; + + foreach (InternetPerConnectionOption o in options) + { + Marshal.StructureToPtr(o, cur, false); + cur += Marshal.SizeOf(o); + } + InternetPerConnectionOptionList optionList = new InternetPerConnectionOptionList + { + pOptions = buf, + OptionCount = options.Count, + Connection = string.IsNullOrEmpty(connName) + ? IntPtr.Zero + : Marshal.StringToHGlobalAuto(connName), + OptionError = 0, + }; + int listSize = Marshal.SizeOf(optionList); + optionList.Size = listSize; + + IntPtr unmanagedList = Marshal.AllocCoTaskMem(listSize); + Marshal.StructureToPtr(optionList, unmanagedList, true); + return (unmanagedList, listSize); + } + + private static void ClearOptionList(IntPtr list) + { + InternetPerConnectionOptionList l = Marshal.PtrToStructure(list); + Marshal.FreeCoTaskMem(l.pOptions); + Marshal.FreeCoTaskMem(list); + } + + private static void Exec(List options) + { + // TODO: optimize load and save + Load(); + Record(); + + Exec(options, null); + foreach (string conn in RAS.GetAllConnections()) + { + Exec(options, conn); + } + + Save(); + } + + private static void Exec(List options, string connName) + { + if (!operational) + { + return; + } + + (IntPtr unmanagedList, int listSize) = PrepareOptionList(options, connName); + + bool ok = InternetSetOption( + IntPtr.Zero, + (int)InternetOptions.PerConnectionOption, + unmanagedList, + listSize + ); + + if (!ok) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + ClearOptionList(unmanagedList); + ok = InternetSetOption( + IntPtr.Zero, + (int)InternetOptions.ProxySettingChanged, + IntPtr.Zero, + 0 + ); + if (!ok) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + ok = InternetSetOption( + IntPtr.Zero, + (int)InternetOptions.Refresh, + IntPtr.Zero, + 0 + ); + if (!ok) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); + + [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern bool InternetQueryOption(IntPtr hInternet, uint dwOption, IntPtr lpBuffer, ref int lpdwBufferLength); + + } +} diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 376279a4..17011a25 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -3,6 +3,7 @@ using Shadowsocks.Controller; using Shadowsocks.Model; using Shadowsocks.Properties; using Shadowsocks.Util; +using Shadowsocks.Util.SystemProxy; using System; using System.Diagnostics; using System.Drawing; @@ -38,7 +39,7 @@ namespace Shadowsocks.View private ToolStripMenuItem disableItem; private ToolStripMenuItem AutoStartupItem; private ToolStripMenuItem ShareOverLANItem; - private ToolStripSeparator SeperatorItem; + private ToolStripSeparator SeperatorItem; private ToolStripMenuItem ConfigItem; private ToolStripMenuItem ProtocolHandlerItem; private ToolStripMenuItem ServersItem; @@ -507,7 +508,8 @@ namespace Shadowsocks.View ) { menuItem.Checked = true; - } else + } + else { menuItem.Checked = false; } @@ -671,6 +673,9 @@ namespace Shadowsocks.View globalModeItem.Checked = config.global; PACModeItem.Checked = !config.global; } + + globalModeItem.Enabled = WinINet.operational; + PACModeItem.Enabled = WinINet.operational; } private void GlobalModeItem_Click(object sender, EventArgs e) diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 25aa9c64..35e28c17 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -31,8 +31,6 @@ - - @@ -95,8 +93,6 @@ - -