Compare commits

..

6 Commits

Author SHA1 Message Date
2dust
da81101cfd up 7.22.7 2026-06-12 19:15:08 +08:00
DHR60
3d4f202cd7 Metrics listen directly (#9533) 2026-06-12 09:47:59 +08:00
2dust
35fbb137e8 Add vcn and pcs properties to VmessQRCode DTO 2026-06-12 09:47:03 +08:00
sparklelcm333
1869a95700 fix: Desktop(Avalonia) 移除子对话框最小化按钮,仅禁 CanMinimize (#9526) 2026-06-10 20:45:23 +08:00
JieXu
34359885ab Update AppBuilderExtension.cs (#9514)
* Update AppBuilderExtension.cs

* Update MainWindow.axaml.cs

* Update MacAppUtils.cs

* Update App.axaml.cs

* Add regions for MacOS activation and app events

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2026-06-09 17:00:51 +08:00
znah
93832656dd fix: disable .net9 CET (#9507) 2026-06-09 14:06:13 +08:00
35 changed files with 102 additions and 706 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "v2rayN/GlobalHotKeys"]
path = v2rayN/GlobalHotKeys
url = https://github.com/2dust/GlobalHotKeys
[submodule "v2rayN/NetBridge"]
path = v2rayN/NetBridge
url = https://github.com/2dust/NetBridge

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.22.6</Version>
<Version>7.22.7</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -161,10 +161,6 @@ public static class ConfigHandler
config.SystemProxyItem ??= new();
config.WebDavItem ??= new();
config.CheckUpdateItem ??= new();
config.NetBridgeItem ??= new()
{
RuleProcess = string.Empty
};
config.Fragment4RayItem ??= new()
{
Packets = "tlshello",

View File

@@ -66,7 +66,9 @@ public class VmessFmt : BaseFmt
sni = item.Sni,
alpn = item.Alpn,
fp = item.Fingerprint,
insecure = item.GetAllowInsecure() ? "1" : "0"
insecure = item.GetAllowInsecure() ? "1" : "0",
vcn = item.VerifyPeerCertByName,
pcs = item.CertSha
};
var url = JsonUtils.Serialize(vmessQRCode);
@@ -141,6 +143,8 @@ public class VmessFmt : BaseFmt
item.Alpn = Utils.ToString(vmessQRCode.alpn);
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
item.AllowInsecure = vmessQRCode.insecure == "1" ? Global.StringTrue : string.Empty;
item.VerifyPeerCertByName = Utils.ToString(vmessQRCode.vcn);
item.CertSha = Utils.ToString(vmessQRCode.pcs);
return item;
}

View File

@@ -1,222 +0,0 @@
using NetBridgeLib.Services;
namespace ServiceLib.Manager;
public sealed class NetBridgeManager
{
private static readonly Lazy<NetBridgeManager> _instance = new(() => new());
public static NetBridgeManager Instance => _instance.Value;
private readonly Config _config = AppManager.Instance.Config;
private NetBridgeService? _netBridgeService;
private bool _isProxyRunning;
private bool _isInitialized;
private List<NetBridgeRuleConfig> _ruleConfigs = [];
private Func<bool, string, Task>? _updateFunc;
private uint _proxyConfigId;
public async Task Init(Func<bool, string, Task>? updateFunc = null)
{
if (_isInitialized)
{
return;
}
_updateFunc = updateFunc;
try
{
_netBridgeService = new NetBridgeService();
_netBridgeService.LogReceived += msg =>
{
var message = $"NetBridge Log: {msg}";
_ = _updateFunc?.Invoke(false, message);
};
_netBridgeService.ConnectionReceived += (processName, pid, destIp, destPort, proxyInfo) =>
{
var message = $"NetBridge Connection: {processName} (PID: {pid}) -> {destIp}:{destPort} -> {proxyInfo}";
_ = _updateFunc?.Invoke(false, message);
};
_ruleConfigs = BuildRuleConfigs(_config.NetBridgeItem?.RuleProcess);
_isInitialized = true;
}
catch (Exception ex)
{
var error = $"Failed to initialize NetBridgeService: {ex.Message}";
await _updateFunc?.Invoke(true, error);
}
}
public async Task<bool> Start()
{
if (_isProxyRunning)
{
return true;
}
try
{
if (_netBridgeService == null)
{
return false;
}
var started = _netBridgeService.Start();
if (!started)
{
return false;
}
_isProxyRunning = true;
}
catch (Exception ex)
{
var error = $"Failed to start NetBridgeService: {ex.Message}";
await _updateFunc?.Invoke(true, error);
return false;
}
return true;
}
public async Task<bool> Stop()
{
if (!_isProxyRunning)
{
return true;
}
try
{
if (_netBridgeService == null)
{
return false;
}
var stopped = _netBridgeService.Stop();
if (!stopped)
{
return false;
}
_isProxyRunning = false;
}
catch (Exception ex)
{
var error = $"Failed to stop NetBridgeService: {ex.Message}";
await _updateFunc?.Invoke(true, error);
return false;
}
return true;
}
public async Task<bool> UpdateRoutes(string? ruleProcess)
{
var newRuleConfigs = BuildRuleConfigs(ruleProcess);
_ruleConfigs = newRuleConfigs;
if (!_isProxyRunning)
{
return true;
}
return await ApplyRoutesInternal();
}
public async Task<bool> UpdateProxyConfig(string proxyHost, int proxyPort)
{
try
{
if (_netBridgeService == null)
{
return false;
}
var proxyType = "SOCKS5";
var username = "";
var password = "";
if (_proxyConfigId > 0)
{
var edited = _netBridgeService.EditProxyConfig(_proxyConfigId, proxyType, proxyHost, (ushort)proxyPort, username, password);
if (!edited)
{
return false;
}
}
else
{
_proxyConfigId = _netBridgeService.AddProxyConfig(proxyType, proxyHost, (ushort)proxyPort, username, password);
if (_proxyConfigId == 0)
{
return false;
}
}
return await Task.FromResult(true);
}
catch (Exception ex)
{
var error = $"Failed to update proxy config: {ex.Message}";
await _updateFunc?.Invoke(true, error);
return false;
}
}
private async Task<bool> ApplyRoutesInternal()
{
if (_netBridgeService == null)
{
return false;
}
List<NetBridgeRuleConfig> rules;
rules = _ruleConfigs.Select(JsonUtils.DeepCopy).ToList();
foreach (var rule in rules.Where(x => x.RuleId > 0))
{
try
{
_ = _netBridgeService.DeleteRule(rule.RuleId);
}
catch
{
// ignored
}
}
for (var i = 0; i < rules.Count; i++)
{
var rule = rules[i];
var newRuleId = _netBridgeService.AddRule(rule.ProcessName, rule.TargetHosts, rule.TargetPorts, rule.Protocol, rule.Action, rule.ProxyConfigId);
if (newRuleId == 0)
{
return false;
}
rules[i].RuleId = newRuleId;
}
_ruleConfigs = rules;
return await Task.FromResult(true);
}
private static List<NetBridgeRuleConfig> BuildRuleConfigs(string? ruleProcess)
{
var processNames = Utils.String2List(Utils.Convert2Comma(ruleProcess));
return processNames.Select(processName => new NetBridgeRuleConfig
{
ProcessName = processName,
TargetHosts = "*",
TargetPorts = "*",
Protocol = "BOTH",
Action = "PROXY",
ProxyConfigId = 0
}).ToList();
}
}

View File

@@ -29,7 +29,6 @@ public class Config
public SystemProxyItem SystemProxyItem { get; set; }
public WebDavItem WebDavItem { get; set; }
public CheckUpdateItem CheckUpdateItem { get; set; }
public NetBridgeItem NetBridgeItem { get; set; }
public Fragment4RayItem? Fragment4RayItem { get; set; }
public List<InItem> Inbound { get; set; }
public List<KeyEventItem> GlobalHotkeys { get; set; }

View File

@@ -277,9 +277,3 @@ public class SimpleDNSItem
public string? Hosts { get; set; }
public string? DirectExpectedIPs { get; set; }
}
[Serializable]
public class NetBridgeItem
{
public string? RuleProcess { get; set; }
}

View File

@@ -20,7 +20,7 @@ public class Stats4Ray
public class Metrics4Ray
{
public string tag { get; set; }
public string listen { get; set; }
}
public class Policy4Ray

View File

@@ -1,12 +0,0 @@
namespace ServiceLib.Models.Dto;
public sealed class NetBridgeRuleConfig
{
public uint RuleId { get; set; }
public string ProcessName { get; set; }
public string TargetHosts { get; set; }
public string TargetPorts { get; set; }
public string Protocol { get; set; }
public string Action { get; set; }
public uint ProxyConfigId { get; set; }
}

View File

@@ -40,4 +40,8 @@ public class VmessQRCode
public string fp { get; set; } = string.Empty;
public string insecure { get; set; } = string.Empty;
public string vcn { get; set; } = string.Empty;
public string pcs { get; set; } = string.Empty;
}

View File

@@ -1320,15 +1320,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Process traffic hijacking (experimental) 的本地化字符串。
/// </summary>
public static string menuNetBridge {
get {
return ResourceManager.GetString("menuNetBridge", resourceCulture);
}
}
/// <summary>
/// 查找类似 New Update 的本地化字符串。
/// </summary>
@@ -2310,15 +2301,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 The process names that need to be proxied; separate multiple processes with commas. Processes not in the list will also be hijacked, but will connect directly. 的本地化字符串。
/// </summary>
public static string NetBridgeRuleTips {
get {
return ResourceManager.GetString("NetBridgeRuleTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Non-VMess or SS protocol 的本地化字符串。
/// </summary>
@@ -3069,15 +3051,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Enables process traffic hijacking (experimental), similar to Proxifer; can coexist with system proxy; conflicts with TUN mode, please do not enable them simultaneously; 的本地化字符串。
/// </summary>
public static string TbEnableNetBridge {
get {
return ResourceManager.GetString("TbEnableNetBridge", resourceCulture);
}
}
/// <summary>
/// 查找类似 Enable Tun 的本地化字符串。
/// </summary>

View File

@@ -1770,13 +1770,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
<value>Split the tail of packets into smaller fragments. This may affect throughput and latency.</value>
</data>
<data name="TbEnableNetBridge" xml:space="preserve">
<value>Enables process traffic hijacking (experimental), similar to Proxifer; can coexist with system proxy; conflicts with TUN mode, please do not enable them simultaneously;</value>
</data>
<data name="NetBridgeRuleTips" xml:space="preserve">
<value>The process names that need to be proxied; separate multiple processes with commas. Processes not in the list will also be hijacked, but will connect directly.</value>
</data>
<data name="menuNetBridge" xml:space="preserve">
<value>Process traffic hijacking (experimental)</value>
</data>
</root>
</root>

View File

@@ -1767,13 +1767,4 @@
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
<value>将数据包末端拆分为更小片段发送,可能影响吞吐与延迟。</value>
</data>
<data name="TbEnableNetBridge" xml:space="preserve">
<value>启用进程流量劫持(实验性),类似 Proxifer 功能;可以和系统代理并存;和 TUN 模式冲突,请不要同时开启;</value>
</data>
<data name="NetBridgeRuleTips" xml:space="preserve">
<value>需要代理的进程名,多个进程请用逗号分隔;不在列表中的进程也会被劫持,但是会直连</value>
</data>
<data name="menuNetBridge" xml:space="preserve">
<value>进程流量劫持(实验性)</value>
</data>
</root>

View File

@@ -1767,13 +1767,4 @@
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
<value>將封包末端拆分為更小的片段進行傳送,可能會影響吞吐量與延遲</value>
</data>
<data name="TbEnableNetBridge" xml:space="preserve">
<value>啟用進程流量劫持(實驗性),類似 Proxifer 功能;可以和系統代理並存;和 TUN 模式衝突,請不要同時開啟;</value>
</data>
<data name="NetBridgeRuleTips" xml:space="preserve">
<value>需要代理程式的進程名,多個進程請用逗號分隔;不在清單中的進程也會被劫持,但是會直連</value>
</data>
<data name="menuNetBridge" xml:space="preserve">
<value>進程流量劫持(實驗性)</value>
</data>
</root>
</root>

View File

@@ -17,14 +17,6 @@
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"inboundTag": [
"api"
],
"outboundTag": "api",
"type": "field"
}
]
"rules": []
}
}

View File

@@ -87,7 +87,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NetBridge\src\NetBridgeLib\NetBridgeLib.csproj" />
<ProjectReference Include="..\ServiceLib.UdpTest\ServiceLib.UdpTest.csproj" />
</ItemGroup>

View File

@@ -6,45 +6,19 @@ public partial class CoreConfigV2rayService
{
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
const string tag = nameof(EInboundProtocol.api);
Metrics4Ray apiObj = new();
Metrics4Ray metricsObj = new();
Policy4Ray policyObj = new();
SystemPolicy4Ray policySystemSetting = new();
_coreConfig.stats = new Stats4Ray();
apiObj.tag = tag;
_coreConfig.metrics = apiObj;
metricsObj.listen = $"{Global.Loopback}:{AppManager.Instance.StatePort}";
_coreConfig.metrics = metricsObj;
policySystemSetting.statsOutboundDownlink = true;
policySystemSetting.statsOutboundUplink = true;
policyObj.system = policySystemSetting;
_coreConfig.policy = policyObj;
if (!_coreConfig.inbounds.Exists(item => item.tag == tag))
{
Inbounds4Ray apiInbound = new();
Inboundsettings4Ray apiInboundSettings = new();
apiInbound.tag = tag;
apiInbound.listen = Global.Loopback;
apiInbound.port = AppManager.Instance.StatePort;
apiInbound.protocol = Global.InboundAPIProtocol;
apiInboundSettings.address = Global.Loopback;
apiInbound.settings = apiInboundSettings;
_coreConfig.inbounds.Add(apiInbound);
}
if (!_coreConfig.routing.rules.Exists(item => item.outboundTag == tag))
{
RulesItem4Ray apiRoutingRule = new()
{
inboundTag = new List<string> { tag },
outboundTag = tag,
type = "field"
};
_coreConfig.routing.rules.Add(apiRoutingRule);
}
}
}
}

View File

@@ -1,100 +0,0 @@
namespace ServiceLib.ViewModels;
public class NetBridgeViewModel : MyReactiveObject
{
[Reactive]
public bool EnableNetBridge { get; set; }
[Reactive]
public string RuleProcess { get; set; }
public ReactiveCommand<Unit, Unit> SaveRulesCmd { get; }
public NetBridgeViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppManager.Instance.Config;
_updateView = updateView;
SaveRulesCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SaveRulesAsync();
});
this.WhenAnyValue(x => x.EnableNetBridge)
.Skip(1)
.Subscribe(async enabled =>
{
await ToggleNetBridgeAsync(enabled);
});
_ = Init();
}
private async Task Init()
{
_config.NetBridgeItem ??= new()
{
RuleProcess = string.Empty
};
EnableNetBridge = false;
RuleProcess = _config.NetBridgeItem.RuleProcess;
if (RuleProcess.IsNullOrEmpty())
{
RuleProcess = "Chrome.exe";
}
await Task.CompletedTask;
}
private async Task ToggleNetBridgeAsync(bool enabled)
{
await NetBridgeManager.Instance.Init(UpdateViewHandler);
if (enabled)
{
var succeed = await NetBridgeManager.Instance.Start();
if (succeed)
{
await NetBridgeManager.Instance.UpdateProxyConfig(Global.Loopback, AppManager.Instance.GetLocalPort(EInboundProtocol.socks));
await NetBridgeManager.Instance.UpdateRoutes(RuleProcess);
}
NoticeManager.Instance.Enqueue(succeed ? ResUI.OperationSuccess : ResUI.OperationFailed);
}
else
{
var succeed = await NetBridgeManager.Instance.Stop();
NoticeManager.Instance.Enqueue(succeed ? ResUI.OperationSuccess : ResUI.OperationFailed);
}
}
/// </summary>
private async Task SaveRulesAsync()
{
_config.NetBridgeItem ??= new();
var normalizedRuleProcess = RuleProcess;
_config.NetBridgeItem.RuleProcess = normalizedRuleProcess;
RuleProcess = normalizedRuleProcess;
if (await ConfigHandler.SaveConfig(_config) != 0)
{
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
return;
}
await NetBridgeManager.Instance.Init(UpdateViewHandler);
if (EnableNetBridge)
{
var routesUpdated = await NetBridgeManager.Instance.UpdateRoutes(normalizedRuleProcess);
NoticeManager.Instance.Enqueue(routesUpdated ? ResUI.OperationSuccess : ResUI.OperationFailed);
}
}
private async Task<bool> UpdateViewHandler(bool isError, string msg)
{
NoticeManager.Instance.SendMessageEx(msg);
return await Task.FromResult(true);
}
}

View File

@@ -23,8 +23,8 @@ public partial class App : Application
DataContext = StatusBarViewModel.Instance;
}
desktop.Exit += OnExit;
desktop.MainWindow = new MainWindow();
var mainWindow = new MainWindow();
desktop.MainWindow = mainWindow;
if (OperatingSystem.IsMacOS())
{
@@ -35,6 +35,8 @@ public partial class App : Application
base.OnFrameworkInitializationCompleted();
}
#region MacOS Activation
private void OnMacOSActivated(object? sender, ActivatedEventArgs args)
{
if (args.Kind != ActivationKind.Reopen)
@@ -42,17 +44,69 @@ public partial class App : Application
return;
}
if ((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is not MainWindow mainWindow)
{
return;
}
var isMiniaturized = MacAppUtils.IsWindowMiniaturized(mainWindow);
Dispatcher.UIThread.Post(() =>
{
if (isMiniaturized)
{
RestoreMacOSAccessoryPolicyAfterMiniaturize(mainWindow);
mainWindow.ShowHideWindow(true);
return;
}
if (!AppManager.Instance.Config.UiItem.MacOSShowInDock)
{
MacAppUtils.SetActivationPolicyAccessory();
}
((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow as MainWindow)?.ShowHideWindow(true);
mainWindow.ShowHideWindow(true);
});
}
private static void RestoreMacOSAccessoryPolicyAfterMiniaturize(MainWindow mainWindow)
{
if (AppManager.Instance.Config.UiItem.MacOSShowInDock)
{
return;
}
mainWindow
.GetObservable(Window.WindowStateProperty)
.Skip(1)
.Where(state => state != WindowState.Minimized)
.Take(1)
.ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => QueueMacOSAccessoryPolicyRestore(mainWindow));
}
private static void QueueMacOSAccessoryPolicyRestore(MainWindow mainWindow)
{
// AppKit may keep isMiniaturized set until the Dock restore animation finishes.
DispatcherTimer.RunOnce(() => RestoreMacOSAccessoryPolicy(mainWindow), TimeSpan.FromMilliseconds(300));
}
private static void RestoreMacOSAccessoryPolicy(MainWindow mainWindow)
{
if (AppManager.Instance.Config.UiItem.MacOSShowInDock || MacAppUtils.IsWindowMiniaturized(mainWindow))
{
return;
}
MacAppUtils.SetActivationPolicyAccessory();
mainWindow.Activate();
mainWindow.Focus();
}
#endregion MacOS Activation
#region App Event
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject != null)
@@ -66,10 +120,6 @@ public partial class App : Application
Logging.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
}
private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
{
}
private async void MenuAddServerViaClipboardClick(object? sender, EventArgs e)
{
try
@@ -91,4 +141,6 @@ public partial class App : Application
await AppManager.Instance.AppExitAsync(false);
AppManager.Instance.Shutdown(true);
}
#endregion App Event
}

View File

@@ -6,6 +6,13 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
{
Initialized += OnWindowInitialized;
Loaded += OnLoaded;
Loaded += (s, e) =>
{
if (Owner != null && !ShowInTaskbar)
{
CanMinimize = false;
}
};
}
private void ReactiveWindowBase_Closed(object? sender, EventArgs e)

View File

@@ -18,18 +18,18 @@ public static class AppBuilderExtension
if (OperatingSystem.IsWindows())
{
AddFontFallback(fallbacks, "Segoe UI Symbol");
AddFontFallback(fallbacks, "Segoe UI Emoji");
AddFontFallback(fallbacks, "Segoe UI Symbol");
}
else if (OperatingSystem.IsMacOS())
{
AddFontFallback(fallbacks, "Apple Symbols");
AddFontFallback(fallbacks, "Apple Color Emoji");
AddFontFallback(fallbacks, "Apple Symbols");
}
else if (OperatingSystem.IsLinux())
{
AddFontFallback(fallbacks, "Noto Sans Symbols");
AddFontFallback(fallbacks, "Noto Color Emoji");
AddFontFallback(fallbacks, "Noto Sans Symbols");
}
return appBuilder.With(new FontManagerOptions

View File

@@ -13,6 +13,10 @@ internal static class MacAppUtils
sel_registerName("setActivationPolicy:"),
ActivationPolicyAccessory);
public static bool IsWindowMiniaturized(Window window)
=> window.TryGetPlatformHandle() is IMacOSTopLevelPlatformHandle { NSWindow: not 0 } handle
&& objc_msgSend_bool(handle.NSWindow, sel_registerName("isMiniaturized"));
[DllImport(LibObjC)]
private static extern nint objc_getClass(string name);
@@ -22,6 +26,10 @@ internal static class MacAppUtils
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
private static extern nint objc_msgSend(nint receiver, nint selector);
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool objc_msgSend_bool(nint receiver, nint selector);
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
private static extern void objc_msgSend(nint receiver, nint selector, nint argument);
}

View File

@@ -89,7 +89,6 @@
</MenuItem>
<MenuItem x:Name="menuBackupAndRestore" Header="{x:Static resx:ResUI.menuBackupAndRestore}" />
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
<MenuItem x:Name="menuNetBridge" Header="{x:Static resx:ResUI.menuNetBridge}" />
</MenuItem>
<MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">

View File

@@ -12,7 +12,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
private readonly WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView;
private NetBridgeView? _netBridgeView;
private bool _blCloseByUser = false;
public MainWindow()
@@ -29,7 +28,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
btnNewUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
menuClose.Click += MenuClose_Click;
menuNetBridge.Click += MenuNetBridge_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler);
@@ -167,7 +165,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
{
Title = $"{Utils.GetVersion()}";
menuAddServerViaScan.IsVisible = false;
menuNetBridge.IsVisible = false;
}
if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
@@ -382,19 +379,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
DialogHost.Show(_backupAndRestoreView);
}
private void MenuNetBridge_Click(object? sender, RoutedEventArgs e)
{
if (Utils.IsAdministrator())
{
_netBridgeView ??= new NetBridgeView();
DialogHost.Show(_netBridgeView);
}
else
{
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.RunAsAdmin);
}
}
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
{
if (await UI.ShowYesNo(this, ResUI.menuExitTips) != ButtonResult.Yes)
@@ -429,7 +413,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
public void ShowHideWindow(bool? blShow)
{
var bl = blShow ??
(Utils.IsLinux()
(Utils.IsLinux() || Utils.IsMacOS()
? (!AppManager.Instance.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
: !AppManager.Instance.ShowInTaskbar);
if (bl)

View File

@@ -18,6 +18,8 @@ public partial class MessageBoxDialog : Window
btnYes.Click += BtnYes_Click;
btnNo.Click += BtnNo_Click;
CanMinimize = false;
}
private void BtnYes_Click(object? sender, RoutedEventArgs e)

View File

@@ -1,75 +0,0 @@
<UserControl
x:Class="v2rayN.Desktop.Views.NetBridgeView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
d:DesignHeight="400"
d:DesignWidth="600"
x:DataType="vms:NetBridgeViewModel"
mc:Ignorable="d">
<Border
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Theme="{DynamicResource CardBorder}">
<DockPanel Margin="{StaticResource Margin8}">
<StackPanel
Margin="{StaticResource Margin8}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<ToggleSwitch
x:Name="togEnableNetBridge"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Width="500"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbEnableNetBridge}"
TextWrapping="Wrap" />
</StackPanel>
<DockPanel>
<StackPanel
Margin="{StaticResource Margin8}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<TextBlock
Width="500"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.NetBridgeRuleTips}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
Margin="{StaticResource Margin8}"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
x:Name="btnSave"
Width="100"
Content="{x:Static resx:ResUI.TbConfirm}"
IsDefault="True" />
</StackPanel>
<TextBox
x:Name="txtRuleProcess"
Margin="{StaticResource Margin8}"
VerticalAlignment="Stretch"
AcceptsReturn="True"
BorderThickness="1"
Classes="TextArea"
TextWrapping="Wrap" />
</DockPanel>
</DockPanel>
</Border>
</UserControl>

View File

@@ -1,25 +0,0 @@
namespace v2rayN.Desktop.Views;
public partial class NetBridgeView : ReactiveUserControl<NetBridgeViewModel>
{
public NetBridgeView()
{
InitializeComponent();
ViewModel = new NetBridgeViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, vm => vm.SaveRulesCmd, v => v.btnSave).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableNetBridge, v => v.togEnableNetBridge.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleProcess, v => v.txtRuleProcess.Text).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
return await Task.FromResult(true);
}
}

View File

@@ -7,6 +7,7 @@
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<AssemblyName>v2rayN</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CETCompat>false</CETCompat>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,6 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.5.11709.299
VisualStudioVersion = 18.5.11709.299 stable
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN", "v2rayN\v2rayN.csproj", "{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}"
EndProject
@@ -35,8 +36,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLib.Tests", "Service
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLib.UdpTest", "ServiceLib.UdpTest\ServiceLib.UdpTest.csproj", "{CE9D9298-0289-4718-2522-B236489F2912}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetBridgeLib", "NetBridge\src\NetBridgeLib\NetBridgeLib.csproj", "{4AF81950-DD4E-A636-6BD5-B4A7839D87B0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -71,10 +70,6 @@ Global
{CE9D9298-0289-4718-2522-B236489F2912}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE9D9298-0289-4718-2522-B236489F2912}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE9D9298-0289-4718-2522-B236489F2912}.Release|Any CPU.Build.0 = Release|Any CPU
{4AF81950-DD4E-A636-6BD5-B4A7839D87B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AF81950-DD4E-A636-6BD5-B4A7839D87B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AF81950-DD4E-A636-6BD5-B4A7839D87B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AF81950-DD4E-A636-6BD5-B4A7839D87B0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -16,7 +16,6 @@
</Folder>
<Project Path="AmazTool/AmazTool.csproj" />
<Project Path="GlobalHotKeys/src/GlobalHotKeys/GlobalHotKeys.csproj" />
<Project Path="NetBridge/src/NetBridgeLib/NetBridgeLib.csproj" />
<Project Path="ServiceLib.Tests/ServiceLib.Tests.csproj" />
<Project Path="ServiceLib.UdpTest/ServiceLib.UdpTest.csproj" Id="77930428-4dc4-4130-9031-6b9e714f5d20" />
<Project Path="ServiceLib/ServiceLib.csproj" />

View File

@@ -233,10 +233,6 @@
x:Name="menuOpenTheFileLocation"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
<MenuItem
x:Name="menuNetBridge"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuNetBridge}" />
</MenuItem>
</Menu>
<Separator />

View File

@@ -10,7 +10,6 @@ public partial class MainWindow
private static Config _config;
private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView;
private NetBridgeView? _netBridgeView;
public MainWindow()
{
@@ -28,7 +27,6 @@ public partial class MainWindow
menuCheckUpdate.Click += MenuCheckUpdate_Click;
btnNewUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
menuNetBridge.Click += MenuNetBridge_Click;
ViewModel = new MainWindowViewModel(UpdateViewHandler);
@@ -378,19 +376,6 @@ public partial class MainWindow
DialogHost.Show(_backupAndRestoreView, "RootDialog");
}
private void MenuNetBridge_Click(object sender, RoutedEventArgs e)
{
if (Utils.IsAdministrator())
{
_netBridgeView ??= new NetBridgeView();
DialogHost.Show(_netBridgeView, "RootDialog");
}
else
{
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.RunAsAdmin);
}
}
#endregion Event
#region UI

View File

@@ -1,79 +0,0 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.NetBridgeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:v2rayN.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Height="400"
d:DesignHeight="400"
d:DesignWidth="600"
x:TypeArguments="vms:NetBridgeViewModel"
Style="{StaticResource ViewGlobal}"
mc:Ignorable="d">
<DockPanel Margin="{StaticResource Margin8}">
<StackPanel
Margin="{StaticResource Margin8}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<ToggleButton
x:Name="togEnableNetBridge"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Width="500"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbEnableNetBridge}"
TextWrapping="Wrap" />
</StackPanel>
<DockPanel>
<StackPanel
Margin="{StaticResource Margin8}"
DockPanel.Dock="Top"
Orientation="Horizontal">
<TextBlock
Width="500"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.NetBridgeRuleTips}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
Margin="{StaticResource Margin8}"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
x:Name="btnSave"
Width="100"
Content="{x:Static resx:ResUI.TbConfirm}"
IsDefault="True"
Style="{StaticResource DefButton}" />
</StackPanel>
<TextBox
x:Name="txtRuleProcess"
Margin="{StaticResource Margin8}"
VerticalAlignment="Stretch"
AcceptsReturn="True"
BorderThickness="1"
Style="{StaticResource DefTextBox}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</DockPanel>
</DockPanel>
</reactiveui:ReactiveUserControl>

View File

@@ -1,25 +0,0 @@
namespace v2rayN.Views;
public partial class NetBridgeView
{
public NetBridgeView()
{
InitializeComponent();
ViewModel = new NetBridgeViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, vm => vm.SaveRulesCmd, v => v.btnSave).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableNetBridge, v => v.togEnableNetBridge.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleProcess, v => v.txtRuleProcess.Text).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
return await Task.FromResult(true);
}
}

View File

@@ -10,6 +10,7 @@
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CETCompat>false</CETCompat>
</PropertyGroup>
<ItemGroup>