chore: remove NoWarn and fix .NET 10 build warnings across platforms (#9301)

* deps: bump ZXing.Net.Bindings.SkiaSharp from 0.16.14 to 0.16.22

Patch update to the latest stable release on the 0.16.x line. No breaking changes, no public API changes - purely internal fixes.

Verified by a full Release build of v2rayN.sln on .NET 10; no new warnings or errors are introduced.

* chore: remove NoWarn and fix .NET 10 build warnings

Removes the repository-level NoWarn suppression from Directory.Build.props and addresses the warnings that surface on top of the .NET 10 migration in #9179, keeping Debug, Release, and cross-platform publishes warning-free without suppressing warnings globally.

Changes:

- Removes <NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058;IDE0053;IDE0200</NoWarn> from Directory.Build.props.

- Annotates Windows-only APIs with [SupportedOSPlatform] and [SupportedOSPlatformGuard] so CA1416 accepts that the Windows surface is gated behind Utils.IsWindows() / Utils.IsNonWindows().

- Splits Utils.SetUnixFileMode into a cross-platform wrapper and a private [UnsupportedOSPlatform("windows")] implementation so File.SetUnixFileMode never reaches the analyzer on Windows builds.

- Adds a parameterless constructor to MessageBoxDialog so Avalonia's runtime XAML loader (AVLN3001) can instantiate the dialog.

- Moves the WPF high-DPI configuration from app.manifest to <ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode> in v2rayN.csproj, fixing WFO0003.

- Adds global using System.Runtime.Versioning; to ServiceLib and v2rayN.Desktop so the platform attributes are usable project-wide.

* test: make cycle dependency tests locale-independent

Accept the localized Russian cycle dependency diagnostic in CoreConfigContextBuilderTests so the assertions pass when tests run under a Russian UI culture.

* fix: tighten Unix platform handling

Adds Linux and macOS platform guards so the analyzer can narrow calls through Utils.IsLinux() and Utils.IsMacOS().

Marks the Linux/macOS autostart and system proxy helpers with explicit platform attributes.

Updates Utils.GetSystemHosts() to read /etc/hosts on Linux and macOS while keeping the existing Windows hosts and hosts.ics merge behavior.
This commit is contained in:
Miheichev Aleksandr Sergeevich
2026-05-14 11:25:07 +00:00
committed by GitHub
parent 780777068d
commit 58d2641559
19 changed files with 66 additions and 16 deletions
-1
View File
@@ -8,7 +8,6 @@
<TargetFramework>net10.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058;IDE0053;IDE0200</NoWarn>
<Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors>
+1 -1
View File
@@ -33,6 +33,6 @@
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="YamlDotNet" Version="17.1.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
</ItemGroup>
</Project>
@@ -99,7 +99,8 @@ public class CoreConfigContextBuilderTests
{
return message.Contains("cycle dependency", StringComparison.OrdinalIgnoreCase)
|| message.Contains("循环依赖", StringComparison.Ordinal)
|| message.Contains("循環依賴", StringComparison.Ordinal);
|| message.Contains("循環依賴", StringComparison.Ordinal)
|| message.Contains("циклическую зависимость", StringComparison.OrdinalIgnoreCase);
}
private static async Task UpsertProfilesAsync(params ProfileItem[] profiles)
+30 -6
View File
@@ -864,15 +864,25 @@ public class Utils
public static Dictionary<string, string> GetSystemHosts()
{
var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
foreach (var (key, value) in hostsIcs)
if (IsWindows())
{
hosts[key] = value;
var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
foreach (var (key, value) in hostsIcs)
{
hosts[key] = value;
}
return hosts;
}
return hosts;
if (IsLinux() || IsMacOS())
{
return GetSystemHosts("/etc/hosts");
}
return new Dictionary<string, string>();
}
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
@@ -1114,12 +1124,16 @@ public class Utils
#region Platform
[SupportedOSPlatformGuard("windows")]
public static bool IsWindows() => OperatingSystem.IsWindows();
[SupportedOSPlatformGuard("linux")]
public static bool IsLinux() => OperatingSystem.IsLinux();
[SupportedOSPlatformGuard("macos")]
public static bool IsMacOS() => OperatingSystem.IsMacOS();
[UnsupportedOSPlatformGuard("windows")]
public static bool IsNonWindows() => !OperatingSystem.IsWindows();
public static string GetExeName(string name)
@@ -1214,6 +1228,16 @@ public class Utils
}
public static bool SetUnixFileMode(string? fileName)
{
if (IsWindows())
{
return false;
}
return SetUnixFileModeInternal(fileName);
}
[UnsupportedOSPlatform("windows")]
private static bool SetUnixFileModeInternal(string? fileName)
{
try
{
+1
View File
@@ -2,6 +2,7 @@ using Microsoft.Win32;
namespace ServiceLib.Common;
[SupportedOSPlatform("windows")]
internal static class WindowsUtils
{
private static readonly string _tag = "WindowsUtils";
+1
View File
@@ -8,6 +8,7 @@ global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using System.Reflection;
global using System.Runtime.InteropServices;
global using System.Runtime.Versioning;
global using System.Security.Cryptography;
global using System.Text;
global using System.Text.Encodings.Web;
@@ -41,6 +41,7 @@ public static class AutoStartupHandler
#region Windows
[SupportedOSPlatform("windows")]
private static async Task ClearTaskWindows()
{
var autoRunName = GetAutoRunNameWindows();
@@ -53,6 +54,7 @@ public static class AutoStartupHandler
await Task.CompletedTask;
}
[SupportedOSPlatform("windows")]
private static async Task SetTaskWindows()
{
try
@@ -82,6 +84,7 @@ public static class AutoStartupHandler
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
[SupportedOSPlatform("windows")]
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (taskName.IsNullOrEmpty())
@@ -124,6 +127,7 @@ public static class AutoStartupHandler
#region Linux
[SupportedOSPlatform("linux")]
private static async Task ClearTaskLinux()
{
try
@@ -137,6 +141,7 @@ public static class AutoStartupHandler
await Task.CompletedTask;
}
[SupportedOSPlatform("linux")]
private static async Task SetTaskLinux()
{
try
@@ -157,6 +162,7 @@ public static class AutoStartupHandler
}
}
[SupportedOSPlatform("linux")]
private static string GetHomePathLinux()
{
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
@@ -168,6 +174,7 @@ public static class AutoStartupHandler
#region macOS
[SupportedOSPlatform("macos")]
private static async Task ClearTaskOSX()
{
try
@@ -187,6 +194,7 @@ public static class AutoStartupHandler
}
}
[SupportedOSPlatform("macos")]
private static async Task SetTaskOSX()
{
try
@@ -204,6 +212,7 @@ public static class AutoStartupHandler
}
}
[SupportedOSPlatform("macos")]
private static string GetLaunchAgentPathMacOS()
{
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
@@ -212,6 +221,7 @@ public static class AutoStartupHandler
return launchAgentPath;
}
[SupportedOSPlatform("macos")]
private static string GenerateLaunchAgentPlist()
{
var exePath = Utils.GetExePath();
@@ -1,5 +1,6 @@
namespace ServiceLib.Handler.SysProxy;
[SupportedOSPlatform("linux")]
public static class ProxySettingLinux
{
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -1,5 +1,6 @@
namespace ServiceLib.Handler.SysProxy;
[SupportedOSPlatform("macos")]
public static class ProxySettingOSX
{
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -2,6 +2,7 @@ using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionO
namespace ServiceLib.Handler.SysProxy;
[SupportedOSPlatform("windows")]
public static class ProxySettingWindows
{
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
@@ -88,6 +88,7 @@ public static class SysProxyHandler
}
}
[SupportedOSPlatform("windows")]
private static async Task SetWindowsProxyPac(int port)
{
var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac);
+1
View File
@@ -8,6 +8,7 @@ public class CoreManager
private static readonly Lazy<CoreManager> _instance = new(() => new());
public static CoreManager Instance => _instance.Value;
private Config _config;
[SupportedOSPlatform("windows")]
private WindowsJobService? _processJob;
private ProcessService? _processService;
private ProcessService? _processPreService;
@@ -3,6 +3,7 @@ namespace ServiceLib.Services;
/// <summary>
/// http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
/// </summary>
[SupportedOSPlatform("windows")]
public sealed class WindowsJobService : IDisposable
{
private nint handle = nint.Zero;
@@ -23,6 +23,7 @@ public partial class QRCodeAvaloniaUtils
}
}
[SupportedOSPlatform("windows")]
private static byte[]? CaptureScreenWindows()
{
var hdcScreen = IntPtr.Zero;
+1
View File
@@ -5,6 +5,7 @@ global using System.IO;
global using System.Linq;
global using System.Reactive.Disposables.Fluent;
global using System.Reactive.Linq;
global using System.Runtime.Versioning;
global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
@@ -8,6 +8,7 @@ public sealed class HotkeyManager
private static readonly Lazy<HotkeyManager> _instance = new(() => new());
public static HotkeyManager Instance = _instance.Value;
private readonly Dictionary<int, EGlobalHotkey> _hotkeyTriggerDic = new();
[SupportedOSPlatform("windows")]
private GlobalHotKeys.HotKeyManager? _hotKeyManager;
private Config? _config;
@@ -16,6 +17,7 @@ public sealed class HotkeyManager
public bool IsPause { get; set; } = false;
[SupportedOSPlatform("windows")]
public void Init(Config config, Action<EGlobalHotkey> updateFunc)
{
_config = config;
@@ -26,9 +28,14 @@ public sealed class HotkeyManager
public void Dispose()
{
if (!Utils.IsWindows())
{
return;
}
_hotKeyManager?.Dispose();
}
[SupportedOSPlatform("windows")]
private void Register()
{
if (_config.GlobalHotkeys.Any(t => t.KeyCode > 0) == false)
@@ -4,6 +4,11 @@ namespace v2rayN.Desktop.Views;
public partial class MessageBoxDialog : Window
{
public MessageBoxDialog()
: this(string.Empty, string.Empty)
{
}
public MessageBoxDialog(string caption, string message)
{
InitializeComponent();
-7
View File
@@ -1,12 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<dependency>
<dependentAssembly>
+1
View File
@@ -7,6 +7,7 @@
<UseWPF>true</UseWPF>
<ApplicationIcon>Resources\v2rayN.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>