Skip to content

Commit ebd9ed3

Browse files
committed
* Looks like we have a working POC that can clear the memory now
1 parent 5e0a187 commit ebd9ed3

File tree

5 files changed

+252
-17
lines changed

5 files changed

+252
-17
lines changed

MemPlus/App.xaml.cs

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Configuration;
4-
using System.Data;
5-
using System.Linq;
64
using System.Threading.Tasks;
75
using System.Windows;
86

MemPlus/Classes/MemPlus.cs

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
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+
}

MemPlus/MemPlus.csproj

+2-9
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,7 @@
3838
</PropertyGroup>
3939
<ItemGroup>
4040
<Reference Include="System" />
41-
<Reference Include="System.Data" />
42-
<Reference Include="System.Xml" />
43-
<Reference Include="Microsoft.CSharp" />
4441
<Reference Include="System.Core" />
45-
<Reference Include="System.Xml.Linq" />
46-
<Reference Include="System.Data.DataSetExtensions" />
47-
<Reference Include="System.Net.Http" />
4842
<Reference Include="System.Xaml">
4943
<RequiredTargetFramework>4.0</RequiredTargetFramework>
5044
</Reference>
@@ -65,6 +59,7 @@
6559
<DependentUpon>App.xaml</DependentUpon>
6660
<SubType>Code</SubType>
6761
</Compile>
62+
<Compile Include="Classes\MemPlus.cs" />
6863
<Compile Include="Windows\MainWindow.xaml.cs">
6964
<DependentUpon>MainWindow.xaml</DependentUpon>
7065
<SubType>Code</SubType>
@@ -96,8 +91,6 @@
9691
<ItemGroup>
9792
<None Include="App.config" />
9893
</ItemGroup>
99-
<ItemGroup>
100-
<Folder Include="Classes\" />
101-
</ItemGroup>
94+
<ItemGroup />
10295
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
10396
</Project>

MemPlus/Windows/MainWindow.xaml

+8-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
55
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6-
xmlns:local="clr-namespace:MemPlus.Windows"
76
mc:Ignorable="d"
8-
Title="MemPlus" Height="350" Width="525" WindowStartupLocation="CenterScreen">
9-
<Grid>
10-
7+
Title="MemPlus" Height="150" Width="300" WindowStartupLocation="CenterScreen">
8+
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
9+
<Grid.RowDefinitions>
10+
<RowDefinition Height="Auto"></RowDefinition>
11+
</Grid.RowDefinitions>
12+
<Button Content="Clear memory" Click="BtnClearMemory_OnClick" Padding="5">
13+
14+
</Button>
1115
</Grid>
1216
</Window>

MemPlus/Windows/MainWindow.xaml.cs

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
1-
using System.Windows;
1+
using System;
2+
using System.Windows;
23

34
namespace MemPlus.Windows
45
{
6+
/// <inheritdoc cref="Window" />
57
/// <summary>
68
/// Interaction logic for MainWindow.xaml
79
/// </summary>
8-
public partial class MainWindow : Window
10+
public partial class MainWindow
911
{
1012
public MainWindow()
1113
{
1214
InitializeComponent();
1315
}
16+
17+
private void BtnClearMemory_OnClick(object sender, RoutedEventArgs e)
18+
{
19+
try
20+
{
21+
//Clear working set of all processes that the user has access to
22+
Classes.MemPlus.EmptyWorkingSetFunction();
23+
//Clear file system cache
24+
Classes.MemPlus.ClearFileSystemCache(true);
25+
26+
MessageBox.Show("Your memory is has now been cleared of any non-essential data.", "MemPlus",MessageBoxButton.OK, MessageBoxImage.Information);
27+
}
28+
catch (Exception ex)
29+
{
30+
MessageBox.Show(ex.Message, "MemPlus", MessageBoxButton.OK, MessageBoxImage.Error);
31+
}
32+
}
1433
}
1534
}

0 commit comments

Comments
 (0)