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
8 changes: 8 additions & 0 deletions src/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,17 @@ public static class ShowWindow
public static class SystemErrorCode
{
public const int ErrorAccessDenied = 5; // (ERROR_ACCESS_DENIED) Access is denied
public const int ErrorInvalidParameter = 87; // (ERROR_INVALID_PARAMETER) The parameter is incorrect
public const int ErrorNotAllAssigned = 1300; // (ERROR_NOT_ALL_ASSIGNED) Not all privileges or groups referenced are assigned to the caller
public const int ErrorSuccess = 0; // (ERROR_SUCCESS) The operation completed successfully
}

public static class NtStatus
{
public const int StatusAccessDenied = unchecked((int)0xC0000022);
public const int StatusInvalidParameter = unchecked((int)0xC000000D);
}

public static class SystemInformationClass
{
public const int SystemCombinePhysicalMemoryInformation = 130; // 0x82
Expand Down
3 changes: 3 additions & 0 deletions src/Interop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ internal static class NativeMethods
[DllImport("ntdll.dll", SetLastError = true)]
internal static extern int NtSetSystemInformation(int SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength);

[DllImport("ntdll.dll")]
internal static extern uint RtlNtStatusToDosError(int status);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
Expand Down
53 changes: 38 additions & 15 deletions src/Service/ComputerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ private static SafeFileHandle OpenVolumeHandle(string driveLetter)
private bool SetIncreasePrivilege(string privilegeName)
{
var result = false;
var lastWin32Error = Constants.Windows.SystemErrorCode.ErrorSuccess;

using (var current = WindowsIdentity.GetCurrent(TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges))
{
Expand All @@ -125,16 +126,32 @@ private bool SetIncreasePrivilege(string privilegeName)
if (NativeMethods.LookupPrivilegeValue(null, privilegeName, ref newState.Luid))
{
// Enables or disables privileges in a specified access token
result = NativeMethods.AdjustTokenPrivileges(current.Token, false, ref newState, 0, IntPtr.Zero, IntPtr.Zero);
var privilegesAdjusted = NativeMethods.AdjustTokenPrivileges(current.Token, false, ref newState, 0, IntPtr.Zero, IntPtr.Zero);
lastWin32Error = Marshal.GetLastWin32Error();
result = IsAdjustTokenPrivilegesSuccessful(privilegesAdjusted, lastWin32Error);
}
else
{
lastWin32Error = Marshal.GetLastWin32Error();
}

if (!result)
Logger.Error(new Win32Exception(Marshal.GetLastWin32Error()));
Logger.Error(new Win32Exception(lastWin32Error));
}

return result;
}

internal static Win32Exception CreateNtStatusException(int ntStatus)
{
return new Win32Exception((int)NativeMethods.RtlNtStatusToDosError(ntStatus));
}

internal static bool IsAdjustTokenPrivilegesSuccessful(bool privilegesAdjusted, int lastWin32Error)
{
return privilegesAdjusted && lastWin32Error == Constants.Windows.SystemErrorCode.ErrorSuccess;
}

#endregion

#region Methods (Memory)
Expand Down Expand Up @@ -474,8 +491,9 @@ private void OptimizeCombinedPageList()

handle = GCHandle.Alloc(memoryCombineInformationEx, GCHandleType.Pinned);

if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemCombinePhysicalMemoryInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(memoryCombineInformationEx)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemCombinePhysicalMemoryInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(memoryCombineInformationEx));
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw CreateNtStatusException(status);
}
finally
{
Expand Down Expand Up @@ -590,8 +608,9 @@ private void OptimizeModifiedPageList()

try
{
if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(Constants.Windows.SystemMemoryListCommand.MemoryFlushModifiedList)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(Constants.Windows.SystemMemoryListCommand.MemoryFlushModifiedList));
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw CreateNtStatusException(status);
}
finally
{
Expand All @@ -616,8 +635,9 @@ private void OptimizeRegistryCache()
if (!OperatingSystem.HasRegistryHive)
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorMemoryAreaOptimizationNotSupported, Localizer.String.RegistryCache));

if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemRegistryReconciliationInformation, IntPtr.Zero, 0) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemRegistryReconciliationInformation, IntPtr.Zero, 0);
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw CreateNtStatusException(status);
}

/// <summary>
Expand All @@ -639,8 +659,9 @@ private void OptimizeStandbyList(bool lowPriority = false)

try
{
if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(memoryPurgeStandbyList)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(memoryPurgeStandbyList));
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw CreateNtStatusException(status);
}
finally
{
Expand Down Expand Up @@ -682,8 +703,9 @@ private void OptimizeSystemFileCache()

handle = GCHandle.Alloc(systemFileCacheInformation, GCHandleType.Pinned);

if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemFileCacheInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(systemFileCacheInformation)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemFileCacheInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(systemFileCacheInformation));
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw CreateNtStatusException(status);
}
finally
{
Expand Down Expand Up @@ -751,15 +773,16 @@ private void OptimizeWorkingSet()
{
// Check privilege
if (!SetIncreasePrivilege(Constants.Windows.Privilege.SeProfSingleProcessName))
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorAdminPrivilegeRequired, Constants.Windows.Privilege.SeDebugName));
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorAdminPrivilegeRequired, Constants.Windows.Privilege.SeProfSingleProcessName));

var handle = GCHandle.Alloc(Constants.Windows.SystemMemoryListCommand.MemoryEmptyWorkingSets, GCHandleType.Pinned);

try
{
if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(Constants.Windows.SystemMemoryListCommand.MemoryEmptyWorkingSets)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
var status = NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(Constants.Windows.SystemMemoryListCommand.MemoryEmptyWorkingSets));
if (status != Constants.Windows.SystemErrorCode.ErrorSuccess)
{
var e = new Win32Exception(Marshal.GetLastWin32Error());
var e = CreateNtStatusException(status);

if (e != null)
{
Expand Down
68 changes: 68 additions & 0 deletions src/Test/NativeMemoryInteropTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using NUnit.Framework;

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

namespace WinMemoryCleaner.NativeMemoryTests
{
[TestFixture]
public sealed class NativeMemoryInteropTests
{
[Test]
public void CreateNtStatusException_WhenStatusIsAccessDenied_MapsToWin32AccessDenied()
{
var exception = ComputerService.CreateNtStatusException(Constants.Windows.NtStatus.StatusAccessDenied);

Assert.AreEqual(Constants.Windows.SystemErrorCode.ErrorAccessDenied, exception.NativeErrorCode);
}

[Test]
public void CreateNtStatusException_WhenStatusIsInvalidParameter_MapsWithoutStaleWin32Error()
{
var exception = ComputerService.CreateNtStatusException(Constants.Windows.NtStatus.StatusInvalidParameter);

Assert.AreEqual(Constants.Windows.SystemErrorCode.ErrorInvalidParameter, exception.NativeErrorCode);
}

[Test]
public void IsAdjustTokenPrivilegesSuccessful_WhenNativeCallAndLastErrorSucceed_ReturnsTrue()
{
Assert.IsTrue(ComputerService.IsAdjustTokenPrivilegesSuccessful(
true,
Constants.Windows.SystemErrorCode.ErrorSuccess));
}

[Test]
public void IsAdjustTokenPrivilegesSuccessful_WhenNotAllPrivilegesAreAssigned_ReturnsFalse()
{
Assert.IsFalse(ComputerService.IsAdjustTokenPrivilegesSuccessful(
true,
Constants.Windows.SystemErrorCode.ErrorNotAllAssigned));
}

[Test]
public void IsAdjustTokenPrivilegesSuccessful_WhenNativeCallSucceedsWithError_ReturnsFalse()
{
Assert.IsFalse(ComputerService.IsAdjustTokenPrivilegesSuccessful(
true,
Constants.Windows.SystemErrorCode.ErrorAccessDenied));
}

[Test]
public void IsAdjustTokenPrivilegesSuccessful_WhenNativeCallFailsWithSuccessError_ReturnsFalse()
{
Assert.IsFalse(ComputerService.IsAdjustTokenPrivilegesSuccessful(
false,
Constants.Windows.SystemErrorCode.ErrorSuccess));
}

[Test]
public void IsAdjustTokenPrivilegesSuccessful_WhenNativeCallFailsWithError_ReturnsFalse()
{
Assert.IsFalse(ComputerService.IsAdjustTokenPrivilegesSuccessful(
false,
Constants.Windows.SystemErrorCode.ErrorAccessDenied));
}
}
}

#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
1 change: 1 addition & 0 deletions src/WinMemoryCleaner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
<Compile Include="Test\CoreTests.cs" />
<Compile Include="Test\IntegrationTests.cs" />
<Compile Include="Test\Mocker.cs" />
<Compile Include="Test\NativeMemoryInteropTests.cs" />
<Compile Include="Test\ModelTests.cs" />
<Compile Include="Test\ServiceTests.cs" />
<Compile Include="Test\TestCleanup.cs" />
Expand Down
Loading