- Crate Selection & Cargo.toml
- windows-sys vs windows Crate
- Complete Cargo.toml
- Privilege Elevation (SeProfileSingleProcessPrivilege)
- NtSetSystemInformation FFI Declaration
- GlobalMemoryStatusEx
- SetSystemFileCacheSize — File Cache Trimming
- Registry Cache Flush — NtSetSystemInformation(SystemRegistryReconciliationInformation)
- Process Enumeration & Working Set Trimming
- Memory Combining (Windows 10+)
- Complete Cleaning Sequence
- Full Compilable Source (Simplified Prototype)
- Current Architecture & Features
| Crate | Purpose | Why |
|---|---|---|
windows-sys |
Win32/NT API bindings | Zero-cost, no wrappers — just extern "system" declarations + types. Fastest compile times. Official Microsoft crate. |
clap (with derive feature) |
CLI argument parsing | Industry standard. Derive macros give type-safe subcommands with zero boilerplate. |
colored |
Terminal colors | Lightweight, simple API for colored CLI output. |
anyhow |
Error handling | Ergonomic Result<T> with context chains. |
serde + serde_json |
JSON serialization | Used for --report JSON output and --json status output. |
| Crate | Why Skip |
|---|---|
ntapi |
Stale (last updated 2021). Provides NT API bindings, but windows-sys + manual FFI for the 2-3 undocumented functions is cleaner. |
winapi |
Legacy. windows-sys is Microsoft's official replacement. |
windows (high-level) |
Adds smart pointers, RAII wrappers, COM support — overhead you don't need for a CLI tool calling C-style APIs. |
sysinfo |
Cross-platform abstractions — unnecessary overhead when you already call GlobalMemoryStatusEx directly via windows-sys. |
// Raw C-style bindings. No wrappers, no overhead.
use windows_sys::Win32::System::Threading::OpenProcessToken;
// Returns BOOL (i32). You check == 0 for failure.Pros:
- Zero abstraction cost — same as hand-written
extern "system"blocks - Fastest compile times (~3-5s incremental)
- All types are plain
repr(C)structs and integer constants - Official Microsoft crate, updated with every Windows SDK release
- Perfect for system-level tools that need raw control
Cons:
- Everything is
unsafe - No RAII — you must
CloseHandlemanually - Error handling is manual (
GetLastError())
// Smart wrappers with RAII and Result types.
use windows::Win32::System::Threading::OpenProcessToken;
// Returns windows::core::Result<()>. Handle auto-closes on drop.Pros:
- RAII handles (auto-close)
Resultreturn types- Better for COM-heavy or UI code
Cons:
- 2-3x longer compile times
- Hidden allocations and overhead
- More complex error types
- Overkill for a CLI tool making direct kernel calls
Use windows-sys for this project. A RAM cleaner calls ~10 Win32 functions total. The raw bindings are more predictable and you need unsafe anyway for NtSetSystemInformation (which isn't in any crate — you must declare it yourself).
[package]
name = "magicx-ram-cleaner"
version = "1.0.0"
edition = "2024"
authors = ["MagicX"]
description = "The most powerful Windows RAM cleaner CLI — surpasses EmptyStandbyList with granular control over every memory subsystem"
license = "MIT"
readme = "README.md"
keywords = ["ram", "memory", "cleaner", "windows", "standby-list"]
categories = ["command-line-utilities", "os::windows-apis"]
repository = "https://github.com/ehsan18t/magicx-ram-cleaner"
[lints.clippy]
all = { level = "deny", priority = -1 }
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
# Windows API casts (u32↔i32, usize→u32) are inherent to the FFI boundary.
# Precision loss in u64→f64 is acceptable for human-readable display formatting.
cast_possible_wrap = "allow"
cast_possible_truncation = "allow"
cast_precision_loss = "allow"
cast_sign_loss = "allow"
# Binary-only crate: modules are pub solely for criterion benchmark access.
missing_errors_doc = "allow"
missing_panics_doc = "allow"
[lints.rust]
unsafe_op_in_unsafe_fn = "deny"
missing_docs = "deny"
let_underscore_drop = "deny"
non_ascii_idents = "deny"
[profile.release]
opt-level = 3 # Optimize for speed
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for best optimization
strip = true # Strip debug symbols
panic = "abort" # No unwinding
[dependencies]
clap = { version = "4", features = ["derive", "color", "help", "usage", "suggestions"] }
colored = "3"
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# GUI framework (egui + eframe with glow renderer for smaller binary)
eframe = { version = "0.33", default-features = false, features = ["glow", "default_fonts"] }
egui_extras = "0.33"
egui-phosphor = { version = "0.11", features = ["regular"] }
tray-icon = "0.21"
image = { version = "0.25", default-features = false, features = ["png", "ico"] }
ab_glyph = "0.2"
[dependencies.windows-sys]
version = "0.61"
features = [
# Foundation types (HANDLE, BOOL, LUID, CloseHandle, GetLastError)
"Win32_Foundation",
# Memory APIs (SetSystemFileCacheSize)
"Win32_System_Memory",
# Process/thread APIs (OpenProcess, GetCurrentProcess, OpenProcessToken)
"Win32_System_Threading",
# Security APIs (AdjustTokenPrivileges, LookupPrivilegeValueW)
"Win32_Security",
# Authorization APIs (SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES)
"Win32_Security_Authorization",
# Process status APIs (K32EnumProcesses, K32EmptyWorkingSet, K32GetPerformanceInfo)
"Win32_System_ProcessStatus",
# System information (GlobalMemoryStatusEx)
"Win32_System_SystemInformation",
# Console APIs (GetConsoleProcessList)
"Win32_System_Console",
# Registry APIs (RegCreateKeyExW, RegSetValueExW, RegDeleteTreeW)
"Win32_System_Registry",
# Shell notification APIs (Shell_NotifyIconW, NOTIFYICONDATAW)
"Win32_UI_Shell",
# Icon loading and window messaging (LoadIconW, DestroyIcon)
"Win32_UI_WindowsAndMessaging",
# Module handle for icon loading (GetModuleHandleW)
"Win32_System_LibraryLoader",
# File I/O (CreateFileW for CONOUT$/CONIN$ handle redirection)
"Win32_Storage_FileSystem",
# Common dialog controls (GetOpenFileNameW, GetSaveFileNameW)
"Win32_UI_Controls_Dialogs",
# Desktop Window Manager (DwmSetWindowAttribute for dark title bar)
"Win32_Graphics_Dwm",
# Toolhelp32 APIs (CreateToolhelp32Snapshot, Process32FirstW)
"Win32_System_Diagnostics_ToolHelp",
]
[build-dependencies]
embed-manifest = "1"
winresource = "0.1"
ab_glyph = "0.2"
egui-phosphor = { version = "0.11", features = ["regular"] }
[dev-dependencies]
criterion = { version = "0.8", features = ["html_reports"] }
[[bench]]
name = "benchmarks"
harness = false| Feature | Functions / Types It Provides |
|---|---|
Win32_Foundation |
HANDLE, BOOL, LUID, LUID_AND_ATTRIBUTES, CloseHandle, GetLastError, TRUE/FALSE, NTSTATUS |
Win32_System_Memory |
SetSystemFileCacheSize, GetSystemFileCacheSize |
Win32_System_Threading |
GetCurrentProcess, OpenProcess, PROCESS_SET_QUOTA, PROCESS_QUERY_INFORMATION |
Win32_Security |
TOKEN_ADJUST_PRIVILEGES, TOKEN_QUERY, AdjustTokenPrivileges, LookupPrivilegeValueW, OpenProcessToken |
Win32_Security_Authorization |
TOKEN_PRIVILEGES, SE_PRIVILEGE_ENABLED |
Win32_System_ProcessStatus |
K32EnumProcesses, K32EmptyWorkingSet, K32GetPerformanceInfo, PERFORMANCE_INFORMATION |
Win32_System_SystemInformation |
GlobalMemoryStatusEx, MEMORYSTATUSEX, GetSystemInfo |
Win32_System_Console |
AttachConsole/AllocConsole (dynamic console for SUBSYSTEM:WINDOWS), SetStdHandle, GetConsoleProcessList |
Win32_Storage_FileSystem |
CreateFileW (open CONOUT$/CONIN$ for std handle redirection after AttachConsole) |
Win32_System_Diagnostics_ToolHelp |
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W (per-process enumeration) |
Win32_UI_Shell |
Shell_NotifyIconW, NOTIFYICONDATAW, NIM_ADD, NIM_DELETE, NIF_* (balloon notifications) |
Win32_UI_WindowsAndMessaging |
LoadIconW, DestroyIcon (icon loading for notifications) |
Win32_System_LibraryLoader |
GetModuleHandleW (module handle for icon resource loading) |
Win32_System_Registry |
RegCreateKeyExW, RegSetValueExW, RegDeleteTreeW, RegCloseKey (context menu & autostart registry ops) |
Win32_UI_Controls_Dialogs |
GetOpenFileNameW, GetSaveFileNameW, OPENFILENAMEW (native file dialogs for backup/restore) |
Win32_Graphics_Dwm |
DwmSetWindowAttribute, DWMWA_USE_IMMERSIVE_DARK_MODE (dark title bar on Windows 10+) |
All memory list commands require SeProfileSingleProcessPrivilege. File cache trimming requires SeIncreaseQuotaPrivilege. Both are held by Administrators but disabled by default — you must enable them in the token.
use std::mem;
use std::ptr;
use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE, LUID};
use windows_sys::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueW, OpenProcessToken,
SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
};
use windows_sys::Win32::System::Threading::GetCurrentProcess;
/// Enables a named privilege in the current process token.
/// Returns Ok(()) on success, Err with Win32 error code on failure.
fn enable_privilege(privilege_name: &str) -> Result<(), u32> {
unsafe {
// Step 1: Open our own process token
let mut token: HANDLE = 0;
if OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
) == 0
{
return Err(GetLastError());
}
// Step 2: Look up the LUID for the privilege name
let wide_name: Vec<u16> = privilege_name
.encode_utf16()
.chain(std::iter::once(0))
.collect();
let mut luid: LUID = mem::zeroed();
if LookupPrivilegeValueW(ptr::null(), wide_name.as_ptr(), &mut luid) == 0 {
CloseHandle(token);
return Err(GetLastError());
}
// Step 3: Build TOKEN_PRIVILEGES struct and adjust
let mut tp: TOKEN_PRIVILEGES = mem::zeroed();
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
let success = AdjustTokenPrivileges(
token,
0, // FALSE — don't disable all
&tp,
mem::size_of::<TOKEN_PRIVILEGES>() as u32,
ptr::null_mut(),
ptr::null_mut(),
);
let error = GetLastError();
CloseHandle(token);
// AdjustTokenPrivileges returns success even if not all privileges
// were adjusted. Must check GetLastError() == ERROR_SUCCESS (0).
if success == 0 || error != 0 {
return Err(error);
}
Ok(())
}
}
// Convenience wrappers
fn enable_profile_privilege() -> Result<(), u32> {
enable_privilege("SeProfileSingleProcessPrivilege")
}
fn enable_quota_privilege() -> Result<(), u32> {
enable_privilege("SeIncreaseQuotaPrivilege")
}- The privilege name strings are case-sensitive — use exact casing as shown.
AdjustTokenPrivilegeshas a quirk: it returnsTRUEeven when not all privileges were enabled. You must checkGetLastError() == ERROR_NOT_ALL_ASSIGNED (0x514)to detect partial failure.- The process must be run as Administrator. The privilege exists in admin tokens but is disabled by default.
TOKEN_PRIVILEGESinwindows-syshasPrivilegesas a[LUID_AND_ATTRIBUTES; 1]— a single-element array, which is exactly what we need.
NtSetSystemInformation is an undocumented NT API exported by ntdll.dll. It is NOT in windows-sys. You must declare it manually via #[link].
use std::ffi::c_void;
// ── NTSTATUS codes ──
const STATUS_SUCCESS: i32 = 0;
const STATUS_ACCESS_DENIED: i32 = 0xC0000022_u32 as i32;
const STATUS_INFO_LENGTH_MISMATCH: i32 = 0xC0000004_u32 as i32;
const STATUS_INVALID_PARAMETER: i32 = 0xC000000D_u32 as i32;
// ── SystemInformationClass values ──
const SYSTEM_MEMORY_LIST_INFORMATION: u32 = 0x50; // 80 decimal
const SYSTEM_COMBINE_PHYSICAL_MEMORY_INFORMATION: u32 = 0x82; // 130 decimal
// ── SYSTEM_MEMORY_LIST_COMMAND enum ──
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
enum MemoryListCommand {
CaptureAccessedBits = 0,
CaptureAndResetAccessedBits = 1,
EmptyWorkingSets = 2,
FlushModifiedList = 3,
PurgeStandbyList = 4,
PurgeLowPriorityStandbyList = 5,
CommandMax = 6,
}
// ── Struct for querying memory list info ──
#[repr(C)]
#[derive(Debug, Default, Clone)]
struct SystemMemoryListInformation {
zero_page_count: u64,
free_page_count: u64,
modified_page_count: u64,
modified_no_write_page_count: u64,
bad_page_count: u64,
page_count_by_priority: [u64; 8],
repurposed_pages_by_priority: [u64; 8],
modified_page_count_page_file: u64,
}
// ── Struct for memory combining ──
#[repr(C)]
#[derive(Debug)]
struct MemoryCombineInformationEx {
handle: isize, // HANDLE
pages_combined: usize, // ULONG_PTR
}
// ── Link to ntdll.dll and declare the functions ──
#[link(name = "ntdll")]
extern "system" {
fn NtSetSystemInformation(
SystemInformationClass: u32,
SystemInformation: *mut c_void,
SystemInformationLength: u32,
) -> i32; // NTSTATUS
fn NtQuerySystemInformation(
SystemInformationClass: u32,
SystemInformation: *mut c_void,
SystemInformationLength: u32,
ReturnLength: *mut u32,
) -> i32; // NTSTATUS
}-
#[link(name = "ntdll")]— The Rust linker resolvesNtSetSystemInformationandNtQuerySystemInformationfromntdll.libat link time. No need forLoadLibrary/GetProcAddress— every Windows process hasntdll.dllloaded from the start. -
Calling convention is
"system"— On x86_64 Windows this is__stdcall(which is the same as__cdeclon x64, butextern "system"is the correct portable declaration). -
The command is passed as a pointer to an
i32—NtSetSystemInformationtakesPVOID SystemInformationpointing to the command value, andSystemInformationLength=sizeof(i32)= 4.
use std::mem;
/// Issue a memory list command via NtSetSystemInformation.
/// Requires SeProfileSingleProcessPrivilege to be enabled.
fn issue_memory_command(command: MemoryListCommand) -> Result<(), i32> {
unsafe {
let mut cmd = command as i32;
let status = NtSetSystemInformation(
SYSTEM_MEMORY_LIST_INFORMATION,
&mut cmd as *mut i32 as *mut c_void,
mem::size_of::<i32>() as u32,
);
if status != STATUS_SUCCESS {
Err(status)
} else {
Ok(())
}
}
}
// Convenience functions
fn purge_standby_list() -> Result<(), i32> {
issue_memory_command(MemoryListCommand::PurgeStandbyList)
}
fn purge_low_priority_standby() -> Result<(), i32> {
issue_memory_command(MemoryListCommand::PurgeLowPriorityStandbyList)
}
fn empty_all_working_sets() -> Result<(), i32> {
issue_memory_command(MemoryListCommand::EmptyWorkingSets)
}
fn flush_modified_list() -> Result<(), i32> {
issue_memory_command(MemoryListCommand::FlushModifiedList)
}/// Query detailed memory page list information from the kernel.
fn query_memory_lists() -> Result<SystemMemoryListInformation, i32> {
unsafe {
let mut info = SystemMemoryListInformation::default();
let mut return_length: u32 = 0;
let status = NtQuerySystemInformation(
SYSTEM_MEMORY_LIST_INFORMATION,
&mut info as *mut _ as *mut c_void,
mem::size_of::<SystemMemoryListInformation>() as u32,
&mut return_length,
);
if status != STATUS_SUCCESS {
Err(status)
} else {
Ok(info)
}
}
}
// Usage:
fn print_memory_lists() {
if let Ok(info) = query_memory_lists() {
let page_size = 4096u64;
let total_standby: u64 = info.page_count_by_priority.iter().sum();
println!("Free pages: {} ({} MB)", info.free_page_count,
info.free_page_count * page_size / 1024 / 1024);
println!("Zero pages: {} ({} MB)", info.zero_page_count,
info.zero_page_count * page_size / 1024 / 1024);
println!("Modified pages: {} ({} MB)", info.modified_page_count,
info.modified_page_count * page_size / 1024 / 1024);
println!("Standby total: {} ({} MB)", total_standby,
total_standby * page_size / 1024 / 1024);
for (i, &count) in info.page_count_by_priority.iter().enumerate() {
if count > 0 {
println!(" Standby P{}: {} ({} MB)", i, count,
count * page_size / 1024 / 1024);
}
}
}
}This is fully covered by windows-sys. No manual FFI needed.
use std::mem;
use windows_sys::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
fn get_memory_status() -> Result<MEMORYSTATUSEX, u32> {
unsafe {
let mut status: MEMORYSTATUSEX = mem::zeroed();
status.dwLength = mem::size_of::<MEMORYSTATUSEX>() as u32;
if GlobalMemoryStatusEx(&mut status) == 0 {
return Err(windows_sys::Win32::Foundation::GetLastError());
}
Ok(status)
}
}
fn print_memory_status() {
let s = get_memory_status().expect("GlobalMemoryStatusEx failed");
let mb = |bytes: u64| bytes / 1024 / 1024;
println!("Memory Load: {}%", s.dwMemoryLoad);
println!("Total Physical: {} MB", mb(s.ullTotalPhys));
println!("Avail Physical: {} MB", mb(s.ullAvailPhys));
println!("Total PageFile: {} MB", mb(s.ullTotalPageFile));
println!("Avail PageFile: {} MB", mb(s.ullAvailPageFile));
println!("Total Virtual: {} MB", mb(s.ullTotalVirtual));
println!("Avail Virtual: {} MB", mb(s.ullAvailVirtual));
}The MEMORYSTATUSEX struct from windows-sys (in Win32::System::SystemInformation) is:
#[repr(C)]
pub struct MEMORYSTATUSEX {
pub dwLength: u32, // Must set to size_of::<Self>() before calling
pub dwMemoryLoad: u32, // 0-100% physical memory in use
pub ullTotalPhys: u64, // Total physical RAM (bytes)
pub ullAvailPhys: u64, // Available = free + zero + standby (bytes)
pub ullTotalPageFile: u64, // Commit limit (bytes)
pub ullAvailPageFile: u64, // Available commit (bytes)
pub ullTotalVirtual: u64, // Total user-mode VA (bytes)
pub ullAvailVirtual: u64, // Available user-mode VA (bytes)
pub ullAvailExtendedVirtual: u64, // Always 0
}Key insight: ullAvailPhys = standby + free + zero page lists. This matches "Available" in Task Manager. After purging the standby list, ullAvailPhys increases by the amount of standby pages freed.
The Windows file system cache (aka "System Cache Working Set") holds recently-read file data in physical memory. SetSystemFileCacheSize can flush this cache, forcing pages to the standby list (from where they can be purged).
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Memory::SetSystemFileCacheSize;
/// Flush the file system cache by setting min/max to -1.
/// Requires SeIncreaseQuotaPrivilege to be enabled.
fn flush_file_cache() -> Result<(), u32> {
// Enable SeIncreaseQuotaPrivilege first
enable_privilege("SeIncreaseQuotaPrivilege").map_err(|e| e)?;
unsafe {
// Setting both to usize::MAX (which is (SIZE_T)-1) tells Windows
// to flush the system file cache working set.
let result = SetSystemFileCacheSize(
usize::MAX, // MinimumFileCacheSize = (SIZE_T)-1
usize::MAX, // MaximumFileCacheSize = (SIZE_T)-1
0, // Flags = 0 (no hard limits)
);
if result == 0 {
Err(GetLastError())
} else {
Ok(())
}
}
}extern "system" {
// In windows_sys::Win32::System::Memory
pub fn SetSystemFileCacheSize(
MinimumFileCacheSize: usize, // SIZE_T
MaximumFileCacheSize: usize, // SIZE_T
Flags: u32, // DWORD
) -> i32; // BOOL
}| Parameter | Value for Flushing | Meaning |
|---|---|---|
MinimumFileCacheSize |
usize::MAX (= (SIZE_T)-1) |
Special sentinel: flush cache |
MaximumFileCacheSize |
usize::MAX (= (SIZE_T)-1) |
Special sentinel: flush cache |
Flags |
0 |
No hard limit enforcement |
const FILE_CACHE_MAX_HARD_ENABLE: u32 = 0x00000001; // Enforce a hard max cache size
const FILE_CACHE_MAX_HARD_DISABLE: u32 = 0x00000002; // Remove hard max limit
const FILE_CACHE_MIN_HARD_ENABLE: u32 = 0x00000004; // Enforce a hard min cache size
const FILE_CACHE_MIN_HARD_DISABLE: u32 = 0x00000008; // Remove hard min limitWindows caches registry hive modifications in memory before writing them to disk. NtSetSystemInformation with information class SystemRegistryReconciliationInformation (155 / 0x9B) forces all dirty registry hive pages to be written to disk, freeing the modified memory they occupy.
/// SystemRegistryReconciliationInformation = 155 (0x9B)
const SYSTEM_REGISTRY_RECONCILIATION_INFORMATION: u32 = 155;
/// Flush registry cache to disk. No input buffer required.
fn flush_registry_cache() -> Result<(), i32> {
let status = unsafe {
NtSetSystemInformation(
SYSTEM_REGISTRY_RECONCILIATION_INFORMATION,
std::ptr::null_mut(),
0,
)
};
if status == 0 { Ok(()) } else { Err(status) }
}- Information class: 155 (
SystemRegistryReconciliationInformation) - Input buffer: NULL (no input data required)
- Buffer length: 0
- Required privilege: Administrator (no specific privilege token beyond elevation)
- Source: phnt headers from SystemInformer project; also used by Mem Reduct
This operation is included in MagicX's aggressive and nuclear cleaning levels, executed after the file cache flush and before working set trimming.
use windows_sys::Win32::System::ProcessStatus::K32EnumProcesses;
/// Returns a Vec of all process IDs currently running on the system.
fn enum_processes() -> Result<Vec<u32>, u32> {
unsafe {
// Start with space for 4096 PIDs (16 KB). Sufficient for most systems.
let mut pids: Vec<u32> = vec![0u32; 4096];
let mut bytes_returned: u32 = 0;
let result = K32EnumProcesses(
pids.as_mut_ptr(),
(pids.len() * std::mem::size_of::<u32>()) as u32,
&mut bytes_returned,
);
if result == 0 {
return Err(windows_sys::Win32::Foundation::GetLastError());
}
let count = bytes_returned as usize / std::mem::size_of::<u32>();
pids.truncate(count);
Ok(pids)
}
}use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE};
use windows_sys::Win32::System::ProcessStatus::K32EmptyWorkingSet;
use windows_sys::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_SET_QUOTA,
};
/// Empties the working set of a single process by PID.
/// Returns Ok(true) if trimmed, Ok(false) if process couldn't be opened.
fn empty_process_working_set(pid: u32) -> Result<bool, u32> {
unsafe {
// Need PROCESS_SET_QUOTA for EmptyWorkingSet
// Need PROCESS_QUERY_INFORMATION to read process info
let handle: HANDLE = OpenProcess(
PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION,
0, // bInheritHandle = FALSE
pid,
);
if handle == 0 {
// Can't open — protected process, access denied, etc.
return Ok(false);
}
let result = K32EmptyWorkingSet(handle);
CloseHandle(handle);
if result == 0 {
// EmptyWorkingSet failed but we did open the process
Ok(false)
} else {
Ok(true)
}
}
}
/// Empties working sets of ALL processes. Returns (trimmed_count, failed_count).
fn empty_all_process_working_sets() -> Result<(u32, u32), u32> {
let pids = enum_processes()?;
let mut trimmed = 0u32;
let mut failed = 0u32;
for &pid in &pids {
if pid == 0 {
continue; // System Idle Process
}
match empty_process_working_set(pid) {
Ok(true) => trimmed += 1,
Ok(false) => failed += 1,
Err(_) => failed += 1,
}
}
Ok((trimmed, failed))
}use windows_sys::Win32::System::Threading::SetProcessWorkingSetSizeEx;
/// Empty a process's working set using SetProcessWorkingSetSizeEx.
/// This is what EmptyWorkingSet calls internally.
fn trim_process_working_set(handle: HANDLE) -> Result<(), u32> {
unsafe {
// Passing -1 for both min and max empties the working set.
let result = SetProcessWorkingSetSizeEx(
handle,
usize::MAX, // dwMinimumWorkingSetSize = (SIZE_T)-1
usize::MAX, // dwMaximumWorkingSetSize = (SIZE_T)-1
0, // Flags = 0
);
if result == 0 {
Err(GetLastError())
} else {
Ok(())
}
}
}You have two approaches to emptying working sets:
| Approach | API | Pros | Cons |
|---|---|---|---|
| Kernel-level (one call) | NtSetSystemInformation(0x50, cmd=2) |
Single syscall, empties ALL processes including protected ones | No per-process control, no error reporting per process |
| User-level (per-process) | EnumProcesses + EmptyWorkingSet per PID |
Can skip critical processes, report per-process results, show progress | Many syscalls, can't touch protected processes |
Recommendation: Use NtSetSystemInformation(cmd=2) for the bulk operation, then optionally use per-process trimming for targeted cleaning.
Memory combining (page deduplication) finds identical physical pages and merges them into a single copy, freeing the duplicates. This is triggered by NtSetSystemInformation with SystemCombinePhysicalMemoryInformation (0x82 = 130).
/// Trigger memory page combining/deduplication.
/// Returns the number of pages that were combined.
fn combine_memory_pages() -> Result<usize, i32> {
// Enable SeProfileSingleProcessPrivilege
enable_profile_privilege().map_err(|_| STATUS_ACCESS_DENIED)?;
unsafe {
let mut info = MemoryCombineInformationEx {
handle: 0,
pages_combined: 0,
};
let status = NtSetSystemInformation(
SYSTEM_COMBINE_PHYSICAL_MEMORY_INFORMATION, // 0x82 = 130
&mut info as *mut _ as *mut c_void,
std::mem::size_of::<MemoryCombineInformationEx>() as u32,
);
if status != STATUS_SUCCESS {
Err(status)
} else {
Ok(info.pages_combined)
}
}
}- The
MEMORY_COMBINE_INFORMATION_EXstruct is straightforward:handle: Set to0/NULLon inputpages_combined: Output — number of pages that were deduplicated
- This operation can take several seconds on systems with lots of RAM
- It scans physical memory for identical 4K pages, then creates copy-on-write mappings
- Low risk — pages are marked copy-on-write, so any write creates a new copy
- Returns
STATUS_SUCCESSeven if 0 pages were combined
There's also MEMORY_COMBINE_INFORMATION_EX2 with flags:
#[repr(C)]
struct MemoryCombineInformationEx2 {
handle: isize,
pages_combined: usize,
flags: u32, // New in Windows 10 1607+
}For most purposes, MemoryCombineInformationEx (without flags) is sufficient and more compatible.
use std::time::Instant;
struct CleanResult {
cache_flushed: bool,
processes_trimmed: u32,
modified_flushed: bool,
low_priority_purged: bool,
standby_purged: bool,
pages_combined: usize,
freed_mb: u64,
duration_ms: u128,
}
fn full_clean(aggressive: bool) -> Result<CleanResult, anyhow::Error> {
let start = Instant::now();
// Snapshot memory before
let before = get_memory_status()?;
// ── Step 1: Flush file system cache ──
// Moves file cache pages → standby list
// Requires: SeIncreaseQuotaPrivilege
let cache_flushed = flush_file_cache().is_ok();
// ── Step 2: Empty process working sets ──
// Forces all process pages → standby (clean) or modified (dirty)
// Requires: SeProfileSingleProcessPrivilege
let (processes_trimmed, _) = empty_all_process_working_sets().unwrap_or((0, 0));
// ── Step 3: Flush modified page list ──
// Writes dirty pages to disk → moves them to standby
// This must happen BEFORE purging standby, so we don't lose data
// Requires: SeProfileSingleProcessPrivilege
let modified_flushed = flush_modified_list().is_ok();
// ── Step 4a: Purge low-priority standby list ──
// Frees low-value cache pages (SuperFetch prefetched, etc.)
// Safe — these pages have minimal impact if lost
let low_priority_purged = purge_low_priority_standby().is_ok();
// ── Step 4b: Purge ALL standby list (aggressive mode only) ──
// Nuclear option — destroys ALL cached file data
let standby_purged = if aggressive {
purge_standby_list().is_ok()
} else {
false
};
// ── Step 5: Memory combining/dedup ──
// Scans for duplicate physical pages and merges them
// Low impact, can be slow on large RAM systems
let pages_combined = combine_memory_pages().unwrap_or(0);
// Snapshot memory after
let after = get_memory_status()?;
let freed_mb = (after.ullAvailPhys.saturating_sub(before.ullAvailPhys)) / 1024 / 1024;
Ok(CleanResult {
cache_flushed,
processes_trimmed,
modified_flushed,
low_priority_purged,
standby_purged,
pages_combined,
freed_mb,
duration_ms: start.elapsed().as_millis(),
})
}| Step | Why This Position |
|---|---|
| 1. File cache flush | Moves cache pages to standby so they can be purged in step 4 |
| 2. Empty working sets | Moves process pages to standby (clean) or modified (dirty) |
| 3. Flush modified | Writes dirty pages to disk so they become standby (clean) — prevents data loss |
| 4. Purge standby | Now the standby list is maximally full — purge converts it all to free pages |
| 5. Memory combine | Runs last because it's slow and benefits from the memory layout left by steps 1-4 |
enum CleanLevel {
/// Gentle: Purge ALL standby pages (priorities 0-7).
/// No process impact — standby pages are already outside working sets.
Gentle,
/// Moderate: Flush modified pages to disk, then purge ALL standby.
/// No working-set eviction — running apps are unaffected.
Moderate,
/// Aggressive: File cache + registry flush + empty working sets +
/// flush modified + purge ALL standby. Maximum recovery.
Aggressive,
/// Nuclear: Everything aggressive does + memory combining + 2nd pass.
Nuclear,
}
fn clean_at_level(level: CleanLevel) -> Result<(), anyhow::Error> {
enable_profile_privilege().ok();
enable_quota_privilege().ok();
match level {
CleanLevel::Gentle => {
purge_standby_list().ok(); // all priorities 0-7
}
CleanLevel::Moderate => {
flush_modified_list().ok(); // dirty pages → standby
purge_standby_list().ok(); // then clear all standby
}
CleanLevel::Aggressive => {
flush_file_cache().ok();
flush_registry_cache().ok();
empty_all_working_sets().ok();
flush_modified_list().ok();
purge_standby_list().ok();
}
CleanLevel::Nuclear => {
// Phase 1: same as aggressive
flush_file_cache().ok();
flush_registry_cache().ok();
empty_all_working_sets().ok();
flush_modified_list().ok();
purge_standby_list().ok();
// Phase 2: memory combining (dedup via COW)
combine_memory_pages().ok();
// Phase 3: second pass catches pages modified during combine
flush_modified_list().ok();
purge_standby_list().ok();
}
}
Ok(())
}Note: The code below is a simplified single-file prototype for educational purposes. The actual codebase uses a multi-module architecture (
main.rs,cli.rs,cleaner.rs,stats.rs,display.rs,monitor.rs,ntapi.rs,privilege.rs,console.rs,context_menu.rs) with additional features including smart settle detection, dry-run mode, JSON reports, per-process working set trimming with exclusion filters, continuous monitoring with cooldown, top-N process display,--quiet/--no-coloroutput modes, and more. See Section 12 for the full feature set.
//! MagicX RAM Cleaner — World-class Windows Memory Cleaner CLI
//!
//! Requires: Windows 10/11, Administrator privileges
//! Build: cargo build --release
#![allow(non_snake_case)]
use std::ffi::c_void;
use std::mem;
use std::ptr;
use std::time::Instant;
use anyhow::{anyhow, Context, Result};
use clap::{Parser, Subcommand};
use colored::Colorize;
// ═══════════════════════════════════════════════════════════════════
// windows-sys imports
// ═══════════════════════════════════════════════════════════════════
use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE, LUID};
use windows_sys::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueW, OpenProcessToken,
SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
};
use windows_sys::Win32::System::Memory::SetSystemFileCacheSize;
use windows_sys::Win32::System::ProcessStatus::{
K32EmptyWorkingSet, K32EnumProcesses,
};
use windows_sys::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
use windows_sys::Win32::System::Threading::{
GetCurrentProcess, OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_SET_QUOTA,
};
// ═══════════════════════════════════════════════════════════════════
// NT API constants (not in windows-sys)
// ═══════════════════════════════════════════════════════════════════
const STATUS_SUCCESS: i32 = 0;
const SYSTEM_MEMORY_LIST_INFORMATION: u32 = 0x50;
const SYSTEM_COMBINE_PHYSICAL_MEMORY_INFORMATION: u32 = 0x82;
// ═══════════════════════════════════════════════════════════════════
// SYSTEM_MEMORY_LIST_COMMAND
// ═══════════════════════════════════════════════════════════════════
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
enum MemoryListCommand {
// CaptureAccessedBits = 0,
// CaptureAndResetAccessedBits = 1,
EmptyWorkingSets = 2,
FlushModifiedList = 3,
PurgeStandbyList = 4,
PurgeLowPriorityStandbyList = 5,
}
// ═══════════════════════════════════════════════════════════════════
// NT API structs
// ═══════════════════════════════════════════════════════════════════
#[repr(C)]
#[derive(Debug, Default, Clone)]
struct SystemMemoryListInfo {
zero_page_count: u64,
free_page_count: u64,
modified_page_count: u64,
modified_no_write_page_count: u64,
bad_page_count: u64,
page_count_by_priority: [u64; 8],
repurposed_pages_by_priority: [u64; 8],
modified_page_count_page_file: u64,
}
#[repr(C)]
struct MemoryCombineInformationEx {
handle: isize,
pages_combined: usize,
}
// ═══════════════════════════════════════════════════════════════════
// ntdll.dll FFI
// ═══════════════════════════════════════════════════════════════════
#[link(name = "ntdll")]
extern "system" {
fn NtSetSystemInformation(
SystemInformationClass: u32,
SystemInformation: *mut c_void,
SystemInformationLength: u32,
) -> i32;
fn NtQuerySystemInformation(
SystemInformationClass: u32,
SystemInformation: *mut c_void,
SystemInformationLength: u32,
ReturnLength: *mut u32,
) -> i32;
}
// ═══════════════════════════════════════════════════════════════════
// CLI definition
// ═══════════════════════════════════════════════════════════════════
#[derive(Parser)]
#[command(
name = "magicx-ram-cleaner",
version,
about = "World-class Windows RAM Cleaner",
long_about = "MagicX RAM Cleaner — Frees physical memory on Windows 10/11\n\
Requires Administrator privileges."
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Show detailed memory status
Status,
/// Clean RAM (default: moderate aggressiveness)
Clean {
/// Aggressiveness level: gentle, moderate, aggressive
#[arg(short, long, default_value = "moderate")]
level: String,
},
/// Purge the standby list only
PurgeStandby {
/// Only purge low-priority standby pages
#[arg(short, long)]
low_priority: bool,
},
/// Flush the modified page list to disk
FlushModified,
/// Empty all process working sets
EmptyWorkingSets,
/// Flush the file system cache
FlushCache,
/// Trigger memory page combining/deduplication
Combine,
}
// ═══════════════════════════════════════════════════════════════════
// Privilege management
// ═══════════════════════════════════════════════════════════════════
fn enable_privilege(name: &str) -> Result<()> {
unsafe {
let mut token: HANDLE = 0;
if OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
) == 0
{
return Err(anyhow!("OpenProcessToken failed: 0x{:08X}", GetLastError()));
}
let wide: Vec<u16> = name.encode_utf16().chain(std::iter::once(0)).collect();
let mut luid: LUID = mem::zeroed();
if LookupPrivilegeValueW(ptr::null(), wide.as_ptr(), &mut luid) == 0 {
CloseHandle(token);
return Err(anyhow!(
"LookupPrivilegeValue('{}') failed: 0x{:08X}",
name,
GetLastError()
));
}
let mut tp: TOKEN_PRIVILEGES = mem::zeroed();
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(
token,
0,
&tp,
mem::size_of::<TOKEN_PRIVILEGES>() as u32,
ptr::null_mut(),
ptr::null_mut(),
);
let err = GetLastError();
CloseHandle(token);
if err != 0 {
Err(anyhow!(
"AdjustTokenPrivileges('{}') failed: 0x{:08X}. Run as Administrator!",
name,
err
))
} else {
Ok(())
}
}
}
fn enable_all_privileges() -> Result<()> {
enable_privilege("SeProfileSingleProcessPrivilege")
.context("SeProfileSingleProcessPrivilege")?;
enable_privilege("SeIncreaseQuotaPrivilege")
.context("SeIncreaseQuotaPrivilege")?;
Ok(())
}
// ═══════════════════════════════════════════════════════════════════
// Memory status queries
// ═══════════════════════════════════════════════════════════════════
fn get_memory_status() -> Result<MEMORYSTATUSEX> {
unsafe {
let mut status: MEMORYSTATUSEX = mem::zeroed();
status.dwLength = mem::size_of::<MEMORYSTATUSEX>() as u32;
if GlobalMemoryStatusEx(&mut status) == 0 {
return Err(anyhow!(
"GlobalMemoryStatusEx failed: 0x{:08X}",
GetLastError()
));
}
Ok(status)
}
}
fn query_memory_lists() -> Result<SystemMemoryListInfo> {
unsafe {
let mut info = SystemMemoryListInfo::default();
let mut ret_len: u32 = 0;
let status = NtQuerySystemInformation(
SYSTEM_MEMORY_LIST_INFORMATION,
&mut info as *mut _ as *mut c_void,
mem::size_of::<SystemMemoryListInfo>() as u32,
&mut ret_len,
);
if status != STATUS_SUCCESS {
Err(anyhow!(
"NtQuerySystemInformation(MemoryList) failed: NTSTATUS 0x{:08X}",
status as u32
))
} else {
Ok(info)
}
}
}
// ═══════════════════════════════════════════════════════════════════
// Cleaning operations
// ═══════════════════════════════════════════════════════════════════
fn issue_memory_command(cmd: MemoryListCommand) -> Result<()> {
unsafe {
let mut val = cmd as i32;
let status = NtSetSystemInformation(
SYSTEM_MEMORY_LIST_INFORMATION,
&mut val as *mut i32 as *mut c_void,
mem::size_of::<i32>() as u32,
);
if status != STATUS_SUCCESS {
Err(anyhow!(
"NtSetSystemInformation({:?}) failed: NTSTATUS 0x{:08X}",
cmd,
status as u32
))
} else {
Ok(())
}
}
}
fn flush_file_cache() -> Result<()> {
unsafe {
if SetSystemFileCacheSize(usize::MAX, usize::MAX, 0) == 0 {
Err(anyhow!(
"SetSystemFileCacheSize failed: 0x{:08X}",
GetLastError()
))
} else {
Ok(())
}
}
}
fn combine_memory() -> Result<usize> {
unsafe {
let mut info = MemoryCombineInformationEx {
handle: 0,
pages_combined: 0,
};
let status = NtSetSystemInformation(
SYSTEM_COMBINE_PHYSICAL_MEMORY_INFORMATION,
&mut info as *mut _ as *mut c_void,
mem::size_of::<MemoryCombineInformationEx>() as u32,
);
if status != STATUS_SUCCESS {
Err(anyhow!(
"MemoryCombine failed: NTSTATUS 0x{:08X}",
status as u32
))
} else {
Ok(info.pages_combined)
}
}
}
fn enum_processes_list() -> Result<Vec<u32>> {
unsafe {
let mut pids = vec![0u32; 4096];
let mut bytes_returned: u32 = 0;
if K32EnumProcesses(
pids.as_mut_ptr(),
(pids.len() * mem::size_of::<u32>()) as u32,
&mut bytes_returned,
) == 0
{
return Err(anyhow!("EnumProcesses failed: 0x{:08X}", GetLastError()));
}
let count = bytes_returned as usize / mem::size_of::<u32>();
pids.truncate(count);
Ok(pids)
}
}
fn empty_all_ws_per_process() -> Result<(u32, u32)> {
let pids = enum_processes_list()?;
let (mut ok, mut fail) = (0u32, 0u32);
for &pid in &pids {
if pid == 0 {
continue;
}
unsafe {
let h = OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, 0, pid);
if h == 0 {
fail += 1;
continue;
}
if K32EmptyWorkingSet(h) != 0 {
ok += 1;
} else {
fail += 1;
}
CloseHandle(h);
}
}
Ok((ok, fail))
}
// ═══════════════════════════════════════════════════════════════════
// Display helpers
// ═══════════════════════════════════════════════════════════════════
fn bytes_to_mb(b: u64) -> f64 {
b as f64 / 1024.0 / 1024.0
}
fn pages_to_mb(pages: u64) -> f64 {
(pages * 4096) as f64 / 1024.0 / 1024.0
}
fn print_status() -> Result<()> {
let mem = get_memory_status()?;
let lists = query_memory_lists().ok();
println!("{}", "═══ Memory Status ═══".bold().cyan());
println!(
"Memory Load: {}",
format!("{}%", mem.dwMemoryLoad).yellow()
);
println!(
"Total Physical: {:.0} MB",
bytes_to_mb(mem.ullTotalPhys)
);
println!(
"Available: {} MB",
format!("{:.0}", bytes_to_mb(mem.ullAvailPhys)).green()
);
println!(
"In Use: {} MB",
format!(
"{:.0}",
bytes_to_mb(mem.ullTotalPhys - mem.ullAvailPhys)
)
.red()
);
println!("Commit Total: {:.0} MB", bytes_to_mb(mem.ullTotalPageFile));
println!("Commit Available: {:.0} MB", bytes_to_mb(mem.ullAvailPageFile));
if let Some(info) = lists {
let total_standby: u64 = info.page_count_by_priority.iter().sum();
println!();
println!("{}", "═══ Page Lists ═══".bold().cyan());
println!("Free pages: {:.0} MB", pages_to_mb(info.free_page_count));
println!("Zero pages: {:.0} MB", pages_to_mb(info.zero_page_count));
println!(
"Modified pages: {:.0} MB",
pages_to_mb(info.modified_page_count)
);
println!(
"Standby total: {} MB",
format!("{:.0}", pages_to_mb(total_standby)).yellow()
);
for (i, &count) in info.page_count_by_priority.iter().enumerate() {
if count > 0 {
println!(" Standby P{}: {:.0} MB", i, pages_to_mb(count));
}
}
}
Ok(())
}
fn step_ok(name: &str) {
println!(" {} {}", "[OK]".green().bold(), name);
}
fn step_fail(name: &str, e: &anyhow::Error) {
println!(" {} {} — {}", "[FAIL]".red().bold(), name, e);
}
// ═══════════════════════════════════════════════════════════════════
// Main
// ═══════════════════════════════════════════════════════════════════
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Status => {
print_status()?;
}
Commands::Clean { ref level } => {
enable_all_privileges().ok();
let before = get_memory_status()?;
let start = Instant::now();
println!(
"{} (level: {})",
"═══ Cleaning RAM ═══".bold().cyan(),
level.yellow()
);
match level.as_str() {
"gentle" => {
match issue_memory_command(MemoryListCommand::FlushModifiedList) {
Ok(_) => step_ok("Flush modified list"),
Err(e) => step_fail("Flush modified list", &e),
}
match combine_memory() {
Ok(n) => step_ok(&format!("Memory combine ({} pages)", n)),
Err(e) => step_fail("Memory combine", &e),
}
}
"moderate" => {
match flush_file_cache() {
Ok(_) => step_ok("Flush file system cache"),
Err(e) => step_fail("Flush file system cache", &e),
}
match issue_memory_command(MemoryListCommand::FlushModifiedList) {
Ok(_) => step_ok("Flush modified list"),
Err(e) => step_fail("Flush modified list", &e),
}
match issue_memory_command(MemoryListCommand::PurgeLowPriorityStandbyList)
{
Ok(_) => step_ok("Purge low-priority standby"),
Err(e) => step_fail("Purge low-priority standby", &e),
}
match combine_memory() {
Ok(n) => step_ok(&format!("Memory combine ({} pages)", n)),
Err(e) => step_fail("Memory combine", &e),
}
}
"aggressive" => {
match flush_file_cache() {
Ok(_) => step_ok("Flush file system cache"),
Err(e) => step_fail("Flush file system cache", &e),
}
match issue_memory_command(MemoryListCommand::EmptyWorkingSets) {
Ok(_) => step_ok("Empty all working sets (kernel)"),
Err(e) => step_fail("Empty working sets", &e),
}
match issue_memory_command(MemoryListCommand::FlushModifiedList) {
Ok(_) => step_ok("Flush modified list"),
Err(e) => step_fail("Flush modified list", &e),
}
match issue_memory_command(MemoryListCommand::PurgeLowPriorityStandbyList)
{
Ok(_) => step_ok("Purge low-priority standby"),
Err(e) => step_fail("Purge low-priority standby", &e),
}
match issue_memory_command(MemoryListCommand::PurgeStandbyList) {
Ok(_) => step_ok("Purge ALL standby list"),
Err(e) => step_fail("Purge standby list", &e),
}
match combine_memory() {
Ok(n) => step_ok(&format!("Memory combine ({} pages)", n)),
Err(e) => step_fail("Memory combine", &e),
}
}
other => {
return Err(anyhow!(
"Unknown level '{}'. Use: gentle, moderate, aggressive",
other
));
}
}
let after = get_memory_status()?;
let freed = after.ullAvailPhys.saturating_sub(before.ullAvailPhys);
let elapsed = start.elapsed();
println!();
println!(
"Freed: {} MB in {:.1}s",
format!("{:.0}", bytes_to_mb(freed)).green().bold(),
elapsed.as_secs_f64()
);
println!(
"Available now: {} MB / {:.0} MB",
format!("{:.0}", bytes_to_mb(after.ullAvailPhys))
.green()
.bold(),
bytes_to_mb(after.ullTotalPhys)
);
}
Commands::PurgeStandby { low_priority } => {
enable_privilege("SeProfileSingleProcessPrivilege")?;
if low_priority {
issue_memory_command(MemoryListCommand::PurgeLowPriorityStandbyList)?;
println!("{}", "Low-priority standby list purged.".green());
} else {
issue_memory_command(MemoryListCommand::PurgeStandbyList)?;
println!("{}", "ALL standby list purged.".green());
}
}
Commands::FlushModified => {
enable_privilege("SeProfileSingleProcessPrivilege")?;
issue_memory_command(MemoryListCommand::FlushModifiedList)?;
println!("{}", "Modified page list flushed.".green());
}
Commands::EmptyWorkingSets => {
enable_privilege("SeProfileSingleProcessPrivilege")?;
issue_memory_command(MemoryListCommand::EmptyWorkingSets)?;
println!("{}", "All working sets emptied.".green());
}
Commands::FlushCache => {
enable_privilege("SeIncreaseQuotaPrivilege")?;
flush_file_cache()?;
println!("{}", "File system cache flushed.".green());
}
Commands::Combine => {
enable_privilege("SeProfileSingleProcessPrivilege")?;
let pages = combine_memory()?;
println!(
"Memory combining complete: {} pages ({:.1} MB) deduplicated.",
pages,
pages_to_mb(pages as u64)
);
}
}
Ok(())
}To auto-request elevation (UAC prompt) when the exe is launched, embed a Windows application manifest.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>fn main() {
// Embed the manifest for admin elevation
println!("cargo:rustc-link-arg-bins=/MANIFEST:EMBED");
println!("cargo:rustc-link-arg-bins=/MANIFESTINPUT:{}",
std::path::Path::new("app.manifest").canonicalize().unwrap().display());
// Alternative: use the embed-manifest crate
// Or use winres crate:
}Or use the embed-manifest crate (simpler):
[build-dependencies]
embed-manifest = "1.4"// build.rs
use embed_manifest::{embed_manifest, new_manifest, ExecutionLevel};
fn main() {
embed_manifest(new_manifest("MagicX RAM Cleaner")
.requested_execution_level(ExecutionLevel::RequireAdministrator))
.expect("Failed to embed manifest");
}| NTSTATUS | Name | Meaning |
|---|---|---|
0x00000000 |
STATUS_SUCCESS |
Operation succeeded |
0xC0000022 |
STATUS_ACCESS_DENIED |
Missing required privilege — run as admin and call enable_privilege() |
0xC0000004 |
STATUS_INFO_LENGTH_MISMATCH |
Buffer too small for query result |
0xC000000D |
STATUS_INVALID_PARAMETER |
Invalid command value passed |
0xC0000003 |
STATUS_INVALID_INFO_CLASS |
Invalid SystemInformationClass value |
To find which feature flag provides a specific function or type:
- Go to https://microsoft.github.io/windows-docs-rs/
- Search for the function name (e.g.,
GlobalMemoryStatusEx) - The docs show the required feature at the top of the page
Or use cargo doc --open on windows-sys to browse locally.
This appendix previously documented the
sysinfocrate as an alternative for display-only statistics. The project now usesGlobalMemoryStatusExandK32GetPerformanceInfodirectly viawindows-sys, withCreateToolhelp32Snapshotfor per-process enumeration. No third-party system info crate is needed.
The codebase has evolved from the simplified prototype in Section 11 into a multi-module architecture with the following structure:
build.rs — embeds admin-elevation manifest, application icon, Phosphor context-menu
sub-icons (IDs 2–6), and version metadata via winresource
assets/
app.ico — multi-size application icon (16–256 px) embedded as resource ID 1
app.png — PNG version of the app icon used as the egui window icon
src/
main.rs — thin entry point: mod declarations, main(), run(), command dispatch
lib.rs — library crate root: module re-exports for criterion benchmarks
cli.rs — clap Parser, Commands enum, help text constants, STYLES
cleaner.rs — cleaning operations & orchestration (smart_clean, CleanLevel)
console.rs — Windows console management (dynamic attach/alloc for SUBSYSTEM:WINDOWS,
ANSI, notifications, dark-mode detection, title-bar theming)
context_menu.rs — Windows Desktop context menu integration (registry install/uninstall)
display.rs — ALL terminal formatting: banner, status, clean output, box drawing
gui/ — egui graphical interface module
mod.rs — module entry point, run_gui() launcher, single-instance guard
app.rs — core app state, eframe::App impl, sidebar, layout routing
persistence.rs — settings file I/O, Win32 file dialogs, autostart registry
theme.rs — colour palette, spacing constants, dark/light Visuals
tray.rs — system tray icon with context menu and Phosphor glyph icons
widgets.rs — reusable UI components (cards, stat labels, toggle switch)
panels/ — one file per tab
mod.rs — panel module re-exports
about.rs — app info, developer profile, project details
dashboard.rs — memory overview + one-click cleaning buttons
monitor.rs — auto-clean configuration UI
processes.rs — sortable grouped process memory table
settings.rs — appearance, integration, backup & restore
monitor.rs — continuous monitoring loop, Ctrl+C handler, auto-clean
ntapi.rs — NT kernel FFI (NtSetSystemInformation, NtQuerySystemInformation)
privilege.rs — Windows privilege elevation (Se*Privilege) + admin check
stats.rs — memory statistics, Win32 API calls, MemorySnapshot
| Type | Module | Purpose |
|---|---|---|
CleanLevel |
cleaner |
Enum: Gentle, Moderate, Aggressive, Nuclear |
CleanResult |
cleaner |
Per-operation result: freed_bytes, message, success, elapsed_secs |
SmartCleanResult |
cleaner |
Aggregate: results[], overall_before/overall_after, total_freed, total_elapsed_secs |
SettleMode |
cleaner |
Enum: Full (3 stable reads, 20 polls max) / Quick (1 stable read, 8 polls) |
MemoryListCommand |
ntapi |
Enum mapping NT kernel commands (with display_info() for dry-run labels) |
MemorySnapshot |
stats |
Full memory state (physical + page file + kernel pools + system counters) |
QuickMemoryReading |
stats |
Lightweight snapshot (total_physical + available_physical only) |
MemoryListInfo |
stats |
Kernel page list breakdown (standby priorities, modified, free, zeroed, bad) |
ProcessMemoryInfo |
stats |
Per-process memory info (PID, name, working set, peak, private working set) |
FileCacheSnapshot |
stats |
File system cache working set (current, peak, min/max limits) |
| Feature | Flag / Option | Description |
|---|---|---|
| GUI mode | (no arguments) | Launch the graphical interface with dashboard, charts, and settings |
| Smart clean | clean [-l LEVEL] |
Context-aware cleaning at 4 levels with settle detection between operations |
| Dry-run mode | clean --dry-run |
Preview what operations would run without executing them |
| JSON report | clean --report FILE |
Write SmartCleanResult as pretty-printed JSON to a file |
| Process exclusion | clean --exclude NAME |
Case-insensitive process name exclusion (implies per-process working set trim) |
| Status display | status [--detailed] |
Show memory usage with optional kernel page list breakdown |
| JSON status | status --json |
Machine-readable JSON output of MemorySnapshot |
| Top processes | status --top N |
Show top N processes ranked by private working set (physical RAM) usage |
| Per-process trimming | empty-workingsets -p |
Trim working sets process-by-process instead of kernel-wide |
| Exclusion filters | --exclude NAME |
Case-insensitive process name exclusion (implies --per-process) |
| Continuous monitoring | monitor -t THRESHOLD |
Auto-clean when RAM usage exceeds threshold percentage |
| Monitor cooldown | monitor --cooldown SECS |
Minimum seconds between auto-cleans (default: 2× interval) |
| Quiet mode | -q / --quiet |
Suppress banner and non-essential output (global flag) |
| No colour | --no-color |
Disable coloured terminal output (also respects NO_COLOR env var) |
| Notify mode | --notify |
Skip console attachment, run silently, show balloon notification with results |
| Context menu install | context-menu install |
Add Desktop right-click submenu for quick cleaning access |
| Context menu remove | context-menu uninstall |
Remove Desktop right-click submenu |
After each kernel memory operation, wait_for_settle() polls QuickMemoryReading
at 100 ms intervals to detect when the available memory has stabilised. This prevents
measuring freed memory before the kernel has finished reclaiming pages.
Both modes use the same jitter threshold formula: 0.01% of total physical RAM with a 4 MB floor (e.g. ~1.6 MB on 16 GB, ~13 MB on 128 GB). The modes differ in how many consecutive stable readings are required and how long they poll:
SettleMode::Full— 3 consecutive stable reads, up to 20 polls (2 s max). Used for the final operation in a chain and for standalone commands.SettleMode::Quick— 1 stable read, up to 8 polls (0.8 s max). Used for intermediate operations insmart_cleanwhere only per-op deltas are needed.
Once settled, a full MemorySnapshot::capture() is taken for the after-measurement.
All kernel memory operations share a common execute_kernel_memory_op() helper that:
- Captures a full
MemorySnapshotbefore the kernel call - Calls
ntapi::execute_memory_command()to issue the NT kernel operation - Calls
wait_for_settle()with the appropriateSettleMode(which captures the after snapshot) - Computes
freed_bytes(delta ofavailable_physical) and wall-clock elapsed time - Returns a structured
CleanResultwith before/after snapshots, timing, and status
Display strings (operation name, success message, verbose label) come from
MemoryListCommand::display_info(), so callers only pass the command variant.
This eliminates code duplication across flush_modified_list, purge_standby_all,
purge_standby_low_priority, and empty_working_sets_kernel.
Non-kernel operations (flush_file_cache, flush_registry_cache, combine_memory)
follow the same capture → execute → settle → result pattern but with their own
Win32/NT API calls instead of execute_memory_command().