diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 21942430..b23457c3 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -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 diff --git a/src/Interop/NativeMethods.cs b/src/Interop/NativeMethods.cs index 104de741..256018e4 100644 --- a/src/Interop/NativeMethods.cs +++ b/src/Interop/NativeMethods.cs @@ -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); diff --git a/src/Service/ComputerService.cs b/src/Service/ComputerService.cs index a4ca5319..f992da46 100644 --- a/src/Service/ComputerService.cs +++ b/src/Service/ComputerService.cs @@ -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)) { @@ -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) @@ -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 { @@ -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 { @@ -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); } /// @@ -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 { @@ -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 { @@ -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) { diff --git a/src/Test/NativeMemoryInteropTests.cs b/src/Test/NativeMemoryInteropTests.cs new file mode 100644 index 00000000..0e648681 --- /dev/null +++ b/src/Test/NativeMemoryInteropTests.cs @@ -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 diff --git a/src/WinMemoryCleaner.csproj b/src/WinMemoryCleaner.csproj index 6bd06c24..2d215b7c 100644 --- a/src/WinMemoryCleaner.csproj +++ b/src/WinMemoryCleaner.csproj @@ -163,6 +163,7 @@ +