From a6d2b8f3aea66821af90ba074a5a5ca2a03d6a23 Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 19:20:21 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix(mem-swap):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=9C=A8=E6=97=A0=E7=AE=A1=E7=90=86=E5=91=98?= =?UTF-8?q?=E6=9D=83=E9=99=90=E4=B8=8B=E8=B0=83=E7=94=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Essentials/MemSwap/MemSwapService.cs | 100 ++++++++++++++++++ PCL.Core/App/Essentials/MemSwap/SwapScope.cs | 17 +++ PCL.Core/App/Essentials/MemSwap/SwapWorks.cs | 91 ++++++++++++++++ PCL.Core/App/Essentials/UpdateService.cs | 2 +- PCL.Core/IO/ByteStream.cs | 41 ++++--- PCL.Core/Utils/OS/NtInterop.cs | 2 +- PCL.Core/Utils/OS/ProcessInterop.cs | 9 +- Plain Craft Launcher 2/Application.xaml.vb | 14 --- .../Modules/Base/ModBase.vb | 20 +--- .../Pages/PageTools/PageToolsTest.xaml.vb | 62 ++--------- 10 files changed, 254 insertions(+), 104 deletions(-) create mode 100644 PCL.Core/App/Essentials/MemSwap/MemSwapService.cs create mode 100644 PCL.Core/App/Essentials/MemSwap/SwapScope.cs create mode 100644 PCL.Core/App/Essentials/MemSwap/SwapWorks.cs diff --git a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs new file mode 100644 index 000000000..dabefe0ed --- /dev/null +++ b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs @@ -0,0 +1,100 @@ +using PCL.Core.App.IoC; +using PCL.Core.IO; +using PCL.Core.Utils.OS; +using System; +using System.Threading; + +namespace PCL.Core.App.Essentials.MemSwap; + +[LifecycleService(LifecycleState.BeforeLoading, Priority = 10)] +[LifecycleScope("mem-swap", "内存交换", false)] +public partial class MemSwapService +{ + [LifecycleStart] + private static void _CheckRequest() + { + var args = Basics.CommandLineArguments; + + if (args is ["memory"]) + { + Context.Info("检测到内存交换请求,开始处理"); + + if (!ProcessInterop.IsAdmin()) + { + Context.Error("缺少管理员权限,退出内存处理"); + Context.RequestExit(-1); + return; + } + + try + { + var before = KernelInterop.GetPhysicalMemoryBytes().Available; + Context.Info($"处理前内存量 {ByteStream.GetReadableLength((long)before)}"); + AcquirePrivileges(); + if (!MemorySwap(SwapScope.All)) + { + Context.Error("请求无法处理,返回"); + return; + } + + var after = KernelInterop.GetPhysicalMemoryBytes().Available; + Context.Info($"处理后内存量 {ByteStream.GetReadableLength((long)after)}"); + var diff = Math.Max(0, after - before); + Context.Info($"处理结束,总共处理 {ByteStream.GetReadableLength((long)diff)}"); + diff /= 1024; + if (diff > int.MaxValue) diff = int.MaxValue; + + Context.RequestExit((int)diff); + } + catch (Exception ex) + { + Context.Error("内存处理失败", ex); + Context.RequestExit(-1); + } + } + } + + + + private static SemaphoreSlim _memSwapLock = new(0, 1); + public static bool MemorySwap(SwapScope scope = SwapScope.All) + { + if (!_memSwapLock.Wait(0)) + { + Context.Warn("检测到正在进行的内存处理,取消当前处理"); + return false; + } + + try + { + if (!ProcessInterop.IsAdmin()) return false; + + Context.Info($"开始处理,区域请求:{(int)scope}"); + if (scope.HasFlag(SwapScope.EmptyWorkingSets)) SwapWorks.EmptyWorkingSets(); + if (scope.HasFlag(SwapScope.FlushFileCache)) SwapWorks.FlushFileCache(); + if (scope.HasFlag(SwapScope.FlushModifiedList)) SwapWorks.FlushModifiedList(); + if (scope.HasFlag(SwapScope.PurgeStandbyList)) SwapWorks.PurgeStandbyList(); + if (scope.HasFlag(SwapScope.PurgeLowPriorityStandbyList)) SwapWorks.PurgeLowPriorityStandbyList(); + if (scope.HasFlag(SwapScope.RegistryReconciliation)) SwapWorks.RegistryReconciliation(); + if (scope.HasFlag(SwapScope.CombinePhysicalMemory)) SwapWorks.CombinePhysicalMemory(); + + return true; + } + catch (Exception ex) + { + Context.Error("内存处理出现异常", ex); + return false; + } + finally + { + _memSwapLock.Release(); + } + } + + public static void AcquirePrivileges() + { + Context.Info("获取权限……"); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true); + } +} diff --git a/PCL.Core/App/Essentials/MemSwap/SwapScope.cs b/PCL.Core/App/Essentials/MemSwap/SwapScope.cs new file mode 100644 index 000000000..00abf4f27 --- /dev/null +++ b/PCL.Core/App/Essentials/MemSwap/SwapScope.cs @@ -0,0 +1,17 @@ +using System; + +namespace PCL.Core.App.Essentials.MemSwap; + +[Flags] +public enum SwapScope +{ + None = 0, + EmptyWorkingSets = 1 << 0, + FlushFileCache = 1 << 1, + FlushModifiedList = 1 << 2, + PurgeStandbyList = 1 << 3, + PurgeLowPriorityStandbyList = 1 << 4, + RegistryReconciliation = 1 << 5, + CombinePhysicalMemory = 1 << 6, + All = 0b111111 +} diff --git a/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs b/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs new file mode 100644 index 000000000..0927cf725 --- /dev/null +++ b/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs @@ -0,0 +1,91 @@ +using PCL.Core.Utils.OS; +using System; +using System.Runtime.InteropServices; + +namespace PCL.Core.App.Essentials.MemSwap; + +internal static class SwapWorks +{ + private static void _ExecuteMemoryListOperation(int infoValue) + { + GCHandle handle = GCHandle.Alloc(infoValue, GCHandleType.Pinned); + try + { + NtInterop.SetSystemInformation( + NtInterop.SystemInformationClass.SystemMemoryListInformation, + handle.AddrOfPinnedObject(), + (uint)Marshal.SizeOf(infoValue)); + } + finally + { + if (handle.IsAllocated) handle.Free(); + } + } + + private static void _ExecuteStructureOperation(T structure, NtInterop.SystemInformationClass infoClass) + { + GCHandle handle = GCHandle.Alloc(structure, GCHandleType.Pinned); + try + { + NtInterop.SetSystemInformation( + infoClass, + handle.AddrOfPinnedObject(), + (uint)Marshal.SizeOf(structure)); + } + finally + { + if (handle.IsAllocated) handle.Free(); + } + } + + public static void EmptyWorkingSets() => _ExecuteMemoryListOperation(2); + + [StructLayout(LayoutKind.Sequential)] + private struct SYSTEM_FILECACHE_INFORMATION + { + public UIntPtr CurrentSize; + public UIntPtr PeakSize; + public UIntPtr PageFaultCount; + public UIntPtr MinimumWorkingSet; + public UIntPtr MaximumWorkingSet; + public UIntPtr CurrentSizeIncludingTransitionInPages; + public UIntPtr PeakSizeIncludingTransitionInPages; + public UIntPtr TransitionRePurposeCount; + public UIntPtr Flags; + } + + public static void FlushFileCache() + { + var scfi = new SYSTEM_FILECACHE_INFORMATION + { + MaximumWorkingSet = uint.MaxValue, + MinimumWorkingSet = uint.MaxValue + }; + _ExecuteStructureOperation(scfi, NtInterop.SystemInformationClass.SystemFileCacheInformationEx); + } + + public static void FlushModifiedList() => _ExecuteMemoryListOperation(3); + + public static void PurgeStandbyList() => _ExecuteMemoryListOperation(4); + + internal static void PurgeLowPriorityStandbyList() => _ExecuteMemoryListOperation(5); + + public static void RegistryReconciliation() => + NtInterop.SetSystemInformation( + NtInterop.SystemInformationClass.SystemRegistryReconciliationInformation, + IntPtr.Zero, + 0); + + private struct MEMORY_COMBINE_INFORMATION_EX + { + public IntPtr Handle; + public UIntPtr PagesCombined; + public uint Flags; + } + + public static void CombinePhysicalMemory() + { + var combineInfoEx = new MEMORY_COMBINE_INFORMATION_EX(); + _ExecuteStructureOperation(combineInfoEx, NtInterop.SystemInformationClass.SystemCombinePhysicalMemoryInformation); + } +} diff --git a/PCL.Core/App/Essentials/UpdateService.cs b/PCL.Core/App/Essentials/UpdateService.cs index 1af6a7a92..1fe6f09b7 100644 --- a/PCL.Core/App/Essentials/UpdateService.cs +++ b/PCL.Core/App/Essentials/UpdateService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using PCL.Core.App.IoC; diff --git a/PCL.Core/IO/ByteStream.cs b/PCL.Core/IO/ByteStream.cs index 65ab0158a..815d43cad 100644 --- a/PCL.Core/IO/ByteStream.cs +++ b/PCL.Core/IO/ByteStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace PCL.Core.IO; @@ -9,21 +9,36 @@ public class ByteStream(Stream stream) public string GetReadableLength() => GetReadableLength(this.Length); - public static string GetReadableLength(long length) + private static readonly string[] _Units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + /// + /// 格式化大小 + /// + /// 字节 + /// 开始单位 + /// + /// + public static string GetReadableLength(long length, int startUnit = 0) { - string[] unit = ["B", "KB", "MB", "GB", "TB", "PB"]; - var displayCount = length * 100; - var displayUnit = 0; - while (displayCount >= 102400) + bool isNegative = length < 0; + decimal absBytes = isNegative ? -length : length; + + if (absBytes == 0) + return "0 B"; + + int unitIndex = startUnit; + decimal value = absBytes; + + while (value >= 1024 && unitIndex < _Units.Length - 1) { - displayCount >>= 10; - displayUnit++; + value /= 1024; + unitIndex++; } - if (displayUnit > unit.Length) - throw new IndexOutOfRangeException("Why there is no enough unit to show :("); - var displayText = displayCount.ToString(); - displayText = displayText.Insert(displayText.Length - 2, "."); - return $"{displayText} {unit[displayUnit]}"; + if (unitIndex >= _Units.Length) + throw new ArgumentOutOfRangeException(nameof(length), + "Value too large for predefined units."); + + string sign = isNegative ? "-" : ""; + return $"{sign}{value:0.##} {_Units[unitIndex]}"; } } \ No newline at end of file diff --git a/PCL.Core/Utils/OS/NtInterop.cs b/PCL.Core/Utils/OS/NtInterop.cs index aebd1315e..0a2046c95 100644 --- a/PCL.Core/Utils/OS/NtInterop.cs +++ b/PCL.Core/Utils/OS/NtInterop.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Runtime.InteropServices; diff --git a/PCL.Core/Utils/OS/ProcessInterop.cs b/PCL.Core/Utils/OS/ProcessInterop.cs index 861028735..d5434f851 100644 --- a/PCL.Core/Utils/OS/ProcessInterop.cs +++ b/PCL.Core/Utils/OS/ProcessInterop.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Management; @@ -39,7 +39,12 @@ public static bool IsAdmin() => public static Process? Start(string path, string? arguments = null, bool runAsAdmin = false) { var psi = new ProcessStartInfo(path); if (arguments != null) psi.Arguments = arguments; - if (runAsAdmin) psi.Verb = "runas"; + if (runAsAdmin) + { + psi.UseShellExecute = true; + psi.Verb = "runas"; + } + return Process.Start(psi); } diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb index aa945af9a..3a92adb38 100644 --- a/Plain Craft Launcher 2/Application.xaml.vb +++ b/Plain Craft Launcher 2/Application.xaml.vb @@ -43,20 +43,6 @@ Public Class Application Catch ex As Exception Environment.Exit(ProcessReturnValues.Fail) End Try - ElseIf args(0).StartsWithF("--memory") Then - '内存优化 - Dim Ram = KernelInterop.GetAvailablePhysicalMemoryBytes() - Try - PageToolsTest.MemoryOptimizeInternal(False) - Catch ex As Exception - MsgBox(ex.Message, MsgBoxStyle.Critical, "内存优化失败") - Environment.Exit(-1) - End Try - If KernelInterop.GetAvailablePhysicalMemoryBytes() < Ram Then '避免 ULong 相减出现负数 - Environment.Exit(0) - Else - Environment.Exit((KernelInterop.GetAvailablePhysicalMemoryBytes() - Ram) / 1024) '返回清理的内存量(K) - End If End If End If '初始化文件结构 diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index 51dbcd303..5b00054aa 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb @@ -13,6 +13,7 @@ Imports PCL.Core.Utils Imports System.Windows Imports PCL.Core.Utils.Codecs Imports PCL.Core.Utils.OS +Imports PCL.Core.IO Public Module ModBase @@ -1390,24 +1391,7 @@ RetryDir: ''' ''' 以字节为单位的大小表示。 Public Function GetString(FileSize As Long) As String - Dim IsNegative = FileSize < 0 - If IsNegative Then FileSize *= -1 - If FileSize < 1000 Then - 'B 级 - Return If(IsNegative, "-", "") & FileSize & " B" - ElseIf FileSize < 1024 * 1000 Then - 'K 级 - Dim RoundResult As String = Math.Round(FileSize / 1024) - Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " K" - ElseIf FileSize < 1024 * 1024 * 1000 Then - 'M 级 - Dim RoundResult As String = Math.Round(FileSize / 1024 / 1024) - Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024 / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " M" - Else - 'G 级 - Dim RoundResult As String = Math.Round(FileSize / 1024 / 1024 / 1024) - Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024 / 1024 / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " G" - End If + Return ByteStream.GetReadableLength(FileSize) End Function ''' diff --git a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb index d367d6e9e..b74ba10cb 100644 --- a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb @@ -9,6 +9,7 @@ Imports PCL.Core.IO.Net Imports PCL.Core.UI Imports PCL.Core.Utils.OS Imports PCL.Core.Utils.Secret +Imports PCL.Core.App.Essentials Public Class PageToolsTest Public Sub New() @@ -286,7 +287,7 @@ Public Class PageToolsTest Else Log("[Test] 没有管理员权限,将以命令行方式进行内存优化") Try - Dim callProcess = ProcessInterop.StartAsAdmin(Basics.ExecutablePath, "--memory") + Dim callProcess = ProcessInterop.StartAsAdmin(Basics.ExecutablePath, "memory") callProcess.WaitForExit() num = CLng(callProcess.ExitCode) * 1024L Catch ex2 As Exception @@ -320,12 +321,12 @@ Public Class PageToolsTest If Not ProcessInterop.IsAdmin() Then Throw New Exception("内存优化功能需要管理员权限!" & vbCrLf & "如果需要自动以管理员身份启动 PCL,可以右键 PCL,打开 属性 → 兼容性 → 以管理员身份运行此程序。") End If + Log("[Test] 获取内存优化权限") '提权部分 Try - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, True) - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, True) + MemSwap.MemSwapService.AcquirePrivileges() Catch ex As System.ComponentModel.Win32Exception Throw New Exception(String.Format("获取内存优化权限失败(错误代码:{0})", ex.NativeErrorCode)) End Try @@ -334,58 +335,9 @@ Public Class PageToolsTest Hint("正在进行内存优化……", ModMain.HintType.Info, True) End If - '内存优化部分 - Dim NowType = "None" - Try - Dim info As Integer - Dim scfi As SYSTEM_FILECACHE_INFORMATION - Dim combineInfoEx As MEMORY_COMBINE_INFORMATION_EX - Dim _gcHandle As GCHandle - - NowType = "MemoryEmptyWorkingSets" - info = 2 - _gcHandle = GCHandle.Alloc(info, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemMemoryListInformation, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(info)) - _gcHandle.Free() - NowType = "SystemFileCacheInformation" - scfi.MaximumWorkingSet = UInteger.MaxValue - scfi.MinimumWorkingSet = UInteger.MaxValue - _gcHandle = GCHandle.Alloc(scfi, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemFileCacheInformationEx, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(scfi)) - _gcHandle.Free() - NowType = "MemoryFlushModifiedList" - info = 3 - _gcHandle = GCHandle.Alloc(info, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemMemoryListInformation, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(info)) - _gcHandle.Free() - NowType = "MemoryPurgeStandbyList" - info = 4 - _gcHandle = GCHandle.Alloc(info, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemMemoryListInformation, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(info)) - _gcHandle.Free() - NowType = "MemoryPurgeLowPriorityStandbyList" - info = 5 - _gcHandle = GCHandle.Alloc(info, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemMemoryListInformation, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(info)) - _gcHandle.Free() - NowType = "SystemRegistryReconciliationInformation" - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemRegistryReconciliationInformation, - New IntPtr(Nothing), 0) - NowType = "SystemCombinePhysicalMemoryInformation" - _gcHandle = GCHandle.Alloc(combineInfoEx, GCHandleType.Pinned) - NtInterop.SetSystemInformation(NtInterop.SystemInformationClass.SystemCombinePhysicalMemoryInformation, - _gcHandle.AddrOfPinnedObject(), Marshal.SizeOf(combineInfoEx)) - _gcHandle.Free() - Catch ex As System.ComponentModel.Win32Exception - Throw New Exception(String.Format("内存优化操作 {0} 失败(错误代码:{1})", NowType, ex.NativeErrorCode)) - Catch ex As Exception - Throw New Exception(String.Format("内存优化操作 {0} 失败(错误信息:{1})", NowType, ex.Message)) - End Try + If Not MemSwap.MemSwapService.MemorySwap() Then + Hint("内存优化失败") + End If End Sub Public Shared Function GetRandomCave() As String From 5f00fb1de631a6a8d40ab429abe2cd636d130ba1 Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 19:42:40 +0800 Subject: [PATCH 2/7] chore: follow #2585 --- PCL.Core/App/Essentials/MemSwap/MemSwapService.cs | 8 ++++---- PCL.Core/Utils/OS/NtInterop.cs | 8 ++++---- .../Pages/PageTools/PageToolsTest.xaml.vb | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs index dabefe0ed..5ac572c04 100644 --- a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs +++ b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs @@ -56,7 +56,7 @@ private static void _CheckRequest() - private static SemaphoreSlim _memSwapLock = new(0, 1); + private static SemaphoreSlim _memSwapLock = new(1, 1); public static bool MemorySwap(SwapScope scope = SwapScope.All) { if (!_memSwapLock.Wait(0)) @@ -71,7 +71,7 @@ public static bool MemorySwap(SwapScope scope = SwapScope.All) Context.Info($"开始处理,区域请求:{(int)scope}"); if (scope.HasFlag(SwapScope.EmptyWorkingSets)) SwapWorks.EmptyWorkingSets(); - if (scope.HasFlag(SwapScope.FlushFileCache)) SwapWorks.FlushFileCache(); + //if (scope.HasFlag(SwapScope.FlushFileCache)) SwapWorks.FlushFileCache(); if (scope.HasFlag(SwapScope.FlushModifiedList)) SwapWorks.FlushModifiedList(); if (scope.HasFlag(SwapScope.PurgeStandbyList)) SwapWorks.PurgeStandbyList(); if (scope.HasFlag(SwapScope.PurgeLowPriorityStandbyList)) SwapWorks.PurgeLowPriorityStandbyList(); @@ -94,7 +94,7 @@ public static bool MemorySwap(SwapScope scope = SwapScope.All) public static void AcquirePrivileges() { Context.Info("获取权限……"); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true, false); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, false); } } diff --git a/PCL.Core/Utils/OS/NtInterop.cs b/PCL.Core/Utils/OS/NtInterop.cs index 0a2046c95..8d429b94d 100644 --- a/PCL.Core/Utils/OS/NtInterop.cs +++ b/PCL.Core/Utils/OS/NtInterop.cs @@ -15,9 +15,9 @@ private static partial void RtlGetNtVersionNumbers( [LibraryImport("ntdll.dll")] private static partial uint RtlAdjustPrivilege( SePrivilege privilege, - [MarshalAs(UnmanagedType.Bool)] bool enable, - [MarshalAs(UnmanagedType.Bool)] bool currentThread, - [MarshalAs(UnmanagedType.Bool)] out bool enabled); + [MarshalAs(UnmanagedType.U1)] bool enable, + [MarshalAs(UnmanagedType.U1)] bool currentThread, + [MarshalAs(UnmanagedType.U1)] out bool enabled); [LibraryImport("ntdll.dll")] private static partial ulong RtlNtStatusToDosError(uint status); @@ -64,7 +64,7 @@ public static Version GetCurrentOsVersion() /// 返回原来相应特权的状态。 public static bool SetPrivilege(SePrivilege privilege, bool state, bool currentThread = true) { - var result = RtlAdjustPrivilege(privilege, false, currentThread, out var enabled); + var result = RtlAdjustPrivilege(privilege, state, currentThread, out var enabled); if (result != 0) _ThrowLastWin32Error((int)RtlNtStatusToDosError(result)); return enabled; } diff --git a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb index b74ba10cb..c30e2450a 100644 --- a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb @@ -105,7 +105,7 @@ Public Class PageToolsTest Else 'UNC 路径 loaderdownload = New LoaderDownloadUnc("自定义下载文件:" + FileName + " ", New Tuple(Of String, String)(Url, Folder + FileName)) End If - Dim loaderCombo As New LoaderCombo(Of Integer)("自定义下载 (" + uuid.ToString() + ") ", New LoaderBase() {loaderDownload}) With {.OnStateChanged = AddressOf DownloadState} + Dim loaderCombo As New LoaderCombo(Of Integer)("自定义下载 (" + uuid.ToString() + ") ", New LoaderBase() {loaderdownload}) With {.OnStateChanged = AddressOf DownloadState} loaderCombo.Start() LoaderTaskbarAdd(Of Integer)(loaderCombo) FrmMain.BtnExtraDownload.ShowRefresh() @@ -336,7 +336,7 @@ Public Class PageToolsTest End If If Not MemSwap.MemSwapService.MemorySwap() Then - Hint("内存优化失败") + If ShowHint Then Hint("内存优化失败") End If End Sub From 5d6ffa475e4c67860a6ba12e21c024fddf642b7b Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 21:22:49 +0800 Subject: [PATCH 3/7] chore: enable FlushFileCache --- PCL.Core/App/Essentials/MemSwap/MemSwapService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs index 5ac572c04..56e42611f 100644 --- a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs +++ b/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs @@ -71,7 +71,7 @@ public static bool MemorySwap(SwapScope scope = SwapScope.All) Context.Info($"开始处理,区域请求:{(int)scope}"); if (scope.HasFlag(SwapScope.EmptyWorkingSets)) SwapWorks.EmptyWorkingSets(); - //if (scope.HasFlag(SwapScope.FlushFileCache)) SwapWorks.FlushFileCache(); + if (scope.HasFlag(SwapScope.FlushFileCache)) SwapWorks.FlushFileCache(); if (scope.HasFlag(SwapScope.FlushModifiedList)) SwapWorks.FlushModifiedList(); if (scope.HasFlag(SwapScope.PurgeStandbyList)) SwapWorks.PurgeStandbyList(); if (scope.HasFlag(SwapScope.PurgeLowPriorityStandbyList)) SwapWorks.PurgeLowPriorityStandbyList(); From c6196744343c82dc1191d7d173ac88c671f8dc52 Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 21:26:35 +0800 Subject: [PATCH 4/7] chore: resolve warnings --- PCL.Core/App/Essentials/MemSwap/SwapWorks.cs | 1 + .../Pages/PageTools/PageToolsTest.xaml.vb | 22 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs b/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs index 0927cf725..4b19e4463 100644 --- a/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs +++ b/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs @@ -76,6 +76,7 @@ public static void RegistryReconciliation() => IntPtr.Zero, 0); + [StructLayout(LayoutKind.Sequential)] private struct MEMORY_COMBINE_INFORMATION_EX { public IntPtr Handle; diff --git a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb index 1a31ba7e0..014b99d5d 100644 --- a/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.vb @@ -1,8 +1,5 @@ Imports System.Drawing -Imports System.Net Imports System.Net.Http -Imports System.Runtime.InteropServices -Imports System.Threading.Tasks Imports PCL.Core.App Imports PCL.Core.IO Imports PCL.Core.IO.Net @@ -222,25 +219,6 @@ Public Class PageToolsTest End Try End Sub, "Rubbish Clear") End Sub - - Public Structure SYSTEM_FILECACHE_INFORMATION - Public CurrentSize As UIntPtr - Public PeakSize As UIntPtr - Public PageFaultCount As UInteger - Public MinimumWorkingSet As UIntPtr - Public MaximumWorkingSet As UIntPtr - Public CurrentSizeIncludingTransitionInPages As UIntPtr - Public PeakSizeIncludingTransitionInPages As UIntPtr - Public TransitionRePurposeCount As UInteger - Public Flags As UInteger - End Structure - - Public Structure MEMORY_COMBINE_INFORMATION_EX - Public Handle As IntPtr - Public PagesCombined As UIntPtr - Public Flags As UInteger - End Structure - Public Shared Function AskTrulyWantMemoryOptimize() Dim memTotal = KernelInterop.GetPhysicalMemoryBytes().Total / 1024 / 1024 / 1024 'GB Dim memLoad = KernelInterop.GetMemoryLoadPercent() From 2a703ef7aa0155696f94d583115fed919266fd4a Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 22:06:54 +0800 Subject: [PATCH 5/7] chore: mvoe files --- PCL.Core/{App/Essentials => Tools}/MemSwap/MemSwapService.cs | 1 + PCL.Core/{App/Essentials => Tools}/MemSwap/SwapScope.cs | 2 +- PCL.Core/{App/Essentials => Tools}/MemSwap/SwapWorks.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) rename PCL.Core/{App/Essentials => Tools}/MemSwap/MemSwapService.cs (99%) rename PCL.Core/{App/Essentials => Tools}/MemSwap/SwapScope.cs (88%) rename PCL.Core/{App/Essentials => Tools}/MemSwap/SwapWorks.cs (98%) diff --git a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs b/PCL.Core/Tools/MemSwap/MemSwapService.cs similarity index 99% rename from PCL.Core/App/Essentials/MemSwap/MemSwapService.cs rename to PCL.Core/Tools/MemSwap/MemSwapService.cs index 56e42611f..33c656986 100644 --- a/PCL.Core/App/Essentials/MemSwap/MemSwapService.cs +++ b/PCL.Core/Tools/MemSwap/MemSwapService.cs @@ -1,5 +1,6 @@ using PCL.Core.App.IoC; using PCL.Core.IO; +using PCL.Core.Tools.MemSwap; using PCL.Core.Utils.OS; using System; using System.Threading; diff --git a/PCL.Core/App/Essentials/MemSwap/SwapScope.cs b/PCL.Core/Tools/MemSwap/SwapScope.cs similarity index 88% rename from PCL.Core/App/Essentials/MemSwap/SwapScope.cs rename to PCL.Core/Tools/MemSwap/SwapScope.cs index 00abf4f27..a3d62cddd 100644 --- a/PCL.Core/App/Essentials/MemSwap/SwapScope.cs +++ b/PCL.Core/Tools/MemSwap/SwapScope.cs @@ -1,6 +1,6 @@ using System; -namespace PCL.Core.App.Essentials.MemSwap; +namespace PCL.Core.Tools.MemSwap; [Flags] public enum SwapScope diff --git a/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs b/PCL.Core/Tools/MemSwap/SwapWorks.cs similarity index 98% rename from PCL.Core/App/Essentials/MemSwap/SwapWorks.cs rename to PCL.Core/Tools/MemSwap/SwapWorks.cs index 4b19e4463..91d3d44c0 100644 --- a/PCL.Core/App/Essentials/MemSwap/SwapWorks.cs +++ b/PCL.Core/Tools/MemSwap/SwapWorks.cs @@ -2,7 +2,7 @@ using System; using System.Runtime.InteropServices; -namespace PCL.Core.App.Essentials.MemSwap; +namespace PCL.Core.Tools.MemSwap; internal static class SwapWorks { From c1cc3980ab8f58ea3d2d69ee4e257312453c0084 Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 22:23:54 +0800 Subject: [PATCH 6/7] chore: use command line handler --- PCL.Core/Tools/MemSwap/MemSwapService.cs | 63 +++++++++++------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/PCL.Core/Tools/MemSwap/MemSwapService.cs b/PCL.Core/Tools/MemSwap/MemSwapService.cs index 33c656986..24926ed0d 100644 --- a/PCL.Core/Tools/MemSwap/MemSwapService.cs +++ b/PCL.Core/Tools/MemSwap/MemSwapService.cs @@ -11,52 +11,45 @@ namespace PCL.Core.App.Essentials.MemSwap; [LifecycleScope("mem-swap", "内存交换", false)] public partial class MemSwapService { - [LifecycleStart] + [LifecycleCommandHandler("memory")] private static void _CheckRequest() { - var args = Basics.CommandLineArguments; + Context.Info("检测到内存交换请求,开始处理"); - if (args is ["memory"]) + if (!ProcessInterop.IsAdmin()) { - Context.Info("检测到内存交换请求,开始处理"); + Context.Error("缺少管理员权限,退出内存处理"); + Context.RequestExit(-1); + return; + } - if (!ProcessInterop.IsAdmin()) + try + { + var before = KernelInterop.GetPhysicalMemoryBytes().Available; + Context.Info($"处理前内存量 {ByteStream.GetReadableLength((long)before)}"); + AcquirePrivileges(); + if (!MemorySwap(SwapScope.All)) { - Context.Error("缺少管理员权限,退出内存处理"); - Context.RequestExit(-1); + Context.Error("请求无法处理,返回"); return; } - try - { - var before = KernelInterop.GetPhysicalMemoryBytes().Available; - Context.Info($"处理前内存量 {ByteStream.GetReadableLength((long)before)}"); - AcquirePrivileges(); - if (!MemorySwap(SwapScope.All)) - { - Context.Error("请求无法处理,返回"); - return; - } - - var after = KernelInterop.GetPhysicalMemoryBytes().Available; - Context.Info($"处理后内存量 {ByteStream.GetReadableLength((long)after)}"); - var diff = Math.Max(0, after - before); - Context.Info($"处理结束,总共处理 {ByteStream.GetReadableLength((long)diff)}"); - diff /= 1024; - if (diff > int.MaxValue) diff = int.MaxValue; + var after = KernelInterop.GetPhysicalMemoryBytes().Available; + Context.Info($"处理后内存量 {ByteStream.GetReadableLength((long)after)}"); + var diff = Math.Max(0, after - before); + Context.Info($"处理结束,总共处理 {ByteStream.GetReadableLength((long)diff)}"); + diff /= 1024; + if (diff > int.MaxValue) diff = int.MaxValue; - Context.RequestExit((int)diff); - } - catch (Exception ex) - { - Context.Error("内存处理失败", ex); - Context.RequestExit(-1); - } + Context.RequestExit((int)diff); + } + catch (Exception ex) + { + Context.Error("内存处理失败", ex); + Context.RequestExit(-1); } } - - private static SemaphoreSlim _memSwapLock = new(1, 1); public static bool MemorySwap(SwapScope scope = SwapScope.All) { @@ -95,7 +88,7 @@ public static bool MemorySwap(SwapScope scope = SwapScope.All) public static void AcquirePrivileges() { Context.Info("获取权限……"); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true, false); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, false); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true, true); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, true); } } From 497afa85ea8ce66f9bd545bb7718ab973dbac6fc Mon Sep 17 00:00:00 2001 From: tangge233 Date: Tue, 10 Mar 2026 22:32:14 +0800 Subject: [PATCH 7/7] chore: current thread not work --- PCL.Core/Tools/MemSwap/MemSwapService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PCL.Core/Tools/MemSwap/MemSwapService.cs b/PCL.Core/Tools/MemSwap/MemSwapService.cs index 24926ed0d..88882ce99 100644 --- a/PCL.Core/Tools/MemSwap/MemSwapService.cs +++ b/PCL.Core/Tools/MemSwap/MemSwapService.cs @@ -88,7 +88,7 @@ public static bool MemorySwap(SwapScope scope = SwapScope.All) public static void AcquirePrivileges() { Context.Info("获取权限……"); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true, true); - NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, true); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeProfileSingleProcessPrivilege, true, false); + NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, false); } }