e.g. Dial-up connection and VPN - use INTERNET_OPTION_PROXY_SETTINGS_CHANGED instead of INTERNET_OPTION_SETTINGS_CHANGED Signed-off-by: Syrone Wong <wong.syrone@gmail.com>tags/3.3.4
@@ -30,7 +30,13 @@ namespace Shadowsocks.Util.SystemProxy | |||||
INTERNET_OPTION_SETTINGS_CHANGED = 39, | INTERNET_OPTION_SETTINGS_CHANGED = 39, | ||||
// Causes the proxy data to be reread from the registry for a handle. | // Causes the proxy data to be reread from the registry for a handle. | ||||
INTERNET_OPTION_REFRESH = 37 | |||||
INTERNET_OPTION_REFRESH = 37, | |||||
// Alerts the current WinInet instance that proxy settings have changed | |||||
// and that they must update with the new settings. | |||||
// To alert all available WinInet instances, set the Buffer parameter of | |||||
// InternetSetOption to NULL and BufferLength to 0 when passing this option. | |||||
INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95 | |||||
} | } | ||||
} | } |
@@ -22,7 +22,7 @@ using System.Runtime.InteropServices; | |||||
namespace Shadowsocks.Util.SystemProxy | namespace Shadowsocks.Util.SystemProxy | ||||
{ | { | ||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | |||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |||||
public struct INTERNET_PER_CONN_OPTION_LIST : IDisposable | public struct INTERNET_PER_CONN_OPTION_LIST : IDisposable | ||||
{ | { | ||||
public int Size; | public int Size; | ||||
@@ -26,7 +26,7 @@ namespace Shadowsocks.Util.SystemProxy | |||||
/// <summary> | /// <summary> | ||||
/// Sets an Internet option. | /// Sets an Internet option. | ||||
/// </summary> | /// </summary> | ||||
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)] | |||||
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] | |||||
internal static extern bool InternetSetOption( | internal static extern bool InternetSetOption( | ||||
IntPtr hInternet, | IntPtr hInternet, | ||||
INTERNET_OPTION dwOption, | INTERNET_OPTION dwOption, | ||||
@@ -0,0 +1,151 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Shadowsocks.Util.SystemProxy | |||||
{ | |||||
public static class RAS | |||||
{ | |||||
private enum RasFieldSizeConstants | |||||
{ | |||||
#region original header | |||||
//#if (WINVER >= 0x400) | |||||
//#define RAS_MaxEntryName 256 | |||||
//#define RAS_MaxDeviceName 128 | |||||
//#define RAS_MaxCallbackNumber RAS_MaxPhoneNumber | |||||
//#else | |||||
//#define RAS_MaxEntryName 20 | |||||
//#define RAS_MaxDeviceName 32 | |||||
//#define RAS_MaxCallbackNumber 48 | |||||
//#endif | |||||
#endregion | |||||
RAS_MaxEntryName = 256, | |||||
RAS_MaxPath = 260 | |||||
} | |||||
private const int ERROR_SUCCESS = 0; | |||||
private const int RASBASE = 600; | |||||
private const int ERROR_BUFFER_TOO_SMALL = RASBASE + 3; | |||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |||||
private struct RasEntryName | |||||
{ | |||||
#region original header | |||||
//#define RASENTRYNAMEW struct tagRASENTRYNAMEW | |||||
//RASENTRYNAMEW | |||||
//{ | |||||
// DWORD dwSize; | |||||
// WCHAR szEntryName[RAS_MaxEntryName + 1]; | |||||
// | |||||
//#if (WINVER >= 0x500) | |||||
// // | |||||
// // If this flag is REN_AllUsers then its a | |||||
// // system phonebook. | |||||
// // | |||||
// DWORD dwFlags; | |||||
// WCHAR szPhonebookPath[MAX_PATH + 1]; | |||||
//#endif | |||||
//}; | |||||
// | |||||
//#define RASENTRYNAMEA struct tagRASENTRYNAMEA | |||||
//RASENTRYNAMEA | |||||
//{ | |||||
// DWORD dwSize; | |||||
// CHAR szEntryName[RAS_MaxEntryName + 1]; | |||||
// | |||||
//#if (WINVER >= 0x500) | |||||
// DWORD dwFlags; | |||||
// CHAR szPhonebookPath[MAX_PATH + 1]; | |||||
//#endif | |||||
//}; | |||||
#endregion | |||||
public int dwSize; | |||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=(int)RasFieldSizeConstants.RAS_MaxEntryName + 1)] | |||||
public string szEntryName; | |||||
public int dwFlags; | |||||
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=(int)RasFieldSizeConstants.RAS_MaxPath + 1)] | |||||
public string szPhonebookPath; | |||||
} | |||||
[DllImport("rasapi32.dll", CharSet = CharSet.Auto)] | |||||
private static extern uint RasEnumEntries( | |||||
string reserved, // reserved, must be NULL | |||||
string lpszPhonebook, // pointer to full path and file name of phone-book file | |||||
[In, Out] RasEntryName[] lprasentryname, // buffer to receive phone-book entries | |||||
ref int lpcb, // size in bytes of buffer | |||||
out int lpcEntries // number of entries written to buffer | |||||
); | |||||
/// <summary> | |||||
/// Get all entries from RAS | |||||
/// </summary> | |||||
/// <param name="allConns"></param> | |||||
/// <returns> | |||||
/// 0: success with entries | |||||
/// 1: success but no entries found | |||||
/// 2: failed | |||||
/// </returns> | |||||
public static uint GetAllConns(ref string[] allConns) | |||||
{ | |||||
int lpNames = 1; | |||||
int entryNameSize = 0; | |||||
int lpSize = 0; | |||||
uint retval = ERROR_SUCCESS; | |||||
RasEntryName[] names = null; | |||||
entryNameSize = Marshal.SizeOf(typeof(RasEntryName)); | |||||
lpSize = lpNames * entryNameSize; | |||||
names = new RasEntryName[lpNames]; | |||||
names[0].dwSize = entryNameSize; | |||||
retval = RAS.RasEnumEntries(null, null, names, ref lpSize, out lpNames); | |||||
//if we have more than one connection, we need to resize | |||||
if (retval == ERROR_BUFFER_TOO_SMALL) | |||||
{ | |||||
names = new RasEntryName[lpNames]; | |||||
for (int i = 0; i < names.Length; i++) | |||||
{ | |||||
names[i].dwSize = entryNameSize; | |||||
} | |||||
retval = RAS.RasEnumEntries(null, null, names, ref lpSize, out lpNames); | |||||
} | |||||
if (retval == ERROR_SUCCESS) | |||||
{ | |||||
if (lpNames == 0) | |||||
{ | |||||
// no entries found. | |||||
return 1; | |||||
} | |||||
allConns = new string[names.Length]; | |||||
for (int i = 0; i < names.Length; i++) | |||||
{ | |||||
allConns[i] = names[i].szEntryName; | |||||
} | |||||
return 0; | |||||
} | |||||
else | |||||
{ | |||||
return 2; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -19,17 +19,19 @@ using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Util.SystemProxy | namespace Shadowsocks.Util.SystemProxy | ||||
{ | { | ||||
public static class WinINet | public static class WinINet | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Set IE settings for LAN connection. | |||||
/// Set IE settings. | |||||
/// </summary> | /// </summary> | ||||
public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL) | |||||
private static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL, string connName) | |||||
{ | { | ||||
List<INTERNET_PER_CONN_OPTION> _optionlist = new List<INTERNET_PER_CONN_OPTION>(); | List<INTERNET_PER_CONN_OPTION> _optionlist = new List<INTERNET_PER_CONN_OPTION>(); | ||||
if (enable) | if (enable) | ||||
{ | { | ||||
if (global) | if (global) | ||||
@@ -38,17 +40,18 @@ namespace Shadowsocks.Util.SystemProxy | |||||
_optionlist.Add(new INTERNET_PER_CONN_OPTION | _optionlist.Add(new INTERNET_PER_CONN_OPTION | ||||
{ | { | ||||
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI, | dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI, | ||||
Value = { dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_PROXY } | |||||
Value = { dwValue = (int)(INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_PROXY | |||||
| INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_DIRECT) } | |||||
}); | }); | ||||
_optionlist.Add(new INTERNET_PER_CONN_OPTION | _optionlist.Add(new INTERNET_PER_CONN_OPTION | ||||
{ | { | ||||
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER, | dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER, | ||||
Value = { pszValue = Marshal.StringToHGlobalAnsi(proxyServer) } | |||||
Value = { pszValue = Marshal.StringToHGlobalAuto(proxyServer) } | |||||
}); | }); | ||||
_optionlist.Add(new INTERNET_PER_CONN_OPTION | _optionlist.Add(new INTERNET_PER_CONN_OPTION | ||||
{ | { | ||||
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS, | dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS, | ||||
Value = { pszValue = Marshal.StringToHGlobalAnsi("<local>") } | |||||
Value = { pszValue = Marshal.StringToHGlobalAuto("<local>") } | |||||
}); | }); | ||||
} | } | ||||
else | else | ||||
@@ -62,7 +65,7 @@ namespace Shadowsocks.Util.SystemProxy | |||||
_optionlist.Add(new INTERNET_PER_CONN_OPTION | _optionlist.Add(new INTERNET_PER_CONN_OPTION | ||||
{ | { | ||||
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_AUTOCONFIG_URL, | dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_AUTOCONFIG_URL, | ||||
Value = { pszValue = Marshal.StringToHGlobalAnsi(pacURL) } | |||||
Value = { pszValue = Marshal.StringToHGlobalAuto(pacURL) } | |||||
}); | }); | ||||
} | } | ||||
} | } | ||||
@@ -101,8 +104,9 @@ namespace Shadowsocks.Util.SystemProxy | |||||
// Return the unmanaged size of an object in bytes. | // Return the unmanaged size of an object in bytes. | ||||
optionList.Size = Marshal.SizeOf(optionList); | optionList.Size = Marshal.SizeOf(optionList); | ||||
// IntPtr.Zero means LAN connection. | |||||
optionList.Connection = IntPtr.Zero; | |||||
optionList.Connection = connName.IsNullOrEmpty() | |||||
? IntPtr.Zero // NULL means LAN | |||||
: Marshal.StringToHGlobalAuto(connName); // TODO: not working if contains Chinese | |||||
optionList.OptionCount = _optionlist.Count; | optionList.OptionCount = _optionlist.Count; | ||||
optionList.OptionError = 0; | optionList.OptionError = 0; | ||||
@@ -132,15 +136,56 @@ namespace Shadowsocks.Util.SystemProxy | |||||
// Notify the system that the registry settings have been changed and cause | // Notify the system that the registry settings have been changed and cause | ||||
// the proxy data to be reread from the registry for a handle. | // the proxy data to be reread from the registry for a handle. | ||||
NativeMethods.InternetSetOption( | |||||
// bReturn = NativeMethods.InternetSetOption( | |||||
// IntPtr.Zero, | |||||
// INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED, | |||||
// IntPtr.Zero, 0); | |||||
// if ( ! bReturn ) | |||||
// { | |||||
// Logging.Error("InternetSetOption:INTERNET_OPTION_SETTINGS_CHANGED"); | |||||
// } | |||||
bReturn = NativeMethods.InternetSetOption( | |||||
IntPtr.Zero, | IntPtr.Zero, | ||||
INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED, | |||||
INTERNET_OPTION.INTERNET_OPTION_PROXY_SETTINGS_CHANGED, | |||||
IntPtr.Zero, 0); | IntPtr.Zero, 0); | ||||
if (!bReturn) | |||||
{ | |||||
Logging.Error("InternetSetOption:INTERNET_OPTION_PROXY_SETTINGS_CHANGED"); | |||||
} | |||||
NativeMethods.InternetSetOption( | |||||
bReturn = NativeMethods.InternetSetOption( | |||||
IntPtr.Zero, | IntPtr.Zero, | ||||
INTERNET_OPTION.INTERNET_OPTION_REFRESH, | INTERNET_OPTION.INTERNET_OPTION_REFRESH, | ||||
IntPtr.Zero, 0); | IntPtr.Zero, 0); | ||||
if (!bReturn) | |||||
{ | |||||
Logging.Error("InternetSetOption:INTERNET_OPTION_REFRESH"); | |||||
} | |||||
} | |||||
public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL) | |||||
{ | |||||
string[] allConnections = null; | |||||
var ret = RAS.GetAllConns(ref allConnections); | |||||
if (ret == 2) | |||||
throw new Exception("Cannot get all connections"); | |||||
if (ret == 1) | |||||
{ | |||||
// no entries, only set LAN | |||||
SetIEProxy(enable, global, proxyServer, pacURL, null); | |||||
} | |||||
else if (ret == 0) | |||||
{ | |||||
// found entries, set LAN and each connection | |||||
SetIEProxy(enable, global, proxyServer, pacURL, null); | |||||
foreach (string connName in allConnections) | |||||
{ | |||||
SetIEProxy(enable, global, proxyServer, pacURL, connName); | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -190,6 +190,7 @@ | |||||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> | <Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> | ||||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION_LIST.cs" /> | <Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION_LIST.cs" /> | ||||
<Compile Include="Util\SystemProxy\NativeMethods.cs" /> | <Compile Include="Util\SystemProxy\NativeMethods.cs" /> | ||||
<Compile Include="Util\SystemProxy\RAS.cs" /> | |||||
<Compile Include="Util\SystemProxy\WinINet.cs" /> | <Compile Include="Util\SystemProxy\WinINet.cs" /> | ||||
<Compile Include="Util\Util.cs" /> | <Compile Include="Util\Util.cs" /> | ||||
<Compile Include="View\ConfigForm.cs"> | <Compile Include="View\ConfigForm.cs"> | ||||