Skip to content
Closed
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
11 changes: 11 additions & 0 deletions electron/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Node / Electron
node_modules/
dist/
out/

# .NET engine build output
engine/bin/
engine/obj/

# Screenshots / scratch
build/screenshot-*.png
87 changes: 87 additions & 0 deletions electron/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Windows Memory Cleaner — Electron edition

A modern **Electron** desktop front-end for [WinMemoryCleaner](https://github.com/IgorMundstein/WinMemoryCleaner),
with a redesigned "Liquid Glass" interface. All the original kernel-level memory work is
**preserved unchanged** — it runs in a native C# helper process that Electron drives over a
JSON protocol.

## Why this architecture

WinMemoryCleaner's core is not web code: it calls Windows kernel APIs via P/Invoke
(`EmptyWorkingSet`, `NtSetSystemInformation`, `SetSystemFileCacheSize`, `DeviceIoControl`, …),
which Node.js/Chromium cannot do. So the app is split cleanly:

```
┌─────────────────────────────┐ newline-delimited JSON over stdio ┌──────────────────────────┐
│ Electron (renderer + main) │ ───────────────────────────────────▶ │ wmc-engine.exe (C#) │
│ UI · tray · hotkey · sched │ ◀─────────────────────────────────── │ P/Invoke · admin │
└─────────────────────────────┘ requests / events └───────────┬──────────────┘
│
Windows kernel APIs
```

| Layer | Path | Responsibility |
|-------|------|----------------|
| **Engine** | `engine/` | Memory stats, OS capability detection, the 8 optimization routines (ported 1:1 from `ComputerService`), process list, and settings persistence to the **original** `HKLM\SOFTWARE\WinMemoryCleaner` registry keys. |
| **Main** | `main/main.js`, `main/engine.js` | Window lifecycle, tray, global optimize hotkey, auto-optimization scheduler (interval + low-memory threshold), notifications, run-on-startup, engine spawn + protocol. |
| **Preload** | `preload/preload.js` | The only bridge to the renderer — a minimal `window.wmc` API via `contextBridge`. `contextIsolation` on, `nodeIntegration` off. |
| **Renderer** | `renderer/` | The redesigned UI (vanilla HTML/CSS/JS, no bundler). |

### Data compatibility
The engine reads and writes the **same registry location** the original app uses, so any
existing configuration (memory areas, language, auto-optimization, exclusions) carries over
untouched.

## Requirements
- Windows 10/11
- [.NET SDK 8+](https://dotnet.microsoft.com/) (built and tested with SDK 10)
- [Node.js 18+](https://nodejs.org/)

## Develop / run

```sh
cd electron
npm install
npm run build:engine # compiles wmc-engine (dotnet)
npm start # launches Electron
```

The engine binary is manifested `requireAdministrator`, so **full optimization requires the
app to run elevated** (right-click → Run as administrator, or launch from an elevated shell).
For convenience during development, if the app is not elevated the bridge automatically falls
back to launching the engine via `dotnet <dll>` (which bypasses the manifest). In that mode the
UI, live memory and settings all work; the privileged optimizations simply report a per-area
error instead of succeeding — exactly like the original app when not elevated.

Force the dotnet fallback explicitly:

```sh
WMC_ENGINE_VIA_DOTNET=1 npm start
```

## Package

```sh
npm run build:engine
npm run dist # electron-builder → NSIS installer, requestedExecutionLevel=requireAdministrator
```

`electron-builder` bundles the compiled engine from `engine/bin/Release/net10.0-windows`
into the installer's `resources/engine` folder.

## JSON protocol (engine)

Requests are newline-delimited JSON on stdin; responses/events on stdout.

| Command | Args | Result |
|---------|------|--------|
| `ping` | – | `{ pong, version, elevated }` |
| `getState` | – | `{ memory, os, settings, elevated }` |
| `getMemory` | – | `{ physical, virtual }` (bytes + percentages) |
| `getProcesses` | – | `string[]` of process names |
| `getSettings` / `saveSettings` | (settings) | reads/writes the original registry keys |
| `setPriority` | `{ priority: "low"\|"normal" }` | sets the engine process priority |
| `optimize` | `{ areas:int, processExclusion:[], reason }` | streams `optimizeStart`/`progress` events, returns `{ before, after, released, results[] }` |

## License
GPL-3.0, same as the original project.
Binary file added electron/build/icon.ico
Binary file not shown.
118 changes: 118 additions & 0 deletions electron/engine/MemoryInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace WinMemoryCleaner.Engine
{
/// <summary>
/// Memory areas (bit flags). Values match the original Enums.Memory.Areas exactly,
/// so persisted settings remain compatible.
/// </summary>
[Flags]
internal enum MemoryAreas
{
None = 0,
CombinedPageList = 1,
ModifiedFileCache = 2,
ModifiedPageList = 4,
RegistryCache = 8,
StandbyList = 16,
StandbyListLowPriority = 32,
SystemFileCache = 64,
WorkingSet = 128
}

internal enum OptimizationReason
{
LowMemory,
Manual,
Schedule
}

/// <summary>
/// Operating-system capability detection, ported from the original ComputerService/OperatingSystem.
/// </summary>
[SupportedOSPlatform("windows")]
internal sealed class OsInfo
{
public bool Is64Bit { get; }
public bool IsWindowsXpOrGreater { get; }
public bool IsWindowsVistaOrGreater { get; }
public bool IsWindows7OrGreater { get; }
public bool IsWindows8OrGreater { get; }
public bool IsWindows81OrGreater { get; }

public bool HasWorkingSet => IsWindowsXpOrGreater;
public bool HasSystemFileCache => IsWindowsXpOrGreater;
public bool HasModifiedFileCache => IsWindowsXpOrGreater;
public bool HasModifiedPageList => IsWindowsVistaOrGreater;
public bool HasStandbyList => IsWindowsVistaOrGreater;
public bool HasCombinedPageList => IsWindows8OrGreater;
public bool HasRegistryHive => IsWindows81OrGreater;

public OsInfo()
{
var v = Environment.OSVersion.Version;
Is64Bit = Environment.Is64BitOperatingSystem;
IsWindowsXpOrGreater = v.Major >= 5.1;
IsWindowsVistaOrGreater = v.Major >= 6;
IsWindows7OrGreater = (v.Major > 6) || (v.Major == 6 && v.Minor >= 1);
IsWindows8OrGreater = v.Major >= 6.2;
IsWindows81OrGreater = v.Major >= 6.3;
}

/// <summary>Which areas the OS supports, as a bit mask.</summary>
public MemoryAreas SupportedAreas
{
get
{
var a = MemoryAreas.None;
if (HasWorkingSet) a |= MemoryAreas.WorkingSet;
if (HasSystemFileCache) a |= MemoryAreas.SystemFileCache;
if (HasModifiedFileCache) a |= MemoryAreas.ModifiedFileCache;
if (HasModifiedPageList) a |= MemoryAreas.ModifiedPageList;
if (HasStandbyList) a |= MemoryAreas.StandbyList | MemoryAreas.StandbyListLowPriority;
if (HasCombinedPageList) a |= MemoryAreas.CombinedPageList;
if (HasRegistryHive) a |= MemoryAreas.RegistryCache;
return a;
}
}
}

/// <summary>Point-in-time RAM snapshot from GlobalMemoryStatusEx.</summary>
[SupportedOSPlatform("windows")]
internal readonly struct MemorySnapshot
{
public long PhysicalTotal { get; }
public long PhysicalFree { get; }
public long PhysicalUsed { get; }
public int PhysicalUsedPercent { get; }

public long VirtualTotal { get; }
public long VirtualFree { get; }
public long VirtualUsed { get; }
public int VirtualUsedPercent { get; }

private MemorySnapshot(Structs.Windows.MemoryStatusEx m)
{
PhysicalTotal = m.TotalPhys;
PhysicalFree = m.AvailPhys;
PhysicalUsed = m.TotalPhys >= m.AvailPhys ? m.TotalPhys - m.AvailPhys : 0;
PhysicalUsedPercent = m.MemoryLoad;

VirtualTotal = m.TotalPageFile;
VirtualFree = m.AvailPageFile;
VirtualUsed = m.TotalPageFile >= m.AvailPageFile ? m.TotalPageFile - m.AvailPageFile : 0;
VirtualUsedPercent = m.TotalPageFile > 0 ? (int)(VirtualUsed * 100 / m.TotalPageFile) : 0;
}

public static MemorySnapshot Read()
{
var m = new Structs.Windows.MemoryStatusEx();
if (!NativeMethods.GlobalMemoryStatusEx(m))
throw new Win32Exception(Marshal.GetLastWin32Error());
return new MemorySnapshot(m);
}
}
}
Loading
Loading