Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/proteo5/Prevent-Screen-Saver/llms.txt

Use this file to discover all available pages before exploring further.

Prevent Screen Saver is a single-project .NET 10 Windows Forms application. Its source is organized into focused, single-responsibility classes that are composed inside Form1, which acts as the central coordinator between the tray UI and the underlying services. This page documents the repository layout, the purpose of each class, and the key internal types exposed by the service layer.

Repository Layout

Prevent-Screen-Saver/
├── Prevent-Screen-Saver.slnx           # Solution file
├── installer/
│   └── PreventScreenSaver.iss          # Inno Setup script
├── docs/
│   ├── implementation-plan.md
│   └── hotkey-plan.md
└── src/
    └── PreventScreenSaver/
        ├── PreventScreenSaver.csproj
        ├── Program.cs                      # Entry point + single-instance guard
        ├── Form1.cs / Form1.Designer.cs    # Main window and tray coordinator
        ├── ScreenSaverProtectionService.cs
        ├── GlobalHotkeyService.cs
        ├── HotkeyPresets.cs
        ├── AppSettingsStore.cs
        ├── StartupRegistrationService.cs
        ├── DiagnosticsLog.cs
        ├── TrayIconFactory.cs
        ├── AboutForm.cs
        └── HelpForm.cs
The project file (PreventScreenSaver.csproj) targets net10.0-windows, uses Windows Forms (<UseWindowsForms>true</UseWindowsForms>), and outputs a Windows executable (<OutputType>WinExe</OutputType>).

Key Classes

Program — Entry Point

Program.cs is the application entry point. Before launching the Windows Forms message loop, it acquires a named Mutex to enforce the single-instance constraint. If another instance of the app is already running, Main() returns immediately without showing any window or error.
private const string MutexName = "PreventScreenSaver.SingleInstance";

[STAThread]
static void Main()
{
    using Mutex singleInstanceMutex = new(initiallyOwned: true, MutexName, out bool createdNew);

    if (!createdNew)
    {
        return;
    }

    ApplicationConfiguration.Initialize();
    Application.Run(new Form1());
}

Form1 — Main Window and Tray Coordinator

Form1.cs is the central hub of the application. It owns the tray icon, the context menu, and the main status window. On construction, it loads persisted settings, initializes the hotkey submenu, restores the last protection state, and synchronizes all UI elements. Key responsibilities include:
  • Routing user actions — tray left-click, tray double-click, menu items (Activate, Deactivate, Open, Start with Windows, Hotkey, Help, About, Exit), and the main window button all converge on shared ActivateProtection(), DeactivateProtection(), and ToggleProtection() methods.
  • Handling WM_HOTKEY messagesWndProc intercepts the Windows hotkey message and routes it to the same toggle path used by the tray controls.
  • Keeping state in syncUpdateUiState() updates the tray icon, tray tooltip, and window title in a single call whenever protection state changes.
  • Cleanup on close — when the user selects Exit, protection is stopped, the hotkey is unregistered, settings are persisted, and the tray icon is hidden before the form closes.
Closing the main window does not exit the application. The OnFormClosing override cancels the close event and hides the window to the tray unless exitRequested is true, which is only set by the Exit menu item.

ScreenSaverProtectionService — Windows API Wrapper

This service wraps the SetThreadExecutionState P/Invoke call and exposes a clean interface to Form1.
public bool StartProtection()
{
    uint result = SetThreadExecutionState(EsContinuous | EsDisplayRequired);
    if (result == 0) return false;
    protectionActive = true;
    return true;
}

public void StopProtection()
{
    if (!protectionActive) return;
    SetThreadExecutionState(EsContinuous);
    protectionActive = false;
}

public bool IsProtectionActive => protectionActive;
StateFlags passed to SetThreadExecutionState
Protection activeES_CONTINUOUS | ES_DISPLAY_REQUIRED
Protection inactiveES_CONTINUOUS only
StartProtection() returns false if the API call returns zero, allowing Form1 to surface a visible error message rather than silently failing.

GlobalHotkeyService — Hotkey Lifecycle Manager

GlobalHotkeyService wraps RegisterHotKey and UnregisterHotKey from user32.dll and implements IDisposable to guarantee cleanup.
public bool TryRegister(nint windowHandle, int id, HotkeyModifiers modifiers, Keys key, out string errorMessage)

public void Unregister()

public bool IsRegistered => isRegistered;
If TryRegister fails, it returns the Win32 error code in errorMessage so Form1 can log the failure and show a tray notification. The service calls Unregister() automatically before any new registration attempt, preventing stale registrations from persisting across preset changes.

HotkeyModifiers Enum

[Flags]
internal enum HotkeyModifiers : uint
{
    None     = 0,
    Alt      = 0x0001,
    Control  = 0x0002,
    Shift    = 0x0004,
    NoRepeat = 0x4000
}
The NoRepeat flag is always OR’d with the selected preset’s modifiers when registering the hotkey. This prevents the toggle action from firing repeatedly while the user holds the key combination down, which would cause the protection state to flip back and forth.

HotkeyPresets — Built-in Shortcut Registry

HotkeyPresets is a static class that defines five built-in key combinations. The full list is:
Preset IDDisplay NameModifiersKey
ctrl-alt-pCtrl+Alt+PControl + AltP
ctrl-shift-f12Ctrl+Shift+F12Control + ShiftF12
ctrl-shift-f11Ctrl+Shift+F11Control + ShiftF11
ctrl-shift-f10Ctrl+Shift+F10Control + ShiftF10
ctrl-shift-f9Ctrl+Shift+F9Control + ShiftF9
The class exposes All (all five presets as IReadOnlyList<HotkeyPreset>), Default (Ctrl+Alt+P), and Resolve(id), which falls back to the default for any unknown or empty ID — ensuring invalid stored settings never cause a crash.

AppSettingsStore — Settings Persistence

Settings are serialized as indented JSON to %LocalAppData%\PreventScreenSaver\settings.json using System.Text.Json. The stored fields are:
FieldTypeDefault
StartMinimizedbooltrue
LastProtectionActiveboolfalse
HotkeyEnabledbooltrue
HotkeyPresetIdstring"ctrl-alt-p"
AppSettingsStore.Load() returns a fresh instance with defaults if the file does not yet exist. Save() creates the directory if needed before writing, so the file path is always safe to write on first run.

StartupRegistrationService — Windows Startup Integration

This service reads and writes the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run registry key to control whether the application launches at Windows login. No administrator rights are required — the key is in the current-user hive. IsEnabled() checks that the stored value exactly matches the current executable path (case-insensitive). SetEnabled(true) writes the path; SetEnabled(false) removes the value without throwing if it is already absent.

DiagnosticsLog — Plain-Text Log

DiagnosticsLog appends timestamped entries to %LocalAppData%\PreventScreenSaver\log.txt. Each line is formatted as:
yyyy-MM-dd HH:mm:ss zzz <message>
The class creates the directory on each write call, so no setup is needed at startup. Log entries are written for all significant lifecycle events: protection activation and deactivation, hotkey registration success and failure, hotkey presses, startup state changes, and application exit.

TrayIconFactory — Programmatic Icon Generation

TrayIconFactory creates both tray icon states at runtime using System.Drawing — no embedded icon files are required.

Idle Icon

A grey circle (RGB 97, 110, 128) with two vertical white pause bars. Shown when protection is inactive.

Active Icon

A green circle (RGB 32, 158, 97) with a white checkmark. Shown when SetThreadExecutionState is holding the active state.
Both icons are 16×16 pixels with anti-aliased edges and are created via GetHicon(), cloned from a temporary Icon handle, and freed with DestroyIcon to avoid handle leaks.

AboutForm / HelpForm — In-App Dialogs

These are lightweight Windows Forms dialogs opened from the tray context menu. They are positioned relative to the main window (or the cursor position if the window is not visible) using a ShowDialogCenteredOnMainWindow helper in Form1. Both are instantiated fresh on each open and disposed when closed.

Service Dependency Map

All services are instantiated directly by Form1 as private readonly fields. There is no IoC container — the composition is explicit and visible at the top of Form1.cs:
private readonly AppSettingsStore appSettings = AppSettingsStore.Load();
private readonly DiagnosticsLog diagnosticsLog = new();
private readonly ScreenSaverProtectionService protectionService = new();
private readonly StartupRegistrationService startupRegistrationService = new();
private readonly GlobalHotkeyService hotkeyService = new();
Form1 is the only class that holds references to multiple services. Each service class has no knowledge of the others — all coordination flows through Form1.

Build docs developers (and LLMs) love