Skip to content

Commit 2885e5f

Browse files
carterscodeclaude
andcommitted
fix: critical XAML binding bug + clean up temp file lifecycle
User looked at %TEMP% and asked what was there. Found three real issues, all fixed. 1. CRITICAL: ApplyResultsWindow XAML had Run.Text="{Binding Before}" etc. on read-only properties of ResultRow. WPF's default Run.Text binding is TwoWay, which fails on get-only properties with the error "A TwoWay or OneWayToSource binding cannot work on the read-only property 'Before'." Confirmed in user's %TEMP%\gamerguardian_error.log — they triggered Apply in v0.1.18, the window threw during measure, and they never saw the verification details. Added Mode=OneWay to all four Run bindings (Before, Desired, After, Mechanism). Will be fixed for users on v0.1.20+. 2. Stale installer EXEs from the auto-update flow (~75 MB each) were piling up in %TEMP%. UpdateService.DownloadInstallerAsync writes them, launches them, then exits — never cleans up. New TempCleanup.Run() called from OnStartup deletes any GamerGuardian-Setup-*.exe in %TEMP% older than 1 day (1-day grace so an in-progress install isn't yanked). Also one-shot removes any stale gamerguardian_trace.log file from earlier dev builds. 3. gamerguardian_error.log was append-only with no cap. Now rotates at ~1 MB (.log -> .log.1) the same way changes.log does. README gains a "Files GamerGuardian writes" table that documents every path the app touches: install location, config, change log, error log, self-test output, downloaded installers, autostart key. Includes the "auto-cleaned on next launch" note for stale installer downloads so users on v0.1.20+ know they don't need to manually delete them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b02faea commit 2885e5f

4 files changed

Lines changed: 78 additions & 5 deletions

File tree

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ GamerGuardian doesn't ask you to take its word for it.
225225
| Power plan | `powercfg /getactivescheme` |
226226
| HDR / Refresh / Resolution | Settings → System → Display, or `dxdiag` |
227227

228-
**4. The change log.** `%APPDATA%\GamerGuardian\changes.log` — every applied change (manual or silent auto-apply) is appended with a timestamp, source, before/after values, and verification status. Open it from *Settings → General → Open change log*, or from the Apply Results window. Sample line:
228+
**4. The change log.** `%APPDATA%\GamerGuardian\changes.log` — every applied change (manual or silent auto-apply) is appended with a timestamp, source, before/after values, and verification status. Open it from *Settings → General → Open change log*, or from the Apply Results window. Auto-rotates at ~1 MB.
229+
230+
Sample line:
229231

230232
```
231233
[2026-05-06 22:14:08] [manual] OK USB Selective Suspend (global override) | Default -> Disabled (gaming) | now: Disabled (gaming) | reboot pending
@@ -237,6 +239,20 @@ Auto-rotates at ~1 MB (`changes.log.1` keeps the previous batch).
237239

238240
**6. The source.** Every monitor is a single file under [`src/GamerGuardian/Monitors/`](src/GamerGuardian/Monitors/) that does exactly one thing each. Read [`HagsMonitor.cs`](src/GamerGuardian/Monitors/HagsMonitor.cs), say, to see the full code that reads and writes HAGS — about 30 lines.
239241

242+
## Files GamerGuardian writes
243+
244+
| Path | Purpose | Lifetime |
245+
|---|---|---|
246+
| `%LOCALAPPDATA%\Programs\GamerGuardian\GamerGuardian.exe` | Installed app | Until uninstall |
247+
| `%APPDATA%\GamerGuardian\config.json` | Your monitor / want / auto-apply preferences | Persists; survives upgrades |
248+
| `%APPDATA%\GamerGuardian\changes.log` | Append-only audit of every applied change | Auto-rotates at ~1 MB → `changes.log.1` |
249+
| `%TEMP%\gamerguardian_error.log` | Unhandled-exception stack traces (rare) | Auto-rotates at ~1 MB → `.log.1` |
250+
| `%TEMP%\gamerguardian_selftest.txt` | Output of `GamerGuardian.exe --test` | Overwritten each run |
251+
| `%TEMP%\GamerGuardian-Setup-x.y.z.exe` | Installer downloaded by the auto-update flow before it launches | **Auto-cleaned on next launch** if older than 1 day |
252+
| `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\GamerGuardian` | Launch-at-startup entry | Created/removed by the *Launch at Windows startup* checkbox |
253+
254+
If you see leftover `GamerGuardian-Setup-*.exe` files in `%TEMP%` from before v0.1.20, you can delete them — they were the previous auto-update flow's downloaded installers. v0.1.20+ cleans them up automatically.
255+
240256
## Compatibility
241257

242258
- **Windows 11** (any version). Windows 10 support is on the roadmap.

src/GamerGuardian/App.xaml.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ protected override void OnStartup(StartupEventArgs e)
4949
var cfg = _store.Load();
5050
StartupRegistration.Sync(cfg.LaunchAtStartup);
5151
ThemeService.Apply(cfg.Theme);
52+
TempCleanup.Run();
5253

5354
_notifier = new Notifier();
5455
_allMonitors = new IMonitoredSetting[]
@@ -180,6 +181,18 @@ private static void LogException(string source, Exception? ex)
180181
try
181182
{
182183
var path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "gamerguardian_error.log");
184+
// Cap at ~1 MB by rotating to .1
185+
try
186+
{
187+
var fi = new System.IO.FileInfo(path);
188+
if (fi.Exists && fi.Length > 1_000_000)
189+
{
190+
var prev = path + ".1";
191+
if (System.IO.File.Exists(prev)) System.IO.File.Delete(prev);
192+
System.IO.File.Move(path, prev);
193+
}
194+
}
195+
catch { }
183196
System.IO.File.AppendAllText(path,
184197
$"[{DateTime.Now:s}] {source}: {ex?.GetType().FullName}: {ex?.Message}\n{ex?.StackTrace}\n\n");
185198
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.IO;
2+
3+
namespace GamerGuardian.Services;
4+
5+
/// <summary>
6+
/// Best-effort cleanup of stale GamerGuardian artifacts in %TEMP%:
7+
/// - Old installer EXEs left behind by the auto-update flow.
8+
/// - Old trace.log files from earlier dev builds.
9+
/// Files newer than 1 day are kept so an in-progress install isn't disturbed.
10+
/// </summary>
11+
public static class TempCleanup
12+
{
13+
private const int KeepDays = 1;
14+
15+
public static void Run()
16+
{
17+
try
18+
{
19+
var temp = Path.GetTempPath();
20+
var cutoff = DateTime.Now.AddDays(-KeepDays);
21+
22+
foreach (var path in Directory.EnumerateFiles(temp, "GamerGuardian-Setup-*.exe"))
23+
{
24+
TryDeleteIfOlder(path, cutoff);
25+
}
26+
27+
var stale = Path.Combine(temp, "gamerguardian_trace.log");
28+
TryDeleteIfOlder(stale, DateTime.MaxValue); // always remove — code no longer writes it
29+
}
30+
catch { /* best-effort */ }
31+
}
32+
33+
private static void TryDeleteIfOlder(string path, DateTime cutoff)
34+
{
35+
try
36+
{
37+
var fi = new FileInfo(path);
38+
if (!fi.Exists) return;
39+
if (fi.LastWriteTime > cutoff) return;
40+
fi.Delete();
41+
}
42+
catch { }
43+
}
44+
}

src/GamerGuardian/UI/ApplyResultsWindow.xaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,16 @@
6969
</Border>
7070
</Grid>
7171
<TextBlock Margin="30,4,0,0" FontSize="12">
72-
<Run Text="Before: "/><Run Text="{Binding Before}" Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
72+
<Run Text="Before: "/><Run Text="{Binding Before, Mode=OneWay}" Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
7373
<Run Text=""/>
74-
<Run Text="Want: "/><Run Text="{Binding Desired}" Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
74+
<Run Text="Want: "/><Run Text="{Binding Desired, Mode=OneWay}" Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
7575
<Run Text=""/>
76-
<Run Text="Now: "/><Run Text="{Binding After}" Foreground="{Binding IconColor}" FontWeight="SemiBold"/>
76+
<Run Text="Now: "/><Run Text="{Binding After, Mode=OneWay}" Foreground="{Binding IconColor}" FontWeight="SemiBold"/>
7777
</TextBlock>
7878
<TextBlock Margin="30,6,0,0" FontSize="11"
7979
Foreground="{DynamicResource TextFillColorTertiaryBrush}"
8080
TextWrapping="Wrap">
81-
<Run Text="Mechanism: "/><Run Text="{Binding Mechanism}"/>
81+
<Run Text="Mechanism: "/><Run Text="{Binding Mechanism, Mode=OneWay}"/>
8282
</TextBlock>
8383
<Grid Margin="30,4,0,0" Visibility="{Binding HasVerifyCommandVisibility}">
8484
<Grid.ColumnDefinitions>

0 commit comments

Comments
 (0)