|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.ComponentModel; |
| 4 | +using System.Diagnostics; |
| 5 | +using System.Runtime.InteropServices; |
| 6 | +using System.Security.Principal; |
| 7 | + |
| 8 | +namespace MemPlus.Classes |
| 9 | +{ |
| 10 | + /// <summary> |
| 11 | + /// System Cache Information structure for x86 working set |
| 12 | + /// </summary> |
| 13 | + [StructLayout(LayoutKind.Sequential, Pack = 1)] |
| 14 | + internal struct SystemCacheInformation |
| 15 | + { |
| 16 | + internal uint CurrentSize; |
| 17 | + internal uint PeakSize; |
| 18 | + internal uint PageFaultCount; |
| 19 | + internal uint MinimumWorkingSet; |
| 20 | + internal uint MaximumWorkingSet; |
| 21 | + } |
| 22 | + |
| 23 | + /// <summary> |
| 24 | + /// System Cache Information structure for x64 working set |
| 25 | + /// </summary> |
| 26 | + [StructLayout(LayoutKind.Sequential, Pack = 1)] |
| 27 | + internal struct SystemCacheInformation64Bit |
| 28 | + { |
| 29 | + internal long CurrentSize; |
| 30 | + internal long PeakSize; |
| 31 | + internal long PageFaultCount; |
| 32 | + internal long MinimumWorkingSet; |
| 33 | + internal long MaximumWorkingSet; |
| 34 | + } |
| 35 | + |
| 36 | + /// <summary> |
| 37 | + /// Token Privileges structure, used for adjusting token privileges |
| 38 | + /// </summary> |
| 39 | + [StructLayout(LayoutKind.Sequential, Pack = 1)] |
| 40 | + internal struct TokenPrivileges |
| 41 | + { |
| 42 | + internal int Count; |
| 43 | + internal long Luid; |
| 44 | + internal int Attr; |
| 45 | + } |
| 46 | + |
| 47 | + /// <summary> |
| 48 | + /// Enum containing System Information class values |
| 49 | + /// </summary> |
| 50 | + internal enum SystemInformationClass |
| 51 | + { |
| 52 | + SystemFileCacheInformation = 0x0015, |
| 53 | + SystemMemoryListInformation = 0x0050 |
| 54 | + } |
| 55 | + |
| 56 | + /// <summary> |
| 57 | + /// Class containing methods to 'optimize' or clear memory usage in Windows |
| 58 | + /// </summary> |
| 59 | + internal class MemPlus |
| 60 | + { |
| 61 | + /// <summary> |
| 62 | + /// Constant int used for TokenPrivileges Atrr variable |
| 63 | + /// </summary> |
| 64 | + private const int SePrivilegeEnabled = 2; |
| 65 | + /// <summary> |
| 66 | + /// Adjust memory quotas for a process |
| 67 | + /// </summary> |
| 68 | + private const string SeIncreaseQuotaName = "SeIncreaseQuotaPrivilege"; |
| 69 | + /// <summary> |
| 70 | + /// Profile single process |
| 71 | + /// </summary> |
| 72 | + private const string SeProfileSingleProcessName = "SeProfileSingleProcessPrivilege"; |
| 73 | + private const int MemoryPurgeStandbyList = 4; |
| 74 | + |
| 75 | + [DllImport("advapi32.dll", SetLastError = true)] |
| 76 | + private static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); |
| 77 | + |
| 78 | + [DllImport("advapi32.dll", SetLastError = true)] |
| 79 | + private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokenPrivileges newst, int len, IntPtr prev, IntPtr relen); |
| 80 | + |
| 81 | + [DllImport("ntdll.dll")] |
| 82 | + private static extern uint NtSetSystemInformation(int infoClass, IntPtr info, int length); |
| 83 | + |
| 84 | + [DllImport("psapi.dll")] |
| 85 | + private static extern int EmptyWorkingSet(IntPtr hwProc); |
| 86 | + |
| 87 | + /// <summary> |
| 88 | + /// Clear the working sets of all processes that are available to the application |
| 89 | + /// </summary> |
| 90 | + internal static void EmptyWorkingSetFunction() |
| 91 | + { |
| 92 | + List<string> successes = new List<string>(); |
| 93 | + List<string> failures = new List<string>(); |
| 94 | + |
| 95 | + foreach (Process process in Process.GetProcesses()) |
| 96 | + { |
| 97 | + try |
| 98 | + { |
| 99 | + // Empty the working set of the process |
| 100 | + EmptyWorkingSet(process.Handle); |
| 101 | + // Add it to the list of successfully cleared processes |
| 102 | + successes.Add(process.ProcessName); |
| 103 | + } |
| 104 | + catch (Exception ex) |
| 105 | + { |
| 106 | + failures.Add(process.ProcessName + ": " + ex.Message); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + //Print the lists with successful and failed processes |
| 111 | + Console.WriteLine("Success (count): " + successes.Count); |
| 112 | + Console.WriteLine("---"); |
| 113 | + foreach (string success in successes) |
| 114 | + { |
| 115 | + Console.WriteLine(success); |
| 116 | + } |
| 117 | + |
| 118 | + Console.WriteLine("Failed (count) " + failures.Count); |
| 119 | + Console.WriteLine("---"); |
| 120 | + foreach (string error in failures) |
| 121 | + { |
| 122 | + Console.WriteLine(error); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + /// <summary> |
| 127 | + /// Check whether the system is running a x86 or x64 working set |
| 128 | + /// </summary> |
| 129 | + /// <returns>A boolean to indicate whether or not the system is 64 bit</returns> |
| 130 | + private static bool Is64BitMode() |
| 131 | + { |
| 132 | + return Marshal.SizeOf(typeof(IntPtr)) == 8; |
| 133 | + } |
| 134 | + |
| 135 | + /// <summary> |
| 136 | + /// Clear the FileSystem cache |
| 137 | + /// </summary> |
| 138 | + /// <param name="clearStandbyCache">Set whether or not to clear cache that is in standby</param> |
| 139 | + internal static void ClearFileSystemCache(bool clearStandbyCache) |
| 140 | + { |
| 141 | + try |
| 142 | + { |
| 143 | + //Check if privilege can be increased |
| 144 | + if (SetIncreasePrivilege(SeIncreaseQuotaName)) |
| 145 | + { |
| 146 | + uint ntSetSystemInformationRet; |
| 147 | + int systemInfoLength; |
| 148 | + GCHandle gcHandle; |
| 149 | + //Depending on the working set, call the right external function using the right parameters |
| 150 | + if (!Is64BitMode()) |
| 151 | + { |
| 152 | + SystemCacheInformation cacheInformation = |
| 153 | + new SystemCacheInformation |
| 154 | + { |
| 155 | + MinimumWorkingSet = uint.MaxValue, |
| 156 | + MaximumWorkingSet = uint.MaxValue |
| 157 | + }; |
| 158 | + systemInfoLength = Marshal.SizeOf(cacheInformation); |
| 159 | + gcHandle = GCHandle.Alloc(cacheInformation, GCHandleType.Pinned); |
| 160 | + ntSetSystemInformationRet = NtSetSystemInformation((int)SystemInformationClass.SystemFileCacheInformation, gcHandle.AddrOfPinnedObject(), systemInfoLength); |
| 161 | + gcHandle.Free(); |
| 162 | + } |
| 163 | + else |
| 164 | + { |
| 165 | + SystemCacheInformation64Bit information64Bit = |
| 166 | + new SystemCacheInformation64Bit |
| 167 | + { |
| 168 | + MinimumWorkingSet = -1L, |
| 169 | + MaximumWorkingSet = -1L |
| 170 | + }; |
| 171 | + systemInfoLength = Marshal.SizeOf(information64Bit); |
| 172 | + gcHandle = GCHandle.Alloc(information64Bit, GCHandleType.Pinned); |
| 173 | + ntSetSystemInformationRet = NtSetSystemInformation((int)SystemInformationClass.SystemFileCacheInformation, gcHandle.AddrOfPinnedObject(), systemInfoLength); |
| 174 | + gcHandle.Free(); |
| 175 | + } |
| 176 | + // If value is not equal to zero, things didn't go right :( |
| 177 | + if (ntSetSystemInformationRet != 0) throw new Exception("NtSetSystemInformation: ", new Win32Exception(Marshal.GetLastWin32Error())); |
| 178 | + } |
| 179 | + |
| 180 | + // If we don't have to clear the standby cache or cannot increase privileges, don't hesitate to clear the standby cache, otherwise we can clear the standby cache |
| 181 | + if (!clearStandbyCache || !SetIncreasePrivilege(SeProfileSingleProcessName)) return; |
| 182 | + { |
| 183 | + int systemInfoLength = Marshal.SizeOf(MemoryPurgeStandbyList); |
| 184 | + GCHandle gcHandle = GCHandle.Alloc(MemoryPurgeStandbyList, GCHandleType.Pinned); |
| 185 | + uint ntSetSystemInformationRet = NtSetSystemInformation((int)SystemInformationClass.SystemMemoryListInformation, gcHandle.AddrOfPinnedObject(), systemInfoLength); |
| 186 | + gcHandle.Free(); |
| 187 | + if (ntSetSystemInformationRet != 0) throw new Exception("NtSetSystemInformation: ", new Win32Exception(Marshal.GetLastWin32Error())); |
| 188 | + } |
| 189 | + } |
| 190 | + catch (Exception ex) |
| 191 | + { |
| 192 | + Console.Write(ex.ToString()); |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + /// <summary> |
| 197 | + /// Increase the Privilege |
| 198 | + /// </summary> |
| 199 | + /// <param name="privilegeName">The name of the privilege that needs to be increased</param> |
| 200 | + /// <returns>A boolean value indicating whether or not the operation was successfull</returns> |
| 201 | + private static bool SetIncreasePrivilege(string privilegeName) |
| 202 | + { |
| 203 | + using (WindowsIdentity current = WindowsIdentity.GetCurrent(TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges)) |
| 204 | + { |
| 205 | + TokenPrivileges newst; |
| 206 | + newst.Count = 1; |
| 207 | + newst.Luid = 0L; |
| 208 | + newst.Attr = SePrivilegeEnabled; |
| 209 | + |
| 210 | + // If we can't look up the privilege value, we can't function properly |
| 211 | + if (!LookupPrivilegeValue(null, privilegeName, ref newst.Luid)) throw new Exception("LookupPrivilegeValue: ", new Win32Exception(Marshal.GetLastWin32Error())); |
| 212 | + |
| 213 | + //Enables or disables privileges in a specified access token |
| 214 | + int adjustTokenPrivilegesRet = AdjustTokenPrivileges(current.Token, false, ref newst, 0, IntPtr.Zero, IntPtr.Zero) ? 1 : 0; |
| 215 | + // Return value of zero indicates an error |
| 216 | + if (adjustTokenPrivilegesRet == 0) throw new Exception("AdjustTokenPrivileges: ", new Win32Exception(Marshal.GetLastWin32Error())); |
| 217 | + return adjustTokenPrivilegesRet != 0; |
| 218 | + } |
| 219 | + } |
| 220 | + } |
| 221 | +} |
0 commit comments