Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PCL.Core/App/Essentials/UpdateService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using PCL.Core.App.IoC;
Expand Down
41 changes: 28 additions & 13 deletions PCL.Core/IO/ByteStream.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;

namespace PCL.Core.IO;
Expand All @@ -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"];
/// <summary>
/// 格式化大小
/// </summary>
/// <param name="length">字节</param>
/// <param name="startUnit">开始单位</param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
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]}";
}
}
94 changes: 94 additions & 0 deletions PCL.Core/Tools/MemSwap/MemSwapService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using PCL.Core.App.IoC;
using PCL.Core.IO;
using PCL.Core.Tools.MemSwap;
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
{
[LifecycleCommandHandler("memory")]
private static void _CheckRequest()
{
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(1, 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, false);
NtInterop.SetPrivilege(NtInterop.SePrivilege.SeIncreaseQuotaPrivilege, true, false);
}
}
17 changes: 17 additions & 0 deletions PCL.Core/Tools/MemSwap/SwapScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace PCL.Core.Tools.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
}
92 changes: 92 additions & 0 deletions PCL.Core/Tools/MemSwap/SwapWorks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using PCL.Core.Utils.OS;
using System;
using System.Runtime.InteropServices;

namespace PCL.Core.Tools.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>(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);

[StructLayout(LayoutKind.Sequential)]
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);
}
}
2 changes: 1 addition & 1 deletion PCL.Core/Utils/OS/NtInterop.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

Expand Down
9 changes: 7 additions & 2 deletions PCL.Core/Utils/OS/ProcessInterop.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Management;
Expand Down Expand Up @@ -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);
}

Expand Down
14 changes: 0 additions & 14 deletions Plain Craft Launcher 2/Application.xaml.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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
'初始化文件结构
Expand Down
20 changes: 2 additions & 18 deletions Plain Craft Launcher 2/Modules/Base/ModBase.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1390,24 +1391,7 @@ RetryDir:
''' </summary>
''' <param name="FileSize">以字节为单位的大小表示。</param>
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

''' <summary>
Expand Down
Loading