diff --git a/v2rayN/v2rayN/Handler/ProxySetting.cs b/v2rayN/v2rayN/Handler/ProxySetting.cs
index 67541e15..fe0ea689 100644
--- a/v2rayN/v2rayN/Handler/ProxySetting.cs
+++ b/v2rayN/v2rayN/Handler/ProxySetting.cs
@@ -1,37 +1,68 @@
using Microsoft.Win32;
using System.Runtime.InteropServices;
+using static v2rayN.Handler.ProxySetting.InternetConnectionOption;
namespace v2rayN.Handler
{
internal class ProxySetting
{
+ ///
+ // set to use no proxy
+ ///
+ /// Error message with win32 error code
public static bool UnsetProxy()
{
return SetProxy(null, null, 1);
}
+ ///
+ /// Set system proxy settings
+ ///
+ /// proxy address
+ /// exception addresses that do not use proxy
+ /// type of proxy defined in PerConnFlags
+ /// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
+ /// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
+ /// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
+ /// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
+ ///
+ /// Error message with win32 error code
+ /// true: one of connnection is successfully updated proxy settings
public static bool SetProxy(string? strProxy, string? exceptions, int type)
+ {
+ // set proxy for LAN
+ bool result = SetConnectionProxy(null, strProxy, exceptions, type);
+ // set proxy for dial up connections
+ var connections = EnumerateRasEntries();
+ foreach (var connection in connections)
+ {
+ result |= SetConnectionProxy(connection, strProxy, exceptions, type);
+ }
+ return result;
+ }
+
+ private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{
InternetPerConnOptionList list = new();
int optionCount = 1;
- if (type == 1)
+ if (type == 1) // No proxy
{
optionCount = 1;
}
- else if (type is 2 or 4)
+ else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
- if (type == 2)
+ if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
}
- else if (type == 4)
+ else if (type == 4) // autoproxy script url
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
@@ -47,24 +78,31 @@ namespace v2rayN.Handler
if (optionCount > 1)
{
options[1].m_Option = m_Option;
- options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
+ options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy); // !! remember to deallocate memory 1
// except for these addresses ...
if (optionCount > 2)
{
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
- options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
+ options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); // !! remember to deallocate memory 2
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
- list.szConnection = IntPtr.Zero;
+ if (connectionName != null)
+ {
+ list.szConnection = Marshal.StringToHGlobalAuto(connectionName); // !! remember to deallocate memory 3
+ }
+ else
+ {
+ list.szConnection = IntPtr.Zero;
+ }
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
- IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
+ IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
{
@@ -83,26 +121,82 @@ namespace v2rayN.Handler
list.options = optionsPtr;
// and then make a pointer out of the whole list
- IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize);
+ IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
- int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero,
+ bool isSuccess = NativeMethods.InternetSetOption(IntPtr.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
- ipcoListPtr, list.dwSize) ? -1 : 0;
- if (returnvalue == 0)
+ ipcoListPtr, list.dwSize);
+ int returnvalue = 0; // ERROR_SUCCESS
+ if (!isSuccess)
{ // get the error codes, they might be helpful
- returnvalue = Marshal.GetLastWin32Error();
+ returnvalue = Marshal.GetLastPInvokeError();
}
- // FREE the data ASAP
- Marshal.FreeCoTaskMem(optionsPtr);
- Marshal.FreeCoTaskMem(ipcoListPtr);
- if (returnvalue > 0)
- { // throw the error codes, they might be helpful
- //throw new Win32Exception(Marshal.GetLastWin32Error());
+ else
+ {
+ // Notify the system that the registry settings have been changed and cause them to be refreshed
+ NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
+ NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
- return (returnvalue < 0);
+ // FREE the data ASAP
+ if(list.szConnection != IntPtr.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
+ if (optionCount > 1)
+ {
+ Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
+ if (optionCount > 2)
+ {
+ Marshal.FreeHGlobal(options[2].m_Value.m_StringPtr); // release mem 2
+ }
+ }
+ Marshal.FreeCoTaskMem(optionsPtr); // release mem 4
+ Marshal.FreeCoTaskMem(ipcoListPtr); // release mem 5
+ if (returnvalue != 0)
+ {
+ // throw the error codes, they might be helpful
+ throw new ApplicationException($"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}" );
+ }
+
+ return true;
+ }
+
+ ///
+ /// Retrieve list of connections including LAN and WAN to support PPPoE connection
+ ///
+ /// A list of RAS connection names. May be empty list if no dial up connection.
+ /// Error message with win32 error code
+ private static IEnumerable EnumerateRasEntries()
+ {
+ int entries = 0;
+ // attempt to query with 1 entry buffer
+ RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
+ int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
+ rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
+
+ uint result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
+ // increase buffer if the buffer is not large enough
+ if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
+ {
+ rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
+ for (int i = 0; i < rasEntryNames.Length; i++)
+ {
+ rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
+ }
+
+ result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
+ }
+ if (result == 0)
+ {
+ var entryNames = new List();
+ for (int i = 0; i < entries; i++)
+ {
+ entryNames.Add(rasEntryNames[i].szEntryName);
+ }
+
+ return entryNames;
+ }
+ throw new ApplicationException($"RasEnumEntries failed with error code: {result}");
}
#region WinInet structures
@@ -145,6 +239,25 @@ namespace v2rayN.Handler
[FieldOffset(0)]
public IntPtr m_StringPtr;
}
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct RASENTRYNAME
+ {
+ public int dwSize;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
+ public string szEntryName;
+
+ public int dwFlags;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
+ public string szPhonebookPath;
+ }
+
+ // Constants
+ public const int RAS_MaxEntryName = 256;
+ public const int MAX_PATH = 260; // Standard MAX_PATH value in Windows
+
}
#endregion WinInet structures
@@ -156,7 +269,9 @@ namespace v2rayN.Handler
//
public enum InternetOption : uint
{
- INTERNET_OPTION_PER_CONNECTION_OPTION = 75
+ INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
+ INTERNET_OPTION_REFRESH = 37,
+ INTERNET_OPTION_SETTINGS_CHANGED = 39
}
//
@@ -182,6 +297,12 @@ namespace v2rayN.Handler
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
+ public enum ErrorCode : uint
+ {
+ ERROR_BUFFER_TOO_SMALL = 603,
+ ERROR_INVALID_SIZE = 632
+ }
+
#endregion WinInet enums
internal static class NativeMethods
@@ -189,6 +310,14 @@ namespace v2rayN.Handler
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
+ [DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
+ public static extern uint RasEnumEntries(
+ string? reserved, // Reserved, must be null
+ string? lpszPhonebook, // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
+ [In, Out] RASENTRYNAME[]? lprasentryname, // Buffer to receive RAS entry names
+ ref int lpcb, // Size of the buffer
+ ref int lpcEntries // Number of entries written to the buffer
+ );
}
//判断是否使用代理
diff --git a/v2rayN/v2rayN/Handler/SysProxyHandle.cs b/v2rayN/v2rayN/Handler/SysProxyHandle.cs
index 44a25aec..9bce9058 100644
--- a/v2rayN/v2rayN/Handler/SysProxyHandle.cs
+++ b/v2rayN/v2rayN/Handler/SysProxyHandle.cs
@@ -20,7 +20,6 @@ namespace v2rayN.Handler
//
//
//
- private static SysproxyConfig? _userSettings = null;
private enum RET_ERRORS : int
{
@@ -34,15 +33,7 @@ namespace v2rayN.Handler
static SysProxyHandle()
{
- try
- {
- FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
- Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
- }
- catch (IOException ex)
- {
- Utils.SaveLog(ex.Message, ex);
- }
+
}
public static bool UpdateSysProxy(Config config, bool forceDisable)
@@ -79,13 +70,11 @@ namespace v2rayN.Handler
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString());
}
- ProxySetting.SetProxy(strProxy, strExceptions, 2);
- SetIEProxy(true, strProxy, strExceptions);
+ ProxySetting.SetProxy(strProxy, strExceptions, 2); // set a named proxy
}
else if (type == ESysProxyType.ForcedClear)
{
- ProxySetting.UnsetProxy();
- ResetIEProxy();
+ ProxySetting.UnsetProxy(); // set to no proxy
}
else if (type == ESysProxyType.Unchanged)
{
@@ -94,8 +83,7 @@ namespace v2rayN.Handler
{
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.httpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
- ProxySetting.SetProxy(strProxy, "", 4);
- SetIEProxy(false, strProxy, "");
+ ProxySetting.SetProxy(strProxy, "", 4); // use pac script url for auto-config proxy
}
if (type != ESysProxyType.Pac)
@@ -121,102 +109,5 @@ namespace v2rayN.Handler
{
}
}
-
- public static void SetIEProxy(bool global, string strProxy, string strExceptions)
- {
- string arguments = global
- ? $"global {strProxy} {strExceptions}"
- : $"pac {strProxy}";
-
- ExecSysproxy(arguments);
- }
-
- // set system proxy to 1 (null) (null) (null)
- public static void ResetIEProxy()
- {
- ExecSysproxy("set 1 - - -");
- }
-
- 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(false);
- using AutoResetEvent errorWaitHandle = new(false);
- using Process process = new();
-
- // 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(1024);
- StringBuilder error = new(1024);
-
- 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 Exception(process.StartInfo.Arguments);
- }
- string stderr = error.ToString();
- string stdout = output.ToString();
-
- int exitCode = process.ExitCode;
- if (exitCode != (int)RET_ERRORS.RET_NO_ERROR)
- {
- throw new Exception(stderr);
- }
-
- //if (arguments == "query")
- //{
- // if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty())
- // {
- // throw new Exception("failed to query wininet settings");
- // }
- // _queryStr = stdout;
- //}
- }
}
}
\ No newline at end of file
diff --git a/v2rayN/v2rayN/Properties/Resources.Designer.cs b/v2rayN/v2rayN/Properties/Resources.Designer.cs
index f422c042..24b5f9dd 100644
--- a/v2rayN/v2rayN/Properties/Resources.Designer.cs
+++ b/v2rayN/v2rayN/Properties/Resources.Designer.cs
@@ -219,25 +219,5 @@ namespace v2rayN.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
-
- ///
- /// 查找 System.Byte[] 类型的本地化资源。
- ///
- internal static byte[] sysproxy_exe {
- get {
- object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture);
- return ((byte[])(obj));
- }
- }
-
- ///
- /// 查找 System.Byte[] 类型的本地化资源。
- ///
- internal static byte[] sysproxy64_exe {
- get {
- object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture);
- return ((byte[])(obj));
- }
- }
}
}
diff --git a/v2rayN/v2rayN/Properties/Resources.resx b/v2rayN/v2rayN/Properties/Resources.resx
index bebcc3df..60899900 100644
--- a/v2rayN/v2rayN/Properties/Resources.resx
+++ b/v2rayN/v2rayN/Properties/Resources.resx
@@ -118,12 +118,6 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- ..\resources\sysproxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- ..\resources\sysproxy64.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
..\Resources\NotifyIcon1.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/v2rayN/v2rayN/Resources/sysproxy.exe.gz b/v2rayN/v2rayN/Resources/sysproxy.exe.gz
deleted file mode 100644
index 980d304b..00000000
Binary files a/v2rayN/v2rayN/Resources/sysproxy.exe.gz and /dev/null differ
diff --git a/v2rayN/v2rayN/Resources/sysproxy64.exe.gz b/v2rayN/v2rayN/Resources/sysproxy64.exe.gz
deleted file mode 100644
index c5ff36af..00000000
Binary files a/v2rayN/v2rayN/Resources/sysproxy64.exe.gz and /dev/null differ
diff --git a/v2rayN/v2rayUpgrade/Program.cs b/v2rayN/v2rayUpgrade/Program.cs
index ed454bd0..bc4303de 100644
--- a/v2rayN/v2rayUpgrade/Program.cs
+++ b/v2rayN/v2rayUpgrade/Program.cs
@@ -13,6 +13,7 @@ namespace v2rayUpgrade
{
Application.EnableVisualStyles();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
+ Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(args));
}
diff --git a/v2rayN/v2rayUpgrade/app.manifest b/v2rayN/v2rayUpgrade/app.manifest
index c7239fd8..3081161b 100644
--- a/v2rayN/v2rayUpgrade/app.manifest
+++ b/v2rayN/v2rayUpgrade/app.manifest
@@ -2,8 +2,6 @@
- true
- PerMonitorV2
\ No newline at end of file