Skip to content
Merged
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
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@ All notable changes to this project are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [6.4.0] - 2026-11-05

A redesign of the desktop app: a start page, a calmer layout, Russian, and no frozen window.

### Added
- **Overview page** (the new start page): disks with free-space bars, last cleanup, applied
tweaks, startup apps, recent runs — and **Smart scan**, which measures the junk and runs the
read-only health checks in one go, with *Clean now* / *Review* on the result.
- **Russian UI** (`WinSenior.Gui.ru.json`): every screen, dialog, task, tweak and check name.
Follows the Windows display language; switch under *Settings & about*.
- Lists **grouped by category** with collapsible headers and *select all / none* per group;
the name column fills the window.
- **Result banner** after every run (freed space, problems found, errors, what to do next).
- **Themed confirmation dialogs** instead of the grey message boxes.
- *Restart as administrator* banner when the app runs with limited rights.
- Keyboard shortcuts (Ctrl+1…9, Ctrl+F, F5, Ctrl+L, Esc), taskbar progress, dark title bar,
window size/position and log-drawer state remembered.
- Schedule page shows each task's state, next and last run (and what Install will set up).

### Changed
- The log is a drawer behind the *Log* button instead of a permanent third of the window.
- Cleanup options live behind an *Options* button, each with a one-line explanation.
- Startup apps use on/off switches; History opens a run as a web page on double-click.
- Dark scroll bars, list headers, check boxes and inputs throughout.

### Fixed
- **The Optimize page froze the window for ~8 s** while it read the tweak state (the
ScheduledTasks module alone loads in ~3 s). The state is now read in a background runspace.
- **Purge standby memory never worked**: the `TOKEN_PRIVILEGES` struct put the LUID at
offset 8 instead of 4, so the privilege was never enabled — and the task still logged
"RAM freed". It now enables the privilege correctly and reports failures.
- `Cleanup -Category Browsers,DevTools` through `-File` (scheduler, GPO) selected nothing:
the list arrived as one string and was not split.
- The monthly scheduled task failed to register where the time separator is not `:`
(fi-FI, da-DK under PowerShell 7).
- The web installer removed the old version before moving the new one in place, which fails
across drives on Windows PowerShell 5.1 and left no app; it now copies next to the target
and swaps by renaming.
- Console menu: long checklists (100+ cleanup tasks) now scroll around the cursor instead of
running off the window, and frames are painted where the screen was cleared, not at buffer
row 0. *Create restore point* logs to the cleanup log instead of the repair log.

## [6.3.0] - 2026-09-23

A correctness and safety release: every engine was audited against Microsoft's
Expand Down
37 changes: 27 additions & 10 deletions Cleanup-Windows-Senior.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ if ($DryRun) { $WhatIfPreference = $true }
$Include = @($Include | ForEach-Object { "$_" -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$Exclude = @($Exclude | ForEach-Object { "$_" -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$Drives = @($Drives | ForEach-Object { "$_" -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$Category = @($Category | ForEach-Object { "$_" -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ })

# Paths the engine must never operate on, no matter what a task or env var says.
$script:DenyList = @(
Expand Down Expand Up @@ -1069,29 +1070,45 @@ function Get-CleanupTaskRegistry {
New-CleanupTask memory-standby 'Purge standby memory list & trim working sets (frees RAM, not disk)' System Safe -DefaultOn $false -Action {
# Same documented NtSetSystemInformation(SystemMemoryListInformation) call RAMMap uses.
if (Test-WhatIfMode) { Write-CleanupLog '[WhatIf] would purge standby memory list' 'WhatIf'; return $null }
if (-not ('WinSenior.Memory' -as [type])) {
Add-Type -Namespace WinSenior -Name Memory -MemberDefinition @'
# TOKEN_PRIVILEGES is { DWORD Count; LUID Luid; DWORD Attributes } with 4-byte packing:
# the LUID sits at offset 4 (a C# 'long' field would be aligned to 8 and the
# privilege silently never enabled), so it is two 32-bit halves. Type name
# bumped (Memory2) so a session that loaded the old layout still gets this one.
if (-not ('WinSenior.Memory2' -as [type])) {
Add-Type -Namespace WinSenior -Name Memory2 -MemberDefinition @'
[DllImport("ntdll.dll")] public static extern int NtSetSystemInformation(int cls, ref int info, int len);
[DllImport("advapi32.dll", SetLastError=true)] public static extern bool OpenProcessToken(IntPtr h, int acc, out IntPtr tok);
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern bool LookupPrivilegeValue(string s, string n, out long luid);
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern bool LookupPrivilegeValue(string s, string n, out LUID luid);
[DllImport("advapi32.dll", SetLastError=true)] public static extern bool AdjustTokenPrivileges(IntPtr tok, bool dis, ref TOKP np, int len, IntPtr prev, IntPtr ret);
[StructLayout(LayoutKind.Sequential)] public struct TOKP { public int Count; public long Luid; public int Attr; }
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
[StructLayout(LayoutKind.Sequential)] public struct LUID { public uint Low; public int High; }
[StructLayout(LayoutKind.Sequential, Pack = 4)] public struct TOKP { public int Count; public LUID Luid; public int Attr; }
public static bool Enable(string name) {
IntPtr tok; if (!OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, 0x28, out tok)) return false;
TOKP tp; tp.Count = 1; tp.Attr = 2; if (!LookupPrivilegeValue(null, name, out tp.Luid)) return false;
return AdjustTokenPrivileges(tok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
try {
TOKP tp; tp.Count = 1; tp.Attr = 2; if (!LookupPrivilegeValue(null, name, out tp.Luid)) return false;
// AdjustTokenPrivileges returns TRUE even when nothing was assigned; ERROR_NOT_ALL_ASSIGNED = 1300.
return AdjustTokenPrivileges(tok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero) && Marshal.GetLastWin32Error() != 1300;
} finally { CloseHandle(tok); }
}
'@ -ErrorAction SilentlyContinue
}
if (-not ('WinSenior.Memory' -as [type])) { return $null }
[void][WinSenior.Memory]::Enable('SeProfileSingleProcessPrivilege')
[void][WinSenior.Memory]::Enable('SeIncreaseQuotaPrivilege')
if (-not ('WinSenior.Memory2' -as [type])) { return $null }
if (-not [WinSenior.Memory2]::Enable('SeProfileSingleProcessPrivilege')) {
Write-CleanupLog ' Could not enable SeProfileSingleProcessPrivilege - standby list not purged' 'Warning'
return $null
}
[void][WinSenior.Memory2]::Enable('SeIncreaseQuotaPrivilege')
$before = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory * 1KB
$failed = @()
foreach ($cmd in 4, 2) { # 4 = purge standby list, 2 = empty working sets
$v = [int]$cmd
[void][WinSenior.Memory]::NtSetSystemInformation(80, [ref]$v, 4)
$st = [WinSenior.Memory2]::NtSetSystemInformation(80, [ref]$v, 4)
if ($st -ne 0) { $failed += ('{0} (NTSTATUS 0x{1:X8})' -f $cmd, $st) }
}
$after = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory * 1KB
if ($failed.Count -eq 2) { Write-CleanupLog " Memory purge failed: $($failed -join ', ')" 'Warning'; return $null }
if ($failed.Count) { Write-CleanupLog " Memory purge partly failed: $($failed -join ', ')" 'Warning' }
Write-CleanupLog (" RAM freed: {0}" -f (Format-FileSize ([Math]::Max([int64]0, [int64]($after - $before))))) 'Success'
$null
}
Expand Down
65 changes: 38 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Windows System Cleaner and Optimizer 🧹

[![CI](https://github.com/denfry/WindowsCleaner/actions/workflows/ci.yml/badge.svg)](https://github.com/denfry/WindowsCleaner/actions/workflows/ci.yml)
[![Version](https://img.shields.io/badge/version-6.3.0-blue.svg)](https://github.com/denfry/WindowsCleaner)
[![Version](https://img.shields.io/badge/version-6.4.0-blue.svg)](https://github.com/denfry/WindowsCleaner)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![PowerShell](https://img.shields.io/badge/powershell-5.1%2B%20%7C%207%2B-blue.svg)](https://learn.microsoft.com/powershell/)
[![Platform](https://img.shields.io/badge/platform-Windows%2010%20%7C%2011-blue.svg)](https://www.microsoft.com/windows/)
Expand Down Expand Up @@ -81,31 +81,42 @@ Administrator once (UAC) and opens **Windows Senior** without a console window.
`.\WinSenior.ps1 -Gui` does the same from PowerShell; `.\WinSenior.cmd console` opens
the arrow-key menu instead.

The app has eight pages:

- **Disk cleanup** — every task with its category and risk badge. *Scan* runs the real
`-WhatIf` dry run and fills a "Would free" column per task; *Clean now* runs the checked
tasks. Options: current user only, restore point, delete locked files at reboot, close
running browsers, skip SFC/DISM, Conservative, minimum file age, which disks. Dangerous
tasks are never pre-checked, and *Clean now* shows a confirmation with the estimate first.
A filter box, sortable columns and tooltips with each task's paths on every list.
- **Optimize** — all tweaks with their live *applied / not applied* state; preview or apply,
restore point first, every applied tweak is backed up.
- **Troubleshoot** — read-only scan with OK / Warn / Fail per check and the detail text;
tick the problems to repair and press *Fix selected*, or auto-fix everything.
- **Startup apps** — everything that starts with Windows (Run keys, Startup folders);
untick to disable it exactly the way Task Manager does — the entry is kept, so ticking it
again restores it.
- **History** — every run's report (app and scheduled), all-time total freed, HTML export.
- **Undo & restore** — list of optimization backup manifests (undo newest or a chosen one),
create a System Restore point, open System Restore, open log folders.
- **Schedule** — install/remove the recurring Task Scheduler jobs and see their next run.
- **About** — versions, counts, paths.

Engine output streams into the log panel at the bottom (resizable, saveable, cancellable).
The app never re-implements deletion: it launches the same engine scripts with parameters,
so `-WhatIf`, the safety guard, restore points and undo are exactly the ones documented below.
Selections and options persist in `%ProgramData%\WinSenior\gui-settings.json`.
The app speaks **English and Russian** (it follows the Windows display language; switch it
under *Settings*). It has nine pages:

- **Overview** — the start page: computer, Windows build and uptime, a free-space bar per
disk, the last cleanup, how many tweaks are applied and how many apps start with Windows,
plus the recent runs. **Smart scan** measures the junk (cleanup dry run) and runs the
read-only health checks in one go; *Clean now* and *Review* act on the result.
- **Disk cleanup** — tasks grouped by category (collapsible, with *select all / none* per
group) and a risk badge each. *Scan* runs the real `-WhatIf` dry run and fills a
"Would free" column; *Clean now* runs the ticked tasks after a confirmation with the
estimate. *Options* opens restore point, close running browsers, delete locked files at
reboot, Conservative, current user only, skip SFC/DISM, minimum file age and disks.
Dangerous tasks are never pre-ticked. Hover a task to see exactly which folders it touches.
- **Optimize** — tweaks grouped by area with their live *Applied / Not applied* state (read
in the background, the window never freezes); preview or apply, restore point first, every
applied tweak is backed up.
- **Troubleshoot** — read-only scan with problem / warning / healthy counters and the detail
per check; tick the problems to repair and press *Fix selected*, or fix everything.
- **Startup apps** — everything that starts with Windows (Run keys, Startup folders) with an
on/off switch that disables it exactly the way Task Manager does — the entry is kept, so
switching it on restores it.
- **History** — every run's report (app and scheduled), all-time total freed; double-click a
run to open it as a web page.
- **Undo & restore** — optimization backups (undo newest or a chosen one), create a System
Restore point, open System Restore, open log folders.
- **Schedule** — install/remove the recurring Task Scheduler jobs, see their state, next and
last run.
- **Settings & about** — language, versions, counts, paths, keyboard shortcuts.

After every run a banner sums up the result; the engine output streams into a log drawer
(*Log* button or Ctrl+L — resizable, saveable). Shortcuts: Ctrl+1…9 switch pages, Ctrl+F
search, F5 scan/refresh. Without administrator rights a banner offers *Restart as
administrator*. The app never re-implements deletion: it launches the same engine scripts
with parameters, so `-WhatIf`, the safety guard, restore points and undo are exactly the
ones documented below. Selections, options, language and window size persist in
`%ProgramData%\WinSenior\gui-settings.json`.

## The console menu

Expand Down Expand Up @@ -383,7 +394,7 @@ Every engine's `-ReportPath` writes the same envelope, so one parser reads them

```json
{
"Tool": "WinSenior", "Version": "6.3.0", "Engine": "Cleanup",
"Tool": "WinSenior", "Version": "6.4.0", "Engine": "Cleanup",
"Host": "PC01", "Timestamp": "2026-06-24T03:00:11", "Mode": "Live",
"RestorePoint": true, "DurationSec": 42.3,
"Summary": { "TotalFreed": "1.20 GB", "TotalFiles": 8123, "TotalErrors": 2 },
Expand Down
4 changes: 2 additions & 2 deletions WinSenior.Common.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

.NOTES
Author : denfry (https://github.com/denfry/WindowsCleaner)
Version : 6.3.0
Version : 6.4.0
#>

# When an engine's stdout is redirected (desktop app, scheduler, CI) emit UTF-8
Expand Down Expand Up @@ -165,7 +165,7 @@ function Set-WsRestoreThrottle {
# DurationSec; engine-specific counters go in Summary, the per-unit list
# in Items. No-op without -ReportPath.
# =====================================================================
function Get-WinSeniorVersion { '6.3.0' }
function Get-WinSeniorVersion { '6.4.0' }

function Write-WinSeniorReport {
param(
Expand Down
Loading